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