1 // "$Id: Fl_Text_Editor.cxx 5671 2007-02-08 07:58:47Z matt $"
2 //
3 // Copyright 2001-2006 by Bill Spitzak and others.
4 // Original code Copyright Mark Edel.  Permission to distribute under
5 // the LGPL for the FLTK library granted by Mark Edel.
6 // ----------------------------------------------------------------------------
7 // Copyright (C) 2014
8 //              David Freese, W1HKJ
9 //
10 // This file is part of flmsg
11 //
12 // flrig is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // flrig is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 // ----------------------------------------------------------------------------
25 
26 #include <config.h>
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <FL/Fl.H>
33 #include <FL/Fl_Window.H>
34 #include "Fl_Text_Editor_mod.H"
35 #include <FL/fl_ask.H>
36 #include "missing_strings.h"
37 
38 
Fl_Text_Editor_mod(int X,int Y,int W,int H,const char * l)39 Fl_Text_Editor_mod::Fl_Text_Editor_mod(int X, int Y, int W, int H,  const char* l)
40     : Fl_Text_Display_mod(X, Y, W, H, l) {
41   mCursorOn = 1;
42   insert_mode_ = 1;
43   key_bindings = 0;
44 
45   // handle the default key bindings
46   add_default_key_bindings(&key_bindings);
47 
48   // handle everything else
49   default_key_function(kf_default);
50 }
51 
52 Fl_Text_Editor_mod::Key_Binding* Fl_Text_Editor_mod::global_key_bindings = 0;
53 
54 // These are the default key bindings every widget should start with
55 static struct {
56   int key;
57   int state;
58   Fl_Text_Editor_mod::Key_Func func;
59 } default_key_bindings[] = {
60   { FL_Escape,    FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_ignore     },
61   { FL_Enter,     FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_enter      },
62   { FL_KP_Enter,  FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_enter      },
63   { FL_BackSpace, FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_backspace  },
64   { FL_Insert,    FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_insert     },
65   { FL_Delete,    FL_TEXT_EDITOR_ANY_STATE, Fl_Text_Editor_mod::kf_delete     },
66   { FL_Home,      0,                        Fl_Text_Editor_mod::kf_move       },
67   { FL_End,       0,                        Fl_Text_Editor_mod::kf_move       },
68   { FL_Left,      0,                        Fl_Text_Editor_mod::kf_move       },
69   { FL_Up,        0,                        Fl_Text_Editor_mod::kf_move       },
70   { FL_Right,     0,                        Fl_Text_Editor_mod::kf_move       },
71   { FL_Down,      0,                        Fl_Text_Editor_mod::kf_move       },
72   { FL_Page_Up,   0,                        Fl_Text_Editor_mod::kf_move       },
73   { FL_Page_Down, 0,                        Fl_Text_Editor_mod::kf_move       },
74   { FL_Home,      FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
75   { FL_End,       FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
76   { FL_Left,      FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
77   { FL_Up,        FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
78   { FL_Right,     FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
79   { FL_Down,      FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
80   { FL_Page_Up,   FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
81   { FL_Page_Down, FL_SHIFT,                 Fl_Text_Editor_mod::kf_shift_move },
82   { FL_Home,      FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
83   { FL_End,       FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
84   { FL_Left,      FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
85   { FL_Up,        FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
86   { FL_Right,     FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
87   { FL_Down,      FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
88   { FL_Page_Up,   FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
89   { FL_Page_Down, FL_CTRL,                  Fl_Text_Editor_mod::kf_ctrl_move  },
90   { FL_Home,      FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
91   { FL_End,       FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
92   { FL_Left,      FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
93   { FL_Up,        FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
94   { FL_Right,     FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
95   { FL_Down,      FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
96   { FL_Page_Up,   FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
97   { FL_Page_Down, FL_CTRL|FL_SHIFT,         Fl_Text_Editor_mod::kf_c_s_move   },
98 //{ FL_Clear,	  0,                        Fl_Text_Editor_mod::delete_to_eol },
99   { 'z',          FL_CTRL,                  Fl_Text_Editor_mod::kf_undo	  },
100   { '/',          FL_CTRL,                  Fl_Text_Editor_mod::kf_undo	  },
101   { 'x',          FL_CTRL,                  Fl_Text_Editor_mod::kf_cut        },
102   { FL_Delete,    FL_SHIFT,                 Fl_Text_Editor_mod::kf_cut        },
103   { 'c',          FL_CTRL,                  Fl_Text_Editor_mod::kf_copy       },
104   { FL_Insert,    FL_CTRL,                  Fl_Text_Editor_mod::kf_copy       },
105   { 'v',          FL_CTRL,                  Fl_Text_Editor_mod::kf_paste      },
106   { FL_Insert,    FL_SHIFT,                 Fl_Text_Editor_mod::kf_paste      },
107   { 'a',          FL_CTRL,                  Fl_Text_Editor_mod::kf_select_all },
108 
109 #ifdef __APPLE__
110   // Define CMD+key accelerators...
111   { 'z',          FL_COMMAND,               Fl_Text_Editor_mod::kf_undo       },
112   { 'x',          FL_COMMAND,               Fl_Text_Editor_mod::kf_cut        },
113   { 'c',          FL_COMMAND,               Fl_Text_Editor_mod::kf_copy       },
114   { 'v',          FL_COMMAND,               Fl_Text_Editor_mod::kf_paste      },
115   { 'a',          FL_COMMAND,               Fl_Text_Editor_mod::kf_select_all },
116 #endif // __APPLE__
117 
118   { 0,            0,                        0                             }
119 };
120 
add_default_key_bindings(Key_Binding ** list)121 void Fl_Text_Editor_mod::add_default_key_bindings(Key_Binding** list) {
122   for (int i = 0; default_key_bindings[i].key; i++) {
123     add_key_binding(default_key_bindings[i].key,
124                     default_key_bindings[i].state,
125                     default_key_bindings[i].func,
126                     list);
127   }
128 }
129 
130 Fl_Text_Editor_mod::Key_Func
bound_key_function(int key,int state,Key_Binding * list)131 Fl_Text_Editor_mod::bound_key_function(int key, int state, Key_Binding* list) {
132   Key_Binding* cur;
133   if (!list) return 0;
134   for (cur = list; cur; cur = cur->next)
135     if (cur->key == key)
136       if (cur->state == FL_TEXT_EDITOR_ANY_STATE || cur->state == state)
137         break;
138   if (!cur) return 0;
139   return cur->function;
140 }
141 
142 void
remove_all_key_bindings(Key_Binding ** list)143 Fl_Text_Editor_mod::remove_all_key_bindings(Key_Binding** list) {
144   Key_Binding *cur, *next;
145   for (cur = *list; cur; cur = next) {
146     next = cur->next;
147     delete cur;
148   }
149   *list = 0;
150 }
151 
152 void
remove_key_binding(int key,int state,Key_Binding ** list)153 Fl_Text_Editor_mod::remove_key_binding(int key, int state, Key_Binding** list) {
154   Key_Binding *cur, *last = 0;
155   for (cur = *list; cur; last = cur, cur = cur->next)
156     if (cur->key == key && cur->state == state) break;
157   if (!cur) return;
158   if (last) last->next = cur->next;
159   else *list = cur->next;
160   delete cur;
161 }
162 
163 void
add_key_binding(int key,int state,Key_Func function,Key_Binding ** list)164 Fl_Text_Editor_mod::add_key_binding(int key, int state, Key_Func function,
165                                 Key_Binding** list) {
166   Key_Binding* kb = new Key_Binding;
167   kb->key = key;
168   kb->state = state;
169   kb->function = function;
170   kb->next = *list;
171   *list = kb;
172 }
173 
174 ////////////////////////////////////////////////////////////////
175 
176 #define NORMAL_INPUT_MOVE 0
177 
kill_selection(Fl_Text_Editor_mod * e)178 static void kill_selection(Fl_Text_Editor_mod* e) {
179   if (e->buffer()->selected()) {
180     e->insert_position(e->buffer()->primary_selection()->start());
181     e->buffer()->remove_selection();
182   }
183 }
184 
kf_default(int c,Fl_Text_Editor_mod * e)185 int Fl_Text_Editor_mod::kf_default(int c, Fl_Text_Editor_mod* e) {
186   if (!c || (!isprint(c) && c != '\t')) return 0;
187   char s[2] = "\0";
188   s[0] = (char)c;
189   kill_selection(e);
190   if (e->insert_mode()) e->insert(s);
191   else e->overstrike(s);
192   e->show_insert_position();
193   e->set_changed();
194   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
195   return 1;
196 }
197 
kf_ignore(int,Fl_Text_Editor_mod *)198 int Fl_Text_Editor_mod::kf_ignore(int, Fl_Text_Editor_mod*) {
199   return 0; // don't handle
200 }
201 
kf_backspace(int,Fl_Text_Editor_mod * e)202 int Fl_Text_Editor_mod::kf_backspace(int, Fl_Text_Editor_mod* e) {
203   if (!e->buffer()->selected() && e->move_left())
204     e->buffer()->select(e->insert_position(), e->insert_position()+1);
205   kill_selection(e);
206   e->show_insert_position();
207   e->set_changed();
208   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
209   return 1;
210 }
211 
kf_enter(int,Fl_Text_Editor_mod * e)212 int Fl_Text_Editor_mod::kf_enter(int, Fl_Text_Editor_mod* e) {
213   kill_selection(e);
214   e->insert("\n");
215   e->show_insert_position();
216   e->set_changed();
217   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
218   return 1;
219 }
220 
221 extern void fl_text_drag_me(int pos, Fl_Text_Display_mod* d);
222 
kf_move(int c,Fl_Text_Editor_mod * e)223 int Fl_Text_Editor_mod::kf_move(int c, Fl_Text_Editor_mod* e) {
224   int i;
225   int selected = e->buffer()->selected();
226   if (!selected)
227     e->dragPos = e->insert_position();
228   e->buffer()->unselect();
229   switch (c) {
230   case FL_Home:
231       e->insert_position(e->buffer()->line_start(e->insert_position()));
232       break;
233     case FL_End:
234       e->insert_position(e->buffer()->line_end(e->insert_position()));
235       break;
236     case FL_Left:
237       e->move_left();
238       break;
239     case FL_Right:
240       e->move_right();
241       break;
242     case FL_Up:
243       e->move_up();
244       break;
245     case FL_Down:
246       e->move_down();
247       break;
248     case FL_Page_Up:
249       for (i = 0; i < e->mNVisibleLines - 1; i++) e->move_up();
250       break;
251     case FL_Page_Down:
252       for (i = 0; i < e->mNVisibleLines - 1; i++) e->move_down();
253       break;
254   }
255   e->show_insert_position();
256   return 1;
257 }
258 
kf_shift_move(int c,Fl_Text_Editor_mod * e)259 int Fl_Text_Editor_mod::kf_shift_move(int c, Fl_Text_Editor_mod* e) {
260   kf_move(c, e);
261   fl_text_drag_me(e->insert_position(), e);
262   return 1;
263 }
264 
kf_ctrl_move(int c,Fl_Text_Editor_mod * e)265 int Fl_Text_Editor_mod::kf_ctrl_move(int c, Fl_Text_Editor_mod* e) {
266   if (!e->buffer()->selected())
267     e->dragPos = e->insert_position();
268   if (c != FL_Up && c != FL_Down) {
269     e->buffer()->unselect();
270     e->show_insert_position();
271   }
272   switch (c) {
273     case FL_Home:
274       e->insert_position(0);
275       e->scroll(0, 0);
276       break;
277     case FL_End:
278       e->insert_position(e->buffer()->length());
279       e->scroll(e->count_lines(0, e->buffer()->length(), 1), 0);
280       break;
281     case FL_Left:
282       e->previous_word();
283       break;
284     case FL_Right:
285       e->next_word();
286       break;
287     case FL_Up:
288       e->scroll(e->mTopLineNum-1, e->mHorizOffset);
289       break;
290     case FL_Down:
291       e->scroll(e->mTopLineNum+1, e->mHorizOffset);
292       break;
293     case FL_Page_Up:
294       e->insert_position(e->mLineStarts[0]);
295       break;
296     case FL_Page_Down:
297       e->insert_position(e->mLineStarts[e->mNVisibleLines-2]);
298       break;
299   }
300   return 1;
301 }
302 
kf_c_s_move(int c,Fl_Text_Editor_mod * e)303 int Fl_Text_Editor_mod::kf_c_s_move(int c, Fl_Text_Editor_mod* e) {
304   kf_ctrl_move(c, e);
305   fl_text_drag_me(e->insert_position(), e);
306   return 1;
307 }
308 
kf_home(int,Fl_Text_Editor_mod * e)309 int Fl_Text_Editor_mod::kf_home(int, Fl_Text_Editor_mod* e) {
310     return kf_move(FL_Home, e);
311 }
312 
kf_end(int,Fl_Text_Editor_mod * e)313 int Fl_Text_Editor_mod::kf_end(int, Fl_Text_Editor_mod* e) {
314   return kf_move(FL_End, e);
315 }
316 
kf_left(int,Fl_Text_Editor_mod * e)317 int Fl_Text_Editor_mod::kf_left(int, Fl_Text_Editor_mod* e) {
318   return kf_move(FL_Left, e);
319 }
320 
kf_up(int,Fl_Text_Editor_mod * e)321 int Fl_Text_Editor_mod::kf_up(int, Fl_Text_Editor_mod* e) {
322   return kf_move(FL_Up, e);
323 }
324 
kf_right(int,Fl_Text_Editor_mod * e)325 int Fl_Text_Editor_mod::kf_right(int, Fl_Text_Editor_mod* e) {
326   return kf_move(FL_Right, e);
327 }
328 
kf_down(int,Fl_Text_Editor_mod * e)329 int Fl_Text_Editor_mod::kf_down(int, Fl_Text_Editor_mod* e) {
330   return kf_move(FL_Down, e);
331 }
332 
kf_page_up(int,Fl_Text_Editor_mod * e)333 int Fl_Text_Editor_mod::kf_page_up(int, Fl_Text_Editor_mod* e) {
334   return kf_move(FL_Page_Up, e);
335 }
336 
kf_page_down(int,Fl_Text_Editor_mod * e)337 int Fl_Text_Editor_mod::kf_page_down(int, Fl_Text_Editor_mod* e) {
338   return kf_move(FL_Page_Down, e);
339 }
340 
341 
kf_insert(int,Fl_Text_Editor_mod * e)342 int Fl_Text_Editor_mod::kf_insert(int, Fl_Text_Editor_mod* e) {
343   e->insert_mode(e->insert_mode() ? 0 : 1);
344   return 1;
345 }
346 
kf_delete(int,Fl_Text_Editor_mod * e)347 int Fl_Text_Editor_mod::kf_delete(int, Fl_Text_Editor_mod* e) {
348   if (!e->buffer()->selected())
349     e->buffer()->select(e->insert_position(), e->insert_position()+1);
350   kill_selection(e);
351   e->show_insert_position();
352   e->set_changed();
353   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
354   return 1;
355 }
356 
kf_copy(int,Fl_Text_Editor_mod * e)357 int Fl_Text_Editor_mod::kf_copy(int, Fl_Text_Editor_mod* e) {
358   if (!e->buffer()->selected()) return 1;
359   const char *copy = e->buffer()->selection_text();
360   if (*copy) Fl::copy(copy, strlen(copy), 1);
361   free((void*)copy);
362   e->show_insert_position();
363   return 1;
364 }
365 
kf_cut(int c,Fl_Text_Editor_mod * e)366 int Fl_Text_Editor_mod::kf_cut(int c, Fl_Text_Editor_mod* e) {
367   kf_copy(c, e);
368   kill_selection(e);
369   e->set_changed();
370   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
371   return 1;
372 }
373 
kf_paste(int,Fl_Text_Editor_mod * e)374 int Fl_Text_Editor_mod::kf_paste(int, Fl_Text_Editor_mod* e) {
375   kill_selection(e);
376   Fl::paste(*e, 1);
377   e->show_insert_position();
378   e->set_changed();
379   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
380   return 1;
381 }
382 
kf_select_all(int,Fl_Text_Editor_mod * e)383 int Fl_Text_Editor_mod::kf_select_all(int, Fl_Text_Editor_mod* e) {
384   e->buffer()->select(0, e->buffer()->length());
385   return 1;
386 }
387 
kf_undo(int,Fl_Text_Editor_mod * e)388 int Fl_Text_Editor_mod::kf_undo(int , Fl_Text_Editor_mod* e) {
389   e->buffer()->unselect();
390   int crsr;
391   int ret = e->buffer()->undo(&crsr);
392   e->insert_position(crsr);
393   e->show_insert_position();
394   e->set_changed();
395   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
396   return ret;
397 }
398 
handle_key()399 int Fl_Text_Editor_mod::handle_key() {
400   // Call FLTK's rules to try to turn this into a printing character.
401   // This uses the right-hand ctrl key as a "compose prefix" and returns
402   // the changes that should be made to the text, as a number of
403   // bytes to delete and a string to insert:
404   int del;
405   if (Fl::compose(del)) {
406     if (del) buffer()->select(insert_position()-del, insert_position());
407     kill_selection(this);
408     if (Fl::event_length()) {
409       if (insert_mode()) insert(Fl::event_text());
410       else overstrike(Fl::event_text());
411     }
412     show_insert_position();
413     set_changed();
414     if (when()&FL_WHEN_CHANGED) do_callback();
415     return 1;
416   }
417 
418   int key = Fl::event_key(), state = Fl::event_state(), c = Fl::event_text()[0];
419   state &= FL_SHIFT|FL_CTRL|FL_ALT|FL_META; // only care about these states
420   Key_Func f;
421   f = bound_key_function(key, state, global_key_bindings);
422   if (!f) f = bound_key_function(key, state, key_bindings);
423   if (f) return f(key, this);
424   if (default_key_function_ && !state) return default_key_function_(c, this);
425   return 0;
426 }
427 
maybe_do_callback()428 void Fl_Text_Editor_mod::maybe_do_callback() {
429 //  printf("Fl_Text_Editor_mod::maybe_do_callback()\n");
430 //  printf("changed()=%d, when()=%x\n", changed(), when());
431   if (changed() || (when()&FL_WHEN_NOT_CHANGED)) do_callback();
432 }
433 
handle(int event)434 int Fl_Text_Editor_mod::handle(int event) {
435   if (!buffer()) return 0;
436 
437   switch (event) {
438     case FL_FOCUS:
439       show_cursor(mCursorOn); // redraws the cursor
440       if (buffer()->selected()) redraw(); // Redraw selections...
441       Fl::focus(this);
442       return 1;
443 
444     case FL_UNFOCUS:
445       show_cursor(mCursorOn); // redraws the cursor
446       if (buffer()->selected()) redraw(); // Redraw selections...
447     case FL_HIDE:
448       if (when() & FL_WHEN_RELEASE) maybe_do_callback();
449       return 1;
450 
451     case FL_KEYBOARD:
452       if (active_r() && window() && this == Fl::belowmouse())
453         window()->cursor(FL_CURSOR_NONE);
454       return handle_key();
455 
456     case FL_PASTE:
457       if (!Fl::event_text()) {
458         fl_beep();
459 	return 1;
460       }
461       buffer()->remove_selection();
462       if (insert_mode()) insert(Fl::event_text());
463       else overstrike(Fl::event_text());
464       show_insert_position();
465       set_changed();
466       if (when()&FL_WHEN_CHANGED) do_callback();
467       return 1;
468 
469     case FL_ENTER:
470 // MRS: WIN32 only?  Need to test!
471 //    case FL_MOVE:
472       show_cursor(mCursorOn);
473       return 1;
474 
475     case FL_PUSH:
476       if (Fl::event_button() == 2) {
477         // don't let the text_display see this event
478         if (Fl_Group::handle(event)) return 1;
479         dragType = -1;
480         Fl::paste(*this, 0);
481         Fl::focus(this);
482         set_changed();
483         if (when()&FL_WHEN_CHANGED) do_callback();
484         return 1;
485       }
486       break;
487   }
488 
489   return Fl_Text_Display_mod::handle(event);
490 }
491 
492 //
493 // End of "$Id: Fl_Text_Editor.cxx 5671 2007-02-08 07:58:47Z matt $".
494 //
495