1 //
2 // "$Id$"
3 //
4 // Input widget for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2020 by Bill Spitzak and others.
7 //
8 // This library is free software. Distribution and use rights are outlined in
9 // the file "COPYING" which should have been included with this file.  If this
10 // file is missing or damaged, see the license at:
11 //
12 //     https://www.fltk.org/COPYING.php
13 //
14 // Please report all bugs and problems on the following page:
15 //
16 //     https://www.fltk.org/bugs.php
17 //
18 
19 // This is the "user interface", it decodes user actions into what to
20 // do to the text.  See also Fl_Input_.cxx, where the text is actually
21 // manipulated (and some ui, in particular the mouse, is done...).
22 // In theory you can replace this code with another subclass to change
23 // the keybindings.
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <FL/Fl.H>
28 #include <FL/Fl_Window.H>
29 #include <FL/Fl_Input.H>
30 #include <FL/fl_draw.H>
31 #include <FL/fl_ask.H>
32 #include "flstring.h"		// this #includes "<config.h>" !
33 
34 #include <FL/Fl_Float_Input.H>
35 #include <FL/Fl_Int_Input.H>
36 #include <FL/Fl_Multiline_Input.H>
37 #include <FL/Fl_Output.H>
38 #include <FL/Fl_Multiline_Output.H>
39 #include <FL/Fl_Secret_Input.H>
40 
41 #ifdef HAVE_LOCALE_H
42 # include <locale.h>
43 #endif
44 
45 
draw()46 void Fl_Input::draw() {
47   if (input_type() == FL_HIDDEN_INPUT) return;
48   Fl_Boxtype b = box();
49   if (damage() & FL_DAMAGE_ALL) draw_box(b, color());
50   Fl_Input_::drawtext(x()+Fl::box_dx(b), y()+Fl::box_dy(b),
51 		      w()-Fl::box_dw(b), h()-Fl::box_dh(b));
52 }
53 
54 // kludge so shift causes selection to extend:
shift_position(int p)55 int Fl_Input::shift_position(int p) {
56   return position(p, Fl::event_state(FL_SHIFT) ? mark() : p);
57 }
58 
shift_up_down_position(int p)59 int Fl_Input::shift_up_down_position(int p) {
60   return up_down_position(p, Fl::event_state(FL_SHIFT));
61 }
62 
63 // Old text from FLTK 1.1 for reference:
64 // If you define NORMAL_INPUT_MOVE as zero you will get the peculiar fltk
65 // behavior where moving off the end of an input field will move the
66 // cursor into the next field:
67 // define it as 1 to prevent cursor movement from going to next field:
68 //
69 // Note: this has been replaced by Fl::option(Fl::OPTION_ARROW_FOCUS)
70 // in FLTK 1.3.  This option has "inverted" values:
71 //   1 = Arrow keys move focus (previously 0)
72 //   0 = Arrow keys don't move focus (previously 1)
73 // Hence we define ...
74 //
75 #define NORMAL_INPUT_MOVE (Fl::option(Fl::OPTION_ARROW_FOCUS) ? 0 : 1)
76 
77 #define ctrl(x) ((x)^0x40)
78 
79 // List of characters that are legal in a floating point input field.
80 // This text string is created at run-time to take the current locale
81 // into account (for example, continental Europe uses a comma instead
82 // of a decimal point). For back compatibility reasons, we always
83 // allow the decimal point.
84 #ifdef HAVE_LOCALECONV
85 static const char *standard_fp_chars = ".eE+-";
86 static const char *legal_fp_chars = 0L;
87 #else
88 static const char *legal_fp_chars = ".eE+-";
89 #endif
90 
91 // Move cursor up specified #lines
92 //    If OPTION_ARROW_FOCUS is disabled, return 1 to prevent focus navigation.
93 //
kf_lines_up(int repeat_num)94 int Fl_Input::kf_lines_up(int repeat_num) {
95   int i = position();
96   if (!line_start(i)) {
97     //UNNEEDED if (input_type()==FL_MULTILINE_INPUT && !Fl::option(Fl::OPTION_ARROW_FOCUS)) return 1;
98     return NORMAL_INPUT_MOVE;
99   }
100   while(repeat_num--) {
101     i = line_start(i);
102     if (!i) break;
103     i--;
104   }
105   shift_up_down_position(line_start(i));
106   return 1;
107 }
108 
109 // Move cursor down specified #lines
110 //    If OPTION_ARROW_FOCUS is disabled, return 1 to prevent focus navigation.
111 //
kf_lines_down(int repeat_num)112 int Fl_Input::kf_lines_down(int repeat_num) {
113   int i = position();
114   if (line_end(i) >= size()) {
115     //UNNEEDED if (input_type()==FL_MULTILINE_INPUT && !Fl::option(Fl::OPTION_ARROW_FOCUS)) return 1;
116     return NORMAL_INPUT_MOVE;
117   }
118   while (repeat_num--) {
119     i = line_end(i);
120     if (i >= size()) break;
121     i++;
122   }
123   shift_up_down_position(i);
124   return 1;
125 }
126 
127 // Move up a page
kf_page_up()128 int Fl_Input::kf_page_up() {
129   return kf_lines_up(linesPerPage());
130 }
131 
132 // Move down a page
kf_page_down()133 int Fl_Input::kf_page_down() {
134   return kf_lines_down(linesPerPage());
135 }
136 
137 // Toggle insert mode
kf_insert_toggle()138 int Fl_Input::kf_insert_toggle() {
139   if (readonly()) { fl_beep(); return 1; }
140   return 1;				// \todo: needs insert mode
141 }
142 
143 // Delete word right
kf_delete_word_right()144 int Fl_Input::kf_delete_word_right() {
145   if (readonly()) { fl_beep(); return 1; }
146   if (mark() != position()) return cut();
147   cut(position(), word_end(position()));
148   return 1;
149 }
150 
151 // Delete word left
kf_delete_word_left()152 int Fl_Input::kf_delete_word_left() {
153   if (readonly()) { fl_beep(); return 1; }
154   if (mark() != position()) return cut();
155   cut(word_start(position()), position());
156   return 1;
157 }
158 
159 // Delete to start of line
kf_delete_sol()160 int Fl_Input::kf_delete_sol() {
161   if (readonly()) { fl_beep(); return 1; }
162   if (mark() != position()) return cut();
163   cut(line_start(position()), position());
164   return 1;
165 }
166 
167 // Delete to end of line
kf_delete_eol()168 int Fl_Input::kf_delete_eol() {
169   if (readonly()) { fl_beep(); return 1; }
170   if (mark() != position()) return cut();
171   cut(position(), line_end(position()));
172   return 1;
173 }
174 
kf_delete_char_right()175 int Fl_Input::kf_delete_char_right() {
176   if (readonly()) { fl_beep(); return 1; }
177   if (mark() != position()) return cut();
178   else return cut(1);
179 }
180 
kf_delete_char_left()181 int Fl_Input::kf_delete_char_left() {
182   if (readonly()) { fl_beep(); return 1; }
183   if (mark() != position()) cut();
184   else cut(-1);
185   return 1;
186 }
187 
188 // Move cursor to start of line
kf_move_sol()189 int Fl_Input::kf_move_sol() {
190   return shift_position(line_start(position())) + NORMAL_INPUT_MOVE;
191 }
192 
193 // Move cursor to end of line
kf_move_eol()194 int Fl_Input::kf_move_eol() {
195   return shift_position(line_end(position())) + NORMAL_INPUT_MOVE;
196 }
197 
198 // Clear to end of line
kf_clear_eol()199 int Fl_Input::kf_clear_eol() {
200   if (readonly()) { fl_beep(); return 1; }
201   if (position()>=size()) return 0;
202   int i = line_end(position());
203   if (i == position() && i < size()) i++;
204   cut(position(), i);
205   return copy_cuts();
206 }
207 
208 // Move cursor one character to the left
209 //    If OPTION_ARROW_FOCUS is disabled, return 1 to prevent focus navigation.
210 //
kf_move_char_left()211 int Fl_Input::kf_move_char_left() {
212   int i = shift_position(position()-1) + NORMAL_INPUT_MOVE;
213   return Fl::option(Fl::OPTION_ARROW_FOCUS) ? i : 1;
214 }
215 
216 // Move cursor one character to the right
217 //    If OPTION_ARROW_FOCUS is disabled, return 1 to prevent focus navigation.
218 //
kf_move_char_right()219 int Fl_Input::kf_move_char_right() {
220   int i = shift_position(position()+1) + NORMAL_INPUT_MOVE;
221   return Fl::option(Fl::OPTION_ARROW_FOCUS) ? i : 1;
222 }
223 
224 // Move cursor word-left
kf_move_word_left()225 int Fl_Input::kf_move_word_left() {
226   shift_position(word_start(position()));
227   return 1;
228 }
229 
230 // Move cursor word-right
kf_move_word_right()231 int Fl_Input::kf_move_word_right() {
232   shift_position(word_end(position()));
233   return 1;
234 }
235 
236 // Move cursor up one line and to the start of line (paragraph up)
kf_move_up_and_sol()237 int Fl_Input::kf_move_up_and_sol() {
238   if (line_start(position())==position() && position()>0)
239     return shift_position(line_start(position()-1)) + NORMAL_INPUT_MOVE;
240   else
241     return shift_position(line_start(position())) + NORMAL_INPUT_MOVE;
242 }
243 
244 // Move cursor down one line and to the end of line (paragraph down)
kf_move_down_and_eol()245 int Fl_Input::kf_move_down_and_eol() {
246   if (line_end(position())==position() && position()<size())
247     return shift_position(line_end(position()+1)) + NORMAL_INPUT_MOVE;
248   else
249     return shift_position(line_end(position())) + NORMAL_INPUT_MOVE;
250 }
251 
252 // Move to top of document
kf_top()253 int Fl_Input::kf_top() {
254   shift_position(0);
255   return 1;
256 }
257 
258 // Move to bottom of document
kf_bottom()259 int Fl_Input::kf_bottom() {
260   shift_position(size());
261   return 1;
262 }
263 
264 // Select all text in the widget
kf_select_all()265 int Fl_Input::kf_select_all() {
266   position(0,size());
267   return 1;
268 }
269 
270 // Undo.
kf_undo()271 int Fl_Input::kf_undo() {
272   if (readonly()) { fl_beep(); return 1; }
273   return undo();
274 }
275 
276 // Redo. (currently unimplemented.. toggles undo() instead)
kf_redo()277 int Fl_Input::kf_redo() {
278   if (readonly()) { fl_beep(); return 1; }
279   return kf_undo();			// currently we don't support multilevel undo
280 }
281 
282 // Do a copy operation
kf_copy()283 int Fl_Input::kf_copy() {
284   return copy(1);
285 }
286 
287 // Do a paste operation
kf_paste()288 int Fl_Input::kf_paste() {
289   if (readonly()) { fl_beep(); return 1; }
290   Fl::paste(*this, 1);
291   return 1;
292 }
293 
294 // Do a cut with copy
kf_copy_cut()295 int Fl_Input::kf_copy_cut() {
296   if (readonly()) { fl_beep(); return 1; }
297   copy(1);
298   return cut();
299 }
300 
301 // Handle a keystroke.
302 //     Returns 1 if handled by us, 0 if not.
303 //
handle_key()304 int Fl_Input::handle_key() {
305 
306   char ascii = Fl::event_text()[0];
307 
308   int del;
309   if (Fl::compose(del)) {
310 
311     // Insert characters into numeric fields after checking for legality:
312     if (input_type() == FL_FLOAT_INPUT || input_type() == FL_INT_INPUT) {
313       Fl::compose_reset(); // ignore any foreign letters...
314 
315       // initialize the list of legal characters inside a floating point number
316 #ifdef HAVE_LOCALECONV
317       if (!legal_fp_chars) {
318         size_t len = strlen(standard_fp_chars);
319         struct lconv *lc = localeconv();
320         if (lc) {
321           if (lc->decimal_point) len += strlen(lc->decimal_point);
322           if (lc->mon_decimal_point) len += strlen(lc->mon_decimal_point);
323           if (lc->positive_sign) len += strlen(lc->positive_sign);
324           if (lc->negative_sign) len += strlen(lc->negative_sign);
325         }
326         // the following line is not a true memory leak because the array is only
327         // allocated once if required, and automatically freed when the program quits
328         char *chars = (char*)malloc(len+1);
329 	legal_fp_chars = chars;
330         strcpy(chars, standard_fp_chars);
331         if (lc) {
332           if (lc->decimal_point) strcat(chars, lc->decimal_point);
333           if (lc->mon_decimal_point) strcat(chars, lc->mon_decimal_point);
334           if (lc->positive_sign) strcat(chars, lc->positive_sign);
335           if (lc->negative_sign) strcat(chars, lc->negative_sign);
336         }
337       }
338 #endif // HAVE_LOCALECONV
339 
340       // find the insert position
341       int ip = position()<mark() ? position() : mark();
342       // This is complex to allow "0xff12" hex to be typed:
343       if (   (!ip && (ascii == '+' || ascii == '-'))
344           || (ascii >= '0' && ascii <= '9')
345           || (ip==1 && index(0)=='0' && (ascii=='x' || ascii == 'X'))
346           || (ip>1 && index(0)=='0' && (index(1)=='x'||index(1)=='X')
347               && ((ascii>='A'&& ascii<='F') || (ascii>='a'&& ascii<='f')))
348           || (input_type()==FL_FLOAT_INPUT && ascii && strchr(legal_fp_chars, ascii)))
349       {
350 	if (readonly()) fl_beep();
351 	else replace(position(), mark(), &ascii, 1);
352       }
353       return 1;
354     }
355 
356     if (del || Fl::event_length()) {
357       if (readonly()) fl_beep();
358       else replace(position(), del ? position()-del : mark(),
359 	           Fl::event_text(), Fl::event_length());
360     }
361 #ifdef __APPLE__
362     if (Fl::compose_state) {
363       this->mark( this->position() - Fl::compose_state );
364       }
365 #endif
366     return 1;
367   }
368 
369   unsigned int mods = Fl::event_state() & (FL_META|FL_CTRL|FL_ALT);
370   unsigned int shift = Fl::event_state() & FL_SHIFT;
371   unsigned int multiline = (input_type() == FL_MULTILINE_INPUT) ? 1 : 0;
372   //
373   // The following lists apps that support these keypresses.
374   // Prefixes: '!' indicates NOT supported, '?' indicates un-verified.
375   //
376   //    HIG=Human Interface Guide,
377   //    TE=TextEdit.app, SA=Safari.app, WOX=MS Word/OSX -- OSX 10.4.x
378   //    NP=Notepad, WP=WordPad, WOW=MS Word/Windows     -- WinXP
379   //    GE=gedit, KE=kedit                              -- Ubuntu8.04
380   //    OF=old FLTK behavior (<=1.1.10)
381   //
382   // Example: (NP,WP,!WO) means supported in notepad + wordpad, but NOT word.
383   //
384   switch (Fl::event_key()) {
385 
386     case FL_Insert:
387       // Note: Mac has no "Insert" key; it's the "Help" key.
388       //       This keypress is apparently not possible on macs.
389       //
390       if (mods==0 && shift) return kf_paste();			// Shift-Insert   (WP,NP,WOW,GE,KE,OF)
391       if (mods==0)          return kf_insert_toggle();		// Insert         (Standard)
392       if (mods==FL_CTRL)    return kf_copy();			// Ctrl-Insert    (WP,NP,WOW,GE,KE,OF)
393       return 0;							// ignore other combos, pass to parent
394 
395     case FL_Delete: {
396 #ifdef __APPLE__
397       if (mods==0)          return kf_delete_char_right();	// Delete         (OSX-HIG,TE,SA,WOX)
398       if (mods==FL_CTRL)    return kf_delete_char_right();	// Ctrl-Delete    (??? TE,!SA,!WOX)
399       if (mods==FL_ALT)     return kf_delete_word_right();	// Alt-Delete     (OSX-HIG,TE,SA)
400       return 0;							// ignore other combos, pass to parent
401 #else
402       int selected = (position() != mark()) ? 1 : 0;
403       if (mods==0 && shift && selected)
404                             return kf_copy_cut();		// Shift-Delete with selection (WP,NP,WOW,GE,KE,OF)
405       if (mods==0 && shift && !selected)
406                             return kf_delete_char_right();	// Shift-Delete no selection (WP,NP,WOW,GE,KE,!OF)
407       if (mods==0)          return kf_delete_char_right();	// Delete         (Standard)
408       if (mods==FL_CTRL)    return kf_delete_word_right();	// Ctrl-Delete    (WP,!NP,WOW,GE,KE,!OF)
409       return 0;							// ignore other combos, pass to parent
410 #endif
411     }
412 
413     case FL_Left:
414 #ifdef __APPLE__
415       if (mods==0)          return kf_move_char_left();		// Left           (OSX-HIG)
416       if (mods==FL_ALT)     return kf_move_word_left();		// Alt-Left       (OSX-HIG)
417       if (mods==FL_META)    return kf_move_sol();		// Meta-Left      (OSX-HIG)
418       if (mods==FL_CTRL)    return kf_move_sol();		// Ctrl-Left      (TE/SA)
419       return 0;							// ignore other combos, pass to parent
420 #else
421       if (mods==0)          return kf_move_char_left();		// Left           (WP,NP,WOW,GE,KE,OF)
422       if (mods==FL_CTRL)    return kf_move_word_left();		// Ctrl-Left      (WP,NP,WOW,GE,KE,!OF)
423       if (mods==FL_META)    return kf_move_char_left();		// Meta-Left      (WP,NP,?WOW,GE,KE)
424       return 0;							// ignore other combos, pass to parent
425 #endif
426 
427     case FL_Right:
428 #ifdef __APPLE__
429       if (mods==0)          return kf_move_char_right();	// Right          (OSX-HIG)
430       if (mods==FL_ALT)     return kf_move_word_right();	// Alt-Right      (OSX-HIG)
431       if (mods==FL_META)    return kf_move_eol();		// Meta-Right     (OSX-HIG)
432       if (mods==FL_CTRL)    return kf_move_eol();		// Ctrl-Right     (TE/SA)
433       return 0;							// ignore other combos, pass to parent
434 #else
435       if (mods==0)          return kf_move_char_right();	// Right          (WP,NP,WOW,GE,KE,OF)
436       if (mods==FL_CTRL)    return kf_move_word_right();	// Ctrl-Right     (WP,NP,WOW,GE,KE,!OF)
437       if (mods==FL_META)    return kf_move_char_right();	// Meta-Right     (WP,NP,?WOW,GE,KE,!OF)
438       return 0;							// ignore other combos, pass to parent
439 #endif
440 
441     case FL_Up:
442 #ifdef __APPLE__
443       if (mods==0)          return kf_lines_up(1);		// Up             (OSX-HIG)
444       if (mods==FL_CTRL)    return kf_page_up();		// Ctrl-Up        (TE !HIG)
445       if (mods==FL_ALT)     return kf_move_up_and_sol();	// Alt-Up         (OSX-HIG)
446       if (mods==FL_META)    return kf_top();			// Meta-Up        (OSX-HIG)
447       return 0;							// ignore other combos, pass to parent
448 #else
449       if (mods==0)          return kf_lines_up(1);		// Up             (WP,NP,WOW,GE,KE,OF)
450       if (mods==FL_CTRL)    return kf_move_up_and_sol();	// Ctrl-Up        (WP,!NP,WOW,GE,!KE,OF)
451       return 0;							// ignore other combos, pass to parent
452 #endif
453 
454     case FL_Down:
455 #ifdef __APPLE__
456       if (mods==0)          return kf_lines_down(1);		// Dn             (OSX-HIG)
457       if (mods==FL_CTRL)    return kf_page_down();		// Ctrl-Dn        (TE !HIG)
458       if (mods==FL_ALT)     return kf_move_down_and_eol();	// Alt-Dn         (OSX-HIG)
459       if (mods==FL_META)    return kf_bottom();			// Meta-Dn        (OSX-HIG)
460       return 0;							// ignore other combos, pass to parent
461 #else
462       if (mods==0)          return kf_lines_down(1);		// Dn             (WP,NP,WOW,GE,KE,OF)
463       if (mods==FL_CTRL)    return kf_move_down_and_eol();	// Ctrl-Down      (WP,!NP,WOW,GE,!KE,OF)
464       return 0;							// ignore other combos, pass to parent
465 #endif
466 
467     case FL_Page_Up:
468       // Fl_Input has no scroll control, so instead we move the cursor by one page
469       // OSX-HIG recommends Alt increase one semantic unit, Meta next higher..
470 #ifdef __APPLE__
471       if (mods==0)          return kf_page_up();		// PgUp           (OSX-HIG)
472       if (mods==FL_ALT)     return kf_page_up();		// Alt-PageUp     (OSX-HIG)
473       if (mods==FL_META)    return kf_top();			// Meta-PageUp    (OSX-HIG,!TE)
474       return 0;							// ignore other combos, pass to parent
475 #else
476       if (mods==0)          return kf_page_up();		// PageUp         (WP,NP,WOW,GE,KE)
477       if (mods==FL_CTRL)    return kf_page_up();		// Ctrl-PageUp    (!WP,!NP,!WOW,!GE,KE,OF)
478       if (mods==FL_ALT)     return kf_page_up();		// Alt-PageUp     (!WP,!NP,!WOW,!GE,KE,OF)
479       return 0;							// ignore other combos, pass to parent
480 #endif
481 
482     case FL_Page_Down:
483 #ifdef __APPLE__
484       // Fl_Input has no scroll control, so instead we move the cursor by one page
485       // OSX-HIG recommends Alt increase one semantic unit, Meta next higher..
486       if (mods==0)          return kf_page_down();		// PgDn           (OSX-HIG)
487       if (mods==FL_ALT)     return kf_page_down();		// Alt-PageDn     (OSX-HIG)
488       if (mods==FL_META)    return kf_bottom();			// Meta-PageDn    (OSX-HIG,!TE)
489       return 0;							// ignore other combos, pass to parent
490 #else
491       if (mods==0)          return kf_page_down();		// PageDn         (WP,NP,WOW,GE,KE)
492       if (mods==FL_CTRL)    return kf_page_down();		// Ctrl-PageDn    (!WP,!NP,!WOW,!GE,KE,OF)
493       if (mods==FL_ALT)     return kf_page_down();		// Alt-PageDn     (!WP,!NP,!WOW,!GE,KE,OF)
494       return 0;							// ignore other combos, pass to parent
495 #endif
496 
497     case FL_Home:
498 #ifdef __APPLE__
499       if (mods==0)          return kf_top();			// Home           (OSX-HIG)
500       if (mods==FL_ALT)     return kf_top();			// Alt-Home       (???)
501       return 0;							// ignore other combos, pass to parent
502 #else
503       if (mods==0)          return kf_move_sol();		// Home           (WP,NP,WOW,GE,KE,OF)
504       if (mods==FL_CTRL)    return kf_top();			// Ctrl-Home      (WP,NP,WOW,GE,KE,OF)
505       return 0;							// ignore other combos, pass to parent
506 #endif
507 
508     case FL_End:
509 #ifdef __APPLE__
510       if (mods==0)          return kf_bottom();			// End            (OSX-HIG)
511       if (mods==FL_ALT)     return kf_bottom();			// Alt-End        (???)
512       return 0;							// ignore other combos, pass to parent
513 #else
514       if (mods==0)          return kf_move_eol();		// End            (WP,NP,WOW,GE,KE,OF)
515       if (mods==FL_CTRL)    return kf_bottom();			// Ctrl-End       (WP,NP,WOW,GE,KE,OF)
516       return 0;							// ignore other combos, pass to parent
517 #endif
518 
519     case FL_BackSpace:
520 #ifdef __APPLE__
521       if (mods==0)          return kf_delete_char_left();	// Backspace      (OSX-HIG)
522       if (mods==FL_CTRL)    return kf_delete_char_left();	// Ctrl-Backspace (TE/SA)
523       if (mods==FL_ALT)     return kf_delete_word_left();	// Alt-Backspace  (OSX-HIG)
524       if (mods==FL_META)    return kf_delete_sol();		// Meta-Backspace (OSX-HIG,!TE)
525       return 0;							// ignore other combos, pass to parent
526 #else
527       if (mods==0)          return kf_delete_char_left();	// Backspace      (WP,NP,WOW,GE,KE,OF)
528       if (mods==FL_CTRL)    return kf_delete_word_left();	// Ctrl-Backspace (WP,!NP,WOW,GE,KE,!OF)
529       return 0;							// ignore other combos, pass to parent
530 #endif
531 
532     case FL_Enter:
533     case FL_KP_Enter:
534       if (when() & FL_WHEN_ENTER_KEY) {
535         position(size(), 0);
536         maybe_do_callback();
537         return 1;
538       } else if (multiline && !readonly()) {
539         return replace(position(), mark(), "\n", 1);
540       } return 0;			// reserved for shortcuts
541 
542     case FL_Tab:
543       // Handle special case for multiline input with 'old tab behavior';
544       // tab handled as a normal insertable character.
545       //
546       if (mods==0 && !shift 		// Tab?
547 	   && !tab_nav()		// old tab behavior enabled?
548 	   && multiline) {		// multiline input?
549         break;				// insert tab character
550       }
551       if (mods==0) return 0;					// Tab, Shift-Tab? nav focus      (Standard/OSX-HIG)
552       return 0;							// ignore other combos, pass to parent
553 
554     case 'a':
555       if (mods==FL_COMMAND) return kf_select_all();		// Ctrl-A, Mac:Meta-A             (Standard/OSX-HIG)
556       break;							// handle other combos elsewhere
557     case 'c':
558       if (mods==FL_COMMAND) return kf_copy();			// Ctrl-C, Mac:Meta-C             (Standard/OSX-HIG)
559       break;							// handle other combos elsewhere
560     case 'v':
561       if (mods==FL_COMMAND) return kf_paste();			// Ctrl-V, Mac:Meta-V             (Standard/OSX-HIG)
562       break;							// handle other combos elsewhere
563     case 'x':
564       if (mods==FL_COMMAND) return kf_copy_cut();		// Ctrl-X, Mac:Meta-X             (Standard/OSX-HIG)
565       break;
566     case 'z':
567       if (mods==FL_COMMAND && !shift) return kf_undo();		// Ctrl-Z, Mac:Meta-Z             (Standard/OSX-HIG)
568       if (mods==FL_COMMAND && shift)  return kf_redo();		// Shift-Ctrl-Z, Mac:Shift-Meta-Z (Standard/OSX-HIG)
569       break;							// handle other combos elsewhere
570   }
571 
572   switch (ascii) {
573     case ctrl('H'):
574       return kf_delete_char_left();				// Ctrl-H                           (!WP,!NP,!WOW,!WOX,TE,SA,GE,KE,OF)
575     case ctrl('I'): 						// Ctrl-I (literal Tab)             (!WP,NP,!WOW,!GE,KE,OF)
576     case ctrl('J'):						// Ctrl-J (literal Line Feed/Enter) (Standard)
577     case ctrl('L'):						// Ctrl-L (literal Form Feed)       (Standard)
578     case ctrl('M'):						// Ctrl-M (literal Cr)              (Standard)
579       if (readonly()) { fl_beep(); return 1; }
580       // insert a few selected control characters literally:
581       if (input_type() != FL_FLOAT_INPUT && input_type() != FL_INT_INPUT)
582         return replace(position(), mark(), &ascii, 1);
583       break;
584   }
585 
586   return 0;		// ignored
587 }
588 
handle(int event)589 int Fl_Input::handle(int event) {
590   static int dnd_save_position, dnd_save_mark, drag_start = -1, newpos;
591   static Fl_Widget *dnd_save_focus = NULL;
592   switch (event) {
593 #ifdef __APPLE__
594     case FL_UNFOCUS:
595       if (Fl::compose_state) {
596 	this->mark( this->position() );
597 	Fl::reset_marked_text();
598       }
599       break;
600 #endif
601     case FL_FOCUS:
602       switch (Fl::event_key()) {
603         case FL_Right:
604           position(0);
605           break;
606         case FL_Left:
607           position(size());
608           break;
609         case FL_Down:
610           up_down_position(0);
611           break;
612         case FL_Up:
613           up_down_position(line_start(size()));
614           break;
615         case FL_Tab:
616           position(size(),0);
617           break;
618         default:
619           position(position(),mark());// turns off the saved up/down arrow position
620           break;
621       }
622       break;
623 
624     case FL_KEYBOARD:
625       // Handle special case for multiline input with 'old tab behavior'
626       // where tab is entered as a character: make sure user attempt to 'tab over'
627       // widget doesn't destroy the field, replacing it with a tab character.
628       //
629       if (Fl::event_key() == FL_Tab 			// Tab key?
630           && !Fl::event_state(FL_SHIFT)			// no shift?
631           && !tab_nav()					// with tab navigation disabled?
632 	  && input_type() == FL_MULTILINE_INPUT		// with a multiline input?
633 	  && size() > 0                                 // non-empty field?
634           && ((mark()==0 && position()==size()) || (position()==0 && mark()==size()))) {// while entire field selected?
635         // Set cursor to the end of the selection...
636         if (mark() > position())
637           position(mark());
638         else
639           position(position());
640         return (1);
641       } else {
642         if (active_r() && window() && this == Fl::belowmouse())
643           window()->cursor(FL_CURSOR_NONE);
644         return handle_key();
645       }
646       //NOTREACHED
647 
648     case FL_PUSH:
649       if (Fl::dnd_text_ops()) {
650         int oldpos = position(), oldmark = mark();
651         Fl_Boxtype b = box();
652         Fl_Input_::handle_mouse(x()+Fl::box_dx(b), y()+Fl::box_dy(b),
653                                 w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0);
654         newpos = position();
655         position( oldpos, oldmark );
656         if (Fl::focus()==this && !Fl::event_state(FL_SHIFT) && input_type()!=FL_SECRET_INPUT &&
657            ( (newpos >= mark() && newpos < position()) ||
658              (newpos >= position() && newpos < mark()) ) ) {
659           // user clicked in the selection, may be trying to drag
660           drag_start = newpos;
661           return 1;
662         }
663         drag_start = -1;
664       }
665 
666       if (Fl::focus() != this) {
667         Fl::focus(this);
668         handle(FL_FOCUS);
669       }
670       break;
671 
672     case FL_DRAG:
673       if (Fl::dnd_text_ops()) {
674         if (drag_start >= 0) {
675           if (Fl::event_is_click()) return 1; // debounce the mouse
676                                               // save the position because sometimes we don't get DND_ENTER:
677           dnd_save_position = position();
678           dnd_save_mark = mark();
679 	  dnd_save_focus = this;
680           // drag the data:
681           copy(0);
682 #ifdef __APPLE__
683           Fl_X::dnd(1);
684 #else
685           Fl::dnd();
686 #endif
687           return 1;
688         }
689       }
690       break;
691 
692     case FL_RELEASE:
693       if (Fl::event_button() == 2) {
694         Fl::event_is_click(0); // stop double click from picking a word
695         Fl::paste(*this, 0);
696       } else if (!Fl::event_is_click()) {
697         // copy drag-selected text to the clipboard.
698         copy(0);
699       } else if (Fl::event_is_click() && drag_start >= 0) {
700         // user clicked in the field and wants to reset the cursor position...
701         position(drag_start, drag_start);
702         drag_start = -1;
703       } else if (Fl::event_clicks()) {
704         // user double or triple clicked to select word or whole text
705         copy(0);
706       }
707 
708       // For output widgets, do the callback so the app knows the user
709       // did something with the mouse...
710       if (readonly()) do_callback();
711 
712       return 1;
713 
714     case FL_DND_ENTER:
715       Fl::belowmouse(this); // send the leave events first
716       if (dnd_save_focus != this) {
717 	dnd_save_position = position();
718 	dnd_save_mark = mark();
719 	dnd_save_focus = Fl::focus();
720         Fl::focus(this);
721         handle(FL_FOCUS);
722       }
723       // fall through:
724     case FL_DND_DRAG:
725       //int p = mouse_position(X, Y, W, H);
726 #ifdef DND_OUT_XXXX
727       if (Fl::focus()==this && (p>=dnd_save_position && p<=dnd_save_mark ||
728                                 p>=dnd_save_mark && p<=dnd_save_position)) {
729         position(dnd_save_position, dnd_save_mark);
730         return 0;
731       }
732 #endif
733       {
734         Fl_Boxtype b = box();
735         Fl_Input_::handle_mouse(x()+Fl::box_dx(b), y()+Fl::box_dy(b),
736                                 w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0);
737       }
738       return 1;
739 
740     case FL_DND_LEAVE:
741       position(dnd_save_position, dnd_save_mark);
742 #ifdef DND_OUT_XXXX
743       if (!focused())
744 #endif
745         if (dnd_save_focus && dnd_save_focus != this) {
746           Fl::focus(dnd_save_focus);
747           handle(FL_UNFOCUS);
748         }
749 #if !(defined(__APPLE__) || defined(WIN32))
750       Fl::first_window()->cursor(FL_CURSOR_MOVE);
751 #endif
752       dnd_save_focus = NULL;
753       return 1;
754 
755     case FL_DND_RELEASE:
756       if (dnd_save_focus == this) { // if the dragged text comes from the same widget
757 	if (!readonly()) {
758 	  // remove the selected text
759 	  int old_position = position();
760 	  if (dnd_save_mark > dnd_save_position) {
761 	    int tmp = dnd_save_mark;
762 	    dnd_save_mark = dnd_save_position;
763 	    dnd_save_position = tmp;
764 	  }
765 	  replace(dnd_save_mark, dnd_save_position, NULL, 0);
766 	  if (old_position > dnd_save_position)
767 	    position(old_position - (dnd_save_position - dnd_save_mark));
768 	  else
769 	    position(old_position);
770 	} // !readonly()
771       } // from the same widget
772       else if (dnd_save_focus) {
773 	dnd_save_focus->handle(FL_UNFOCUS);
774       }
775       dnd_save_focus = NULL;
776       take_focus();
777       return 1;
778 
779       /* TODO: this will scroll the area, but stop if the cursor would become invisible.
780        That clipping happens in drawtext(). Do we change the clipping or should
781        we move the cursor (ouch)?
782        case FL_MOUSEWHEEL:
783        if (Fl::e_dy > 0) {
784        yscroll( yscroll() - Fl::e_dy*15 );
785        } else if (Fl::e_dy < 0) {
786        yscroll( yscroll() - Fl::e_dy*15 );
787        }
788        return 1;
789        */
790   }
791   Fl_Boxtype b = box();
792   return Fl_Input_::handletext(event,
793                                x()+Fl::box_dx(b), y()+Fl::box_dy(b),
794                                w()-Fl::box_dw(b), h()-Fl::box_dh(b));
795 }
796 
797 /**
798  Creates a new Fl_Input widget using the given position, size,
799  and label string. The default boxtype is FL_DOWN_BOX.
800  */
Fl_Input(int X,int Y,int W,int H,const char * l)801 Fl_Input::Fl_Input(int X, int Y, int W, int H, const char *l)
802 : Fl_Input_(X, Y, W, H, l) {
803 }
804 
805 
Fl_Float_Input(int X,int Y,int W,int H,const char * l)806 Fl_Float_Input::Fl_Float_Input(int X,int Y,int W,int H,const char *l)
807 : Fl_Input(X,Y,W,H,l)
808 {
809   type(FL_FLOAT_INPUT);
810   clear_flag(MAC_USE_ACCENTS_MENU);
811 }
812 
813 
Fl_Int_Input(int X,int Y,int W,int H,const char * l)814 Fl_Int_Input::Fl_Int_Input(int X,int Y,int W,int H,const char *l)
815 : Fl_Input(X,Y,W,H,l) {
816   type(FL_INT_INPUT);
817   clear_flag(MAC_USE_ACCENTS_MENU);
818 }
819 
820 
Fl_Multiline_Input(int X,int Y,int W,int H,const char * l)821 Fl_Multiline_Input::Fl_Multiline_Input(int X,int Y,int W,int H,const char *l)
822 : Fl_Input(X,Y,W,H,l) {
823   type(FL_MULTILINE_INPUT);
824 }
825 
826 
Fl_Output(int X,int Y,int W,int H,const char * l)827 Fl_Output::Fl_Output(int X,int Y,int W,int H, const char *l)
828 : Fl_Input(X, Y, W, H, l) {
829   type(FL_NORMAL_OUTPUT);
830 }
831 
832 
Fl_Multiline_Output(int X,int Y,int W,int H,const char * l)833 Fl_Multiline_Output::Fl_Multiline_Output(int X,int Y,int W,int H,const char *l)
834 : Fl_Output(X,Y,W,H,l) {
835   type(FL_MULTILINE_OUTPUT);
836 }
837 
838 
Fl_Secret_Input(int X,int Y,int W,int H,const char * l)839 Fl_Secret_Input::Fl_Secret_Input(int X,int Y,int W,int H,const char *l)
840 : Fl_Input(X,Y,W,H,l) {
841   type(FL_SECRET_INPUT);
842   clear_flag(MAC_USE_ACCENTS_MENU);
843 }
844 
handle(int event)845 int Fl_Secret_Input::handle(int event) {
846   int retval = Fl_Input::handle(event);
847 #ifdef __APPLE__
848   if (event == FL_KEYBOARD && Fl::compose_state) {
849     this->mark( this->position() ); // don't underline marked text
850   }
851 #endif
852   return retval;
853 }
854 
855 //
856 // End of "$Id$".
857 //
858