1 
2 /******************************************************************************
3 * MODULE     : edit_keyboard.cpp
4 * DESCRIPTION: Keyboard handling
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "edit_interface.hpp"
13 #include "analyze.hpp"
14 #include "tm_buffer.hpp"
15 #include "archiver.hpp"
16 
17 /******************************************************************************
18 * Basic subroutines for keyboard handling
19 ******************************************************************************/
20 
21 int
get_input_mode()22 edit_interface_rep::get_input_mode () {
23   return input_mode;
24 }
25 
26 void
set_input_mode(int mode)27 edit_interface_rep::set_input_mode (int mode) {
28   interrupt_shortcut ();
29   // avoids keyboard shortcuts when using the menu between two keystrokes
30 
31   if ((mode == INPUT_NORMAL) && (input_mode != INPUT_NORMAL)) {
32     selection_cancel ();
33     completions= array<string> ();
34   }
35   input_mode= mode;
36 }
37 
38 void
set_input_normal()39 edit_interface_rep::set_input_normal () {
40   set_input_mode (INPUT_NORMAL);
41 }
42 
43 bool
in_normal_mode()44 edit_interface_rep::in_normal_mode () {
45   return input_mode == INPUT_NORMAL;
46 }
47 
48 bool
in_search_mode()49 edit_interface_rep::in_search_mode () {
50   return input_mode == INPUT_SEARCH;
51 }
52 
53 bool
in_replace_mode()54 edit_interface_rep::in_replace_mode () {
55   return input_mode == INPUT_REPLACE;
56 }
57 
58 bool
in_spell_mode()59 edit_interface_rep::in_spell_mode () {
60   return input_mode == INPUT_SPELL;
61 }
62 
63 bool
kbd_get_command(string which,string & help,command & c)64 edit_interface_rep::kbd_get_command (string which, string& help, command& c) {
65   return sv->kbd_get_command (which, help, c);
66 }
67 
68 /******************************************************************************
69 * Main keyboard routines
70 ******************************************************************************/
71 
72 void
interrupt_shortcut()73 edit_interface_rep::interrupt_shortcut () {
74   if (sh_mark != 0) mark_end (sh_mark);
75   sh_s= "";
76   sh_mark= 0;
77 }
78 
79 bool
try_shortcut(string comb)80 edit_interface_rep::try_shortcut (string comb) {
81   int     status;
82   command cmd;
83   string  shorth;
84   string  help;
85 
86   sv->get_keycomb (comb, status, cmd, shorth, help);
87   //cout << "Try " << comb << " -> " << shorth << ", " << help
88   //<< "; " << sh_mark << ", " << status << "\n";
89   if (status != 0) {
90     if (status >= 3) {
91       interrupt_shortcut ();
92       status -= 3;
93       if (status == 0) return false;
94     }
95     else {
96       if (sh_mark != 0 && !mark_cancel (sh_mark)) {
97         sh_mark= 0;
98         return false;
99       }
100     }
101     sh_s= comb;
102     sh_mark= new_marker ();
103     mark_start (sh_mark);
104     archive_state ();
105     string rew_s= sv->kbd_post_rewrite (sh_s);
106     tree rew= sv->kbd_system_rewrite (rew_s);
107     if (N(help)>0) set_message (help, rew);
108     tree rhs= (shorth == rew_s? tree (""): sv->kbd_system_rewrite (shorth));
109     //cout << "Shortcut: " << sh_s << " -> " << rew << "\n";
110     if ((search_forwards (" ", comb) >= 0 && comb != " ") ||
111 	(search_forwards ("-", comb) >= 0 && comb != "-")) {
112       tree t= rhs;
113       if (is_compound (t, "render-key", 1)) t= t[0];
114       if (is_func (t, WITH)) t= t[N(t)-1];
115       string r= as_string (t);
116       if (starts (r, "<") && !starts (r, "<#"))
117         if (cork_to_utf8 (r) != r)
118           rhs= tree (CONCAT, rhs, " (" * r(1, N(r)-1) * ")");
119       call ("set-temporary-message",
120 	    tree (CONCAT, "keyboard shortcut: ", rew), rhs,
121 	    shorth == ""? 1: 3000);
122     }
123     if ((status & 1) == 1) cmd ();
124     else if (N(shorth) > 0) call ("kbd-insert", shorth);
125     //cout << "Mark= " << sh_mark << "\n";
126     return true;
127   }
128 
129   return false;
130 }
131 
132 void
key_press(string gkey)133 edit_interface_rep::key_press (string gkey) {
134   string zero= "a"; zero[0]= '\0';
135   string key= replace (gkey, "<#0>", zero);
136   if (pre_edit_mark != 0) {
137     ASSERT (sh_mark == 0, "invalid shortcut during pre-edit");
138     mark_cancel (pre_edit_mark);
139     pre_edit_s= "";
140     pre_edit_mark= 0;
141   }
142   if (starts (key, "pre-edit:") ) {
143     interrupt_shortcut ();
144     string s= key (9, N(key));
145     if (s == "") return;
146     int i, n= N(s), pos= N(s);
147     for (i=0; i<n; i++)
148       if (s[i] == ':' && is_int (s (0, i))) {
149         int k= as_int (s (0, i));
150         s= s (i+1, n);
151         pos= 0;
152         for (int j=0; j<k && pos<N(s); j++)
153           tm_char_forwards (s, pos);
154         break;
155       }
156     pre_edit_s= s;
157     pre_edit_mark= new_marker ();
158     mark_start (pre_edit_mark);
159     archive_state ();
160     insert_tree (compound ("pre-edit", s), path (0, pos));
161     return;
162   }
163 
164   string new_sh= N(sh_s)==0? key: sh_s * " " * key;
165   if (try_shortcut (new_sh)) return;
166   if (new_sh != key) {
167     interrupt_shortcut ();
168     if (try_shortcut (key)) return;
169   }
170 
171   string rew= sv->kbd_post_rewrite (key);
172   if (N(rew) == 1) {
173     int i ((unsigned char) rew[0]);
174     if ((i >= 32 && i <= 127) || (i >= 128 && i <= 255) || (i == 25))
175       if (!inside_active_graphics ()) {
176         archive_state ();
177         call ("kbd-insert", rew);
178       }
179     interrupt_shortcut ();
180   }
181   else if (contains_unicode_char (rew)) {
182     archive_state ();
183     call ("kbd-insert", key);
184     interrupt_shortcut ();
185   }
186   else if (DEBUG_KEYBOARD)
187     debug_keyboard
188       << "unrecognized key " << key << ". "
189       << "Undefined shortcut or key missing in the encoding files.\n";
190 }
191 
192 void
emulate_keyboard(string keys,string action)193 edit_interface_rep::emulate_keyboard (string keys, string action) {
194   string s= keys;
195   while (s != "") {
196     int i;
197     for (i=1; i<N(s); i++)
198       if (s[i]==' ') break;
199     call ("keyboard-press", object (s (0, i)), object ((double) 0));
200     if (i<N(s)) i++;
201     s= s (i, N(s));
202   }
203   if (N (action) != 0)
204     set_message (concat ("You can also obtain ", action, " by typing ", keys),
205 		 action);
206 }
207 
208 /******************************************************************************
209 * Retrieving keyboard shortcuts
210 ******************************************************************************/
211 
212 tree
kbd(string s)213 edit_interface_rep::kbd (string s) {
214   return sv->kbd_system_rewrite (s);
215 }
216 
217 tree
kbd_shortcut(string cmd)218 edit_interface_rep::kbd_shortcut (string cmd) {
219   string s= as_string (eval ("(kbd-find-inv-binding '" * cmd * ")"));
220   return kbd (s);
221 }
222 
223 /******************************************************************************
224 * Event handlers
225 ******************************************************************************/
226 
227 void
handle_keypress(string key,time_t t)228 edit_interface_rep::handle_keypress (string key, time_t t) {
229   bool started= false;
230 #ifdef USE_EXCEPTIONS
231   try {
232 #endif
233     if (DEBUG_KEYBOARD) {
234       //for (int i=0; i<N(key); i++)
235       //  cout << ((int) (unsigned char) key[i]) << " ";
236       //cout << "\n";
237       debug_keyboard << "Pressed " << key << " at " << t << "\n";
238     }
239     //time_t t1= texmacs_time ();
240     if (is_nil (eb)) apply_changes ();
241     start_editing ();
242     started= true;
243     string zero= "a"; zero[0]= '\0';
244     string gkey= replace (key, zero, "<#0>");
245     call ("keyboard-press", object (gkey), object ((double) t));
246     update_focus_loci ();
247     if (!is_nil (focus_ids))
248       call ("link-follow-ids", object (focus_ids), object ("focus"));
249     notify_change (THE_DECORATIONS);
250     end_editing ();
251     //time_t t2= texmacs_time ();
252     //if (t2 - t1 >= 10) cout << "handle_keypress took " << t2-t1 << "ms\n";
253 #ifdef USE_EXCEPTIONS
254   }
255   catch (string msg) {
256     if (started) {
257       cancel_editing ();
258       interrupt_shortcut ();
259     }
260   }
261   handle_exceptions ();
262 #endif
263 }
264 
265 void drag_left_reset ();
266 void drag_right_reset ();
267 
268 void
handle_keyboard_focus(bool has_focus,time_t t)269 edit_interface_rep::handle_keyboard_focus (bool has_focus, time_t t) {
270   if (DEBUG_KEYBOARD) {
271     if (has_focus) debug_keyboard << "Got focus at " << t << "\n";
272     else debug_keyboard << "Lost focus at " << t << "\n";
273   }
274   if (got_focus != has_focus) {
275     drag_left_reset ();
276     drag_right_reset ();
277   }
278   got_focus= has_focus; (void) t;
279   notify_change (THE_FOCUS);
280   if (got_focus) {
281     focus_on_this_editor ();
282     notify_change (THE_DECORATIONS);
283   }
284   call ("keyboard-focus", object (has_focus), object ((double) t));
285 }
286