1 | // |
---|
2 | // "$Id$" |
---|
3 | // |
---|
4 | // Input widget for the Fast Light Tool Kit (FLTK). |
---|
5 | // |
---|
6 | // Copyright 1998-2006 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 | #ifdef HAVE_LOCALE_H |
---|
44 | # include <locale.h> |
---|
45 | #endif |
---|
46 | |
---|
47 | |
---|
48 | void Fl_Input::draw() { |
---|
49 | if (input_type() == FL_HIDDEN_INPUT) return; |
---|
50 | Fl_Boxtype b = box(); |
---|
51 | if (damage() & FL_DAMAGE_ALL) draw_box(b, color()); |
---|
52 | Fl_Input_::drawtext(x()+Fl::box_dx(b), y()+Fl::box_dy(b), |
---|
53 | w()-Fl::box_dw(b), h()-Fl::box_dh(b)); |
---|
54 | } |
---|
55 | |
---|
56 | // kludge so shift causes selection to extend: |
---|
57 | int Fl_Input::shift_position(int p) { |
---|
58 | return position(p, Fl::event_state(FL_SHIFT) ? mark() : p); |
---|
59 | } |
---|
60 | int Fl_Input::shift_up_down_position(int p) { |
---|
61 | return up_down_position(p, Fl::event_state(FL_SHIFT)); |
---|
62 | } |
---|
63 | |
---|
64 | // If you define this symbol 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 | #define NORMAL_INPUT_MOVE 0 |
---|
69 | |
---|
70 | #define ctrl(x) ((x)^0x40) |
---|
71 | |
---|
72 | // List of characters that are legal in a floating point input field. |
---|
73 | // This text string is created at run-time to take the current locale |
---|
74 | // into account (for example, continental Europe uses a comma instead |
---|
75 | // of a decimal point). For back compatibility reasons, we always |
---|
76 | // allow the decimal point. |
---|
77 | #ifdef HAVE_LOCALECONV |
---|
78 | static const char *standard_fp_chars = ".eE+-"; |
---|
79 | static const char *legal_fp_chars = 0L; |
---|
80 | #else |
---|
81 | static const char *legal_fp_chars = ".eE+-"; |
---|
82 | #endif |
---|
83 | |
---|
84 | int Fl_Input::handle_key() { |
---|
85 | |
---|
86 | char ascii = Fl::event_text()[0]; |
---|
87 | |
---|
88 | int repeat_num=1; |
---|
89 | |
---|
90 | int del; |
---|
91 | if (Fl::compose(del)) { |
---|
92 | |
---|
93 | // Insert characters into numeric fields after checking for legality: |
---|
94 | if (input_type() == FL_FLOAT_INPUT || input_type() == FL_INT_INPUT) { |
---|
95 | Fl::compose_reset(); // ignore any foreign letters... |
---|
96 | |
---|
97 | // initialize the list of legal characters inside a floating point number |
---|
98 | #ifdef HAVE_LOCALECONV |
---|
99 | if (!legal_fp_chars) { |
---|
100 | int len = strlen(standard_fp_chars); |
---|
101 | struct lconv *lc = localeconv(); |
---|
102 | if (lc) { |
---|
103 | if (lc->decimal_point) len += strlen(lc->decimal_point); |
---|
104 | if (lc->mon_decimal_point) len += strlen(lc->mon_decimal_point); |
---|
105 | if (lc->positive_sign) len += strlen(lc->positive_sign); |
---|
106 | if (lc->negative_sign) len += strlen(lc->negative_sign); |
---|
107 | } |
---|
108 | // the following line is not a true memory leak because the array is only |
---|
109 | // allocated once if required, and automatically freed when the program quits |
---|
110 | char *chars = (char*)malloc(len+1); |
---|
111 | legal_fp_chars = chars; |
---|
112 | strcpy(chars, standard_fp_chars); |
---|
113 | if (lc) { |
---|
114 | if (lc->decimal_point) strcat(chars, lc->decimal_point); |
---|
115 | if (lc->mon_decimal_point) strcat(chars, lc->mon_decimal_point); |
---|
116 | if (lc->positive_sign) strcat(chars, lc->positive_sign); |
---|
117 | if (lc->negative_sign) strcat(chars, lc->negative_sign); |
---|
118 | } |
---|
119 | } |
---|
120 | #endif // HAVE_LOCALECONV |
---|
121 | |
---|
122 | // find the insert position |
---|
123 | int ip = position()<mark() ? position() : mark(); |
---|
124 | // This is complex to allow "0xff12" hex to be typed: |
---|
125 | if (!ip && (ascii == '+' || ascii == '-') |
---|
126 | || (ascii >= '0' && ascii <= '9') |
---|
127 | || (ip==1 && index(0)=='0' && (ascii=='x' || ascii == 'X')) |
---|
128 | || (ip>1 && index(0)=='0' && (index(1)=='x'||index(1)=='X') |
---|
129 | && (ascii>='A'&& ascii<='F' || ascii>='a'&& ascii<='f')) |
---|
130 | || input_type()==FL_FLOAT_INPUT && ascii && strchr(legal_fp_chars, ascii)) |
---|
131 | { |
---|
132 | if (readonly()) fl_beep(); |
---|
133 | else replace(position(), mark(), &ascii, 1); |
---|
134 | } |
---|
135 | return 1; |
---|
136 | } |
---|
137 | |
---|
138 | if (del || Fl::event_length()) { |
---|
139 | if (readonly()) fl_beep(); |
---|
140 | else replace(position(), del ? position()-del : mark(), |
---|
141 | Fl::event_text(), Fl::event_length()); |
---|
142 | } |
---|
143 | return 1; |
---|
144 | } |
---|
145 | |
---|
146 | switch (Fl::event_key()) { |
---|
147 | case FL_Insert: |
---|
148 | if (Fl::event_state() & FL_CTRL) ascii = ctrl('C'); |
---|
149 | else if (Fl::event_state() & FL_SHIFT) ascii = ctrl('V'); |
---|
150 | break; |
---|
151 | case FL_Delete: |
---|
152 | if (Fl::event_state() & FL_SHIFT) ascii = ctrl('X'); |
---|
153 | else ascii = ctrl('D'); |
---|
154 | break; |
---|
155 | case FL_Left: |
---|
156 | ascii = ctrl('B'); break; |
---|
157 | case FL_Right: |
---|
158 | ascii = ctrl('F'); break; |
---|
159 | case FL_Page_Up: |
---|
160 | fl_font(textfont(),textsize()); //ensure current font is set to ours |
---|
161 | repeat_num=h()/fl_height(); // number of lines to scroll |
---|
162 | if (!repeat_num) repeat_num=1; |
---|
163 | case FL_Up: |
---|
164 | ascii = ctrl('P'); break; |
---|
165 | case FL_Page_Down: |
---|
166 | fl_font(textfont(),textsize()); |
---|
167 | repeat_num=h()/fl_height(); |
---|
168 | if (!repeat_num) repeat_num=1; |
---|
169 | case FL_Down: |
---|
170 | ascii = ctrl('N'); break; |
---|
171 | case FL_Home: |
---|
172 | if (Fl::event_state() & FL_CTRL) { |
---|
173 | shift_position(0); |
---|
174 | return 1; |
---|
175 | } |
---|
176 | ascii = ctrl('A'); |
---|
177 | break; |
---|
178 | case FL_End: |
---|
179 | if (Fl::event_state() & FL_CTRL) { |
---|
180 | shift_position(size()); |
---|
181 | return 1; |
---|
182 | } |
---|
183 | ascii = ctrl('E'); break; |
---|
184 | |
---|
185 | case FL_BackSpace: |
---|
186 | ascii = ctrl('H'); break; |
---|
187 | case FL_Enter: |
---|
188 | case FL_KP_Enter: |
---|
189 | if (when() & FL_WHEN_ENTER_KEY) { |
---|
190 | position(size(), 0); |
---|
191 | maybe_do_callback(); |
---|
192 | return 1; |
---|
193 | } else if (input_type() == FL_MULTILINE_INPUT && !readonly()) |
---|
194 | return replace(position(), mark(), "\n", 1); |
---|
195 | else |
---|
196 | return 0; // reserved for shortcuts |
---|
197 | case FL_Tab: |
---|
198 | if (Fl::event_state(FL_CTRL|FL_SHIFT) || input_type()!=FL_MULTILINE_INPUT || readonly()) return 0; |
---|
199 | return replace(position(), mark(), &ascii, 1); |
---|
200 | #ifdef __APPLE__ |
---|
201 | case 'c' : |
---|
202 | case 'v' : |
---|
203 | case 'x' : |
---|
204 | case 'z' : |
---|
205 | // printf("'%c' (0x%02x) pressed with%s%s%s%s\n", ascii, ascii, |
---|
206 | // Fl::event_state(FL_SHIFT) ? " FL_SHIFT" : "", |
---|
207 | // Fl::event_state(FL_CTRL) ? " FL_CTRL" : "", |
---|
208 | // Fl::event_state(FL_ALT) ? " FL_ALT" : "", |
---|
209 | // Fl::event_state(FL_META) ? " FL_META" : ""); |
---|
210 | if (Fl::event_state(FL_META)) ascii -= 0x60; |
---|
211 | // printf("using '%c' (0x%02x)...\n", ascii, ascii); |
---|
212 | break; |
---|
213 | #endif // __APPLE__ |
---|
214 | } |
---|
215 | |
---|
216 | int i; |
---|
217 | switch (ascii) { |
---|
218 | case ctrl('A'): |
---|
219 | return shift_position(line_start(position())) + NORMAL_INPUT_MOVE; |
---|
220 | case ctrl('B'): |
---|
221 | return shift_position(position()-1) + NORMAL_INPUT_MOVE; |
---|
222 | case ctrl('C'): // copy |
---|
223 | return copy(1); |
---|
224 | case ctrl('D'): |
---|
225 | case ctrl('?'): |
---|
226 | if (readonly()) { |
---|
227 | fl_beep(); |
---|
228 | return 1; |
---|
229 | } |
---|
230 | if (mark() != position()) return cut(); |
---|
231 | else return cut(1); |
---|
232 | case ctrl('E'): |
---|
233 | return shift_position(line_end(position())) + NORMAL_INPUT_MOVE; |
---|
234 | case ctrl('F'): |
---|
235 | return shift_position(position()+1) + NORMAL_INPUT_MOVE; |
---|
236 | case ctrl('H'): |
---|
237 | if (readonly()) { |
---|
238 | fl_beep(); |
---|
239 | return 1; |
---|
240 | } |
---|
241 | if (mark() != position()) cut(); |
---|
242 | else cut(-1); |
---|
243 | return 1; |
---|
244 | case ctrl('K'): |
---|
245 | if (readonly()) { |
---|
246 | fl_beep(); |
---|
247 | return 1; |
---|
248 | } |
---|
249 | if (position()>=size()) return 0; |
---|
250 | i = line_end(position()); |
---|
251 | if (i == position() && i < size()) i++; |
---|
252 | cut(position(), i); |
---|
253 | return copy_cuts(); |
---|
254 | case ctrl('N'): |
---|
255 | i = position(); |
---|
256 | if (line_end(i) >= size()) return NORMAL_INPUT_MOVE; |
---|
257 | while (repeat_num--) { |
---|
258 | i = line_end(i); |
---|
259 | if (i >= size()) break; |
---|
260 | i++; |
---|
261 | } |
---|
262 | shift_up_down_position(i); |
---|
263 | return 1; |
---|
264 | case ctrl('P'): |
---|
265 | i = position(); |
---|
266 | if (!line_start(i)) return NORMAL_INPUT_MOVE; |
---|
267 | while(repeat_num--) { |
---|
268 | i = line_start(i); |
---|
269 | if (!i) break; |
---|
270 | i--; |
---|
271 | } |
---|
272 | shift_up_down_position(line_start(i)); |
---|
273 | return 1; |
---|
274 | case ctrl('U'): |
---|
275 | if (readonly()) { |
---|
276 | fl_beep(); |
---|
277 | return 1; |
---|
278 | } |
---|
279 | return cut(0, size()); |
---|
280 | case ctrl('V'): |
---|
281 | case ctrl('Y'): |
---|
282 | if (readonly()) { |
---|
283 | fl_beep(); |
---|
284 | return 1; |
---|
285 | } |
---|
286 | Fl::paste(*this, 1); |
---|
287 | return 1; |
---|
288 | case ctrl('X'): |
---|
289 | case ctrl('W'): |
---|
290 | if (readonly()) { |
---|
291 | fl_beep(); |
---|
292 | return 1; |
---|
293 | } |
---|
294 | copy(1); |
---|
295 | return cut(); |
---|
296 | case ctrl('Z'): |
---|
297 | case ctrl('_'): |
---|
298 | if (readonly()) { |
---|
299 | fl_beep(); |
---|
300 | return 1; |
---|
301 | } |
---|
302 | return undo(); |
---|
303 | case ctrl('I'): |
---|
304 | case ctrl('J'): |
---|
305 | case ctrl('L'): |
---|
306 | case ctrl('M'): |
---|
307 | if (readonly()) { |
---|
308 | fl_beep(); |
---|
309 | return 1; |
---|
310 | } |
---|
311 | // insert a few selected control characters literally: |
---|
312 | if (input_type() != FL_FLOAT_INPUT && input_type() != FL_INT_INPUT) |
---|
313 | return replace(position(), mark(), &ascii, 1); |
---|
314 | } |
---|
315 | |
---|
316 | return 0; |
---|
317 | } |
---|
318 | |
---|
319 | int Fl_Input::handle(int event) { |
---|
320 | static int dnd_save_position, dnd_save_mark, drag_start = -1, newpos; |
---|
321 | static Fl_Widget *dnd_save_focus; |
---|
322 | switch (event) { |
---|
323 | case FL_FOCUS: |
---|
324 | switch (Fl::event_key()) { |
---|
325 | case FL_Right: |
---|
326 | position(0); |
---|
327 | break; |
---|
328 | case FL_Left: |
---|
329 | position(size()); |
---|
330 | break; |
---|
331 | case FL_Down: |
---|
332 | up_down_position(0); |
---|
333 | break; |
---|
334 | case FL_Up: |
---|
335 | up_down_position(line_start(size())); |
---|
336 | break; |
---|
337 | case FL_Tab: |
---|
338 | case 0xfe20: // XK_ISO_Left_Tab |
---|
339 | position(size(),0); |
---|
340 | break; |
---|
341 | default: |
---|
342 | position(position(),mark());// turns off the saved up/down arrow position |
---|
343 | break; |
---|
344 | } |
---|
345 | break; |
---|
346 | |
---|
347 | case FL_KEYBOARD: |
---|
348 | if (Fl::event_key() == FL_Tab && mark() != position()) { |
---|
349 | // Set the current cursor position to the end of the selection... |
---|
350 | if (mark() > position()) |
---|
351 | position(mark()); |
---|
352 | else |
---|
353 | position(position()); |
---|
354 | return (1); |
---|
355 | } else { |
---|
356 | if (active_r() && window() && this == Fl::belowmouse()) |
---|
357 | window()->cursor(FL_CURSOR_NONE); |
---|
358 | return handle_key(); |
---|
359 | } |
---|
360 | |
---|
361 | case FL_PUSH: |
---|
362 | if (Fl::dnd_text_ops()) { |
---|
363 | int oldpos = position(), oldmark = mark(); |
---|
364 | Fl_Boxtype b = box(); |
---|
365 | Fl_Input_::handle_mouse( |
---|
366 | x()+Fl::box_dx(b), y()+Fl::box_dy(b), |
---|
367 | w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0); |
---|
368 | newpos = position(); |
---|
369 | position( oldpos, oldmark ); |
---|
370 | if (Fl::focus()==this && !Fl::event_state(FL_SHIFT) && input_type()!=FL_SECRET_INPUT && |
---|
371 | (newpos >= mark() && newpos < position() || |
---|
372 | newpos >= position() && newpos < mark())) { |
---|
373 | // user clicked in the selection, may be trying to drag |
---|
374 | drag_start = newpos; |
---|
375 | return 1; |
---|
376 | } |
---|
377 | drag_start = -1; |
---|
378 | } |
---|
379 | |
---|
380 | if (Fl::focus() != this) { |
---|
381 | Fl::focus(this); |
---|
382 | handle(FL_FOCUS); |
---|
383 | } |
---|
384 | break; |
---|
385 | |
---|
386 | case FL_DRAG: |
---|
387 | if (Fl::dnd_text_ops()) { |
---|
388 | if (drag_start >= 0) { |
---|
389 | if (Fl::event_is_click()) return 1; // debounce the mouse |
---|
390 | // save the position because sometimes we don't get DND_ENTER: |
---|
391 | dnd_save_position = position(); |
---|
392 | dnd_save_mark = mark(); |
---|
393 | // drag the data: |
---|
394 | copy(0); Fl::dnd(); |
---|
395 | return 1; |
---|
396 | } |
---|
397 | } |
---|
398 | break; |
---|
399 | |
---|
400 | case FL_RELEASE: |
---|
401 | if (Fl::event_button() == 2) { |
---|
402 | Fl::event_is_click(0); // stop double click from picking a word |
---|
403 | Fl::paste(*this, 0); |
---|
404 | } else if (!Fl::event_is_click()) { |
---|
405 | // copy drag-selected text to the clipboard. |
---|
406 | copy(0); |
---|
407 | } else if (Fl::event_is_click() && drag_start >= 0) { |
---|
408 | // user clicked in the field and wants to reset the cursor position... |
---|
409 | position(drag_start, drag_start); |
---|
410 | drag_start = -1; |
---|
411 | } else if (Fl::event_clicks()) { |
---|
412 | // user double or triple clicked to select word or whole text |
---|
413 | copy(0); |
---|
414 | } |
---|
415 | |
---|
416 | // For output widgets, do the callback so the app knows the user |
---|
417 | // did something with the mouse... |
---|
418 | if (readonly()) do_callback(); |
---|
419 | |
---|
420 | return 1; |
---|
421 | |
---|
422 | case FL_DND_ENTER: |
---|
423 | Fl::belowmouse(this); // send the leave events first |
---|
424 | dnd_save_position = position(); |
---|
425 | dnd_save_mark = mark(); |
---|
426 | dnd_save_focus = Fl::focus(); |
---|
427 | if (dnd_save_focus != this) { |
---|
428 | Fl::focus(this); |
---|
429 | handle(FL_FOCUS); |
---|
430 | } |
---|
431 | // fall through: |
---|
432 | case FL_DND_DRAG: |
---|
433 | //int p = mouse_position(X, Y, W, H); |
---|
434 | #if DND_OUT_XXXX |
---|
435 | if (Fl::focus()==this && (p>=dnd_save_position && p<=dnd_save_mark || |
---|
436 | p>=dnd_save_mark && p<=dnd_save_position)) { |
---|
437 | position(dnd_save_position, dnd_save_mark); |
---|
438 | return 0; |
---|
439 | } |
---|
440 | #endif |
---|
441 | { |
---|
442 | Fl_Boxtype b = box(); |
---|
443 | Fl_Input_::handle_mouse( |
---|
444 | x()+Fl::box_dx(b), y()+Fl::box_dy(b), |
---|
445 | w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0); |
---|
446 | } |
---|
447 | return 1; |
---|
448 | |
---|
449 | case FL_DND_LEAVE: |
---|
450 | position(dnd_save_position, dnd_save_mark); |
---|
451 | #if DND_OUT_XXXX |
---|
452 | if (!focused()) |
---|
453 | #endif |
---|
454 | if (dnd_save_focus != this) { |
---|
455 | Fl::focus(dnd_save_focus); |
---|
456 | handle(FL_UNFOCUS); |
---|
457 | } |
---|
458 | return 1; |
---|
459 | |
---|
460 | case FL_DND_RELEASE: |
---|
461 | take_focus(); |
---|
462 | return 1; |
---|
463 | |
---|
464 | } |
---|
465 | Fl_Boxtype b = box(); |
---|
466 | return Fl_Input_::handletext(event, |
---|
467 | x()+Fl::box_dx(b), y()+Fl::box_dy(b), |
---|
468 | w()-Fl::box_dw(b), h()-Fl::box_dh(b)); |
---|
469 | } |
---|
470 | |
---|
471 | Fl_Input::Fl_Input(int X, int Y, int W, int H, const char *l) |
---|
472 | : Fl_Input_(X, Y, W, H, l) { |
---|
473 | } |
---|
474 | |
---|
475 | // |
---|
476 | // End of "$Id$". |
---|
477 | // |
---|