DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame^] | 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 | |
| 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: |
| 66 | int Fl_Input::shift_position(int p) { |
| 67 | return position(p, Fl::event_state(FL_SHIFT) ? mark() : p); |
| 68 | } |
| 69 | |
| 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 | // |
| 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 | // |
| 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 |
| 139 | int Fl_Input::kf_page_up() { |
| 140 | return kf_lines_up(linesPerPage()); |
| 141 | } |
| 142 | |
| 143 | // Move down a page |
| 144 | int Fl_Input::kf_page_down() { |
| 145 | return kf_lines_down(linesPerPage()); |
| 146 | } |
| 147 | |
| 148 | // Toggle insert mode |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 | |
| 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 | |
| 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 |
| 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 |
| 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 |
| 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 | // |
| 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 | // |
| 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 |
| 236 | int Fl_Input::kf_move_word_left() { |
| 237 | shift_position(word_start(position())); |
| 238 | return 1; |
| 239 | } |
| 240 | |
| 241 | // Move cursor 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) |
| 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) |
| 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 |
| 264 | int Fl_Input::kf_top() { |
| 265 | shift_position(0); |
| 266 | return 1; |
| 267 | } |
| 268 | |
| 269 | // Move to bottom of document |
| 270 | int Fl_Input::kf_bottom() { |
| 271 | shift_position(size()); |
| 272 | return 1; |
| 273 | } |
| 274 | |
| 275 | // Select all text in the widget |
| 276 | int Fl_Input::kf_select_all() { |
| 277 | position(0,size()); |
| 278 | return 1; |
| 279 | } |
| 280 | |
| 281 | // 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) |
| 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 |
| 294 | int Fl_Input::kf_copy() { |
| 295 | return copy(1); |
| 296 | } |
| 297 | |
| 298 | // Do a paste operation |
| 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 |
| 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 | // |
| 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 | |
| 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 | */ |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | // |