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