|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: Fl_Input_.cxx 8068 2010-12-20 07:48:59Z greg.ercolano $" 00003 // 00004 // Common input widget routines for the Fast Light Tool Kit (FLTK). 00005 // 00006 // Copyright 1998-2010 by Bill Spitzak and others. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems on the following page: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 #include <FL/Fl.H> 00029 #include <FL/Fl_Input_.H> 00030 #include <FL/Fl_Window.H> 00031 #include <FL/fl_draw.H> 00032 #include <FL/fl_ask.H> 00033 #include <math.h> 00034 #include <FL/fl_utf8.h> 00035 #include "flstring.h" 00036 #include <stdlib.h> 00037 #include <ctype.h> 00038 00039 #define MAXBUF 1024 00040 00041 extern void fl_draw(const char*, int, float, float); 00042 00044 00058 const char* Fl_Input_::expand(const char* p, char* buf) const { 00059 char* o = buf; 00060 char* e = buf+(MAXBUF-4); 00061 const char* lastspace = p; 00062 char* lastspace_out = o; 00063 int width_to_lastspace = 0; 00064 int word_count = 0; 00065 int word_wrap; 00066 // const char *pe = p + strlen(p); 00067 00068 if (input_type()==FL_SECRET_INPUT) { 00069 while (o<e && p < value_+size_) { 00070 if (fl_utf8len((char)p[0]) >= 1) *o++ = '*'; 00071 p++; 00072 } 00073 00074 } else while (o<e) { 00075 if (wrap() && (p >= value_+size_ || isspace(*p & 255))) { 00076 word_wrap = w() - Fl::box_dw(box()) - 2; 00077 width_to_lastspace += (int)fl_width(lastspace_out, o-lastspace_out); 00078 if (p > lastspace+1) { 00079 if (word_count && width_to_lastspace > word_wrap) { 00080 p = lastspace; o = lastspace_out; break; 00081 } 00082 word_count++; 00083 } 00084 lastspace = p; 00085 lastspace_out = o; 00086 } 00087 00088 if (p >= value_+size_) break; 00089 int c = *p++ & 255; 00090 if (c < ' ' || c == 127) { 00091 if (c=='\n' && input_type()==FL_MULTILINE_INPUT) {p--; break;} 00092 if (c == '\t' && input_type()==FL_MULTILINE_INPUT) { 00093 for (c = fl_utf_nb_char((uchar*)buf, o-buf)%8; c<8 && o<e; c++) { 00094 *o++ = ' '; 00095 } 00096 } else { 00097 *o++ = '^'; 00098 *o++ = c ^ 0x40; 00099 } 00100 } else { 00101 *o++ = c; 00102 } 00103 } 00104 *o = 0; 00105 return p; 00106 } 00107 00119 double Fl_Input_::expandpos( 00120 const char* p, // real string 00121 const char* e, // pointer into real string 00122 const char* buf, // conversion of real string by expand() 00123 int* returnn // return offset into buf here 00124 ) const { 00125 int n = 0; 00126 int chr = 0; 00127 if (input_type()==FL_SECRET_INPUT) { 00128 while (p<e) { 00129 if (fl_utf8len((char)p[0]) >= 1) n++; 00130 p++; 00131 } 00132 } else while (p<e) { 00133 int c = *p & 255; 00134 if (c < ' ' || c == 127) { 00135 if (c == '\t' && input_type()==FL_MULTILINE_INPUT) { 00136 n += 8-(chr%8); 00137 chr += 7-(chr%8); 00138 } else n += 2; 00139 } else { 00140 n++; 00141 } 00142 chr += fl_utf8len((char)p[0]) >= 1; 00143 p++; 00144 } 00145 if (returnn) *returnn = n; 00146 return fl_width(buf, n); 00147 } 00148 00150 00165 void Fl_Input_::minimal_update(int p) { 00166 if (damage() & FL_DAMAGE_ALL) return; // don't waste time if it won't be done 00167 if (damage() & FL_DAMAGE_EXPOSE) { 00168 if (p < mu_p) mu_p = p; 00169 } else { 00170 mu_p = p; 00171 } 00172 00173 damage(FL_DAMAGE_EXPOSE); 00174 erase_cursor_only = 0; 00175 } 00176 00186 void Fl_Input_::minimal_update(int p, int q) { 00187 if (q < p) p = q; 00188 minimal_update(p); 00189 } 00190 00192 00193 /* Horizontal cursor position in pixels while moving up or down. */ 00194 double Fl_Input_::up_down_pos = 0; 00195 00196 /* Flag to remember last cursor move. */ 00197 int Fl_Input_::was_up_down = 0; 00198 00202 void Fl_Input_::setfont() const { 00203 fl_font(textfont(), textsize()); 00204 } 00205 00215 void Fl_Input_::drawtext(int X, int Y, int W, int H) { 00216 int do_mu = !(damage()&FL_DAMAGE_ALL); 00217 00218 if (Fl::focus()!=this && !size()) { 00219 if (do_mu) { // we have to erase it if cursor was there 00220 draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), 00221 W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); 00222 } 00223 return; 00224 } 00225 00226 int selstart, selend; 00227 if (Fl::focus()!=this && /*Fl::selection_owner()!=this &&*/ Fl::pushed()!=this) 00228 selstart = selend = 0; 00229 else if (position() <= mark()) { 00230 selstart = position(); selend = mark(); 00231 } else { 00232 selend = position(); selstart = mark(); 00233 } 00234 00235 setfont(); 00236 const char *p, *e; 00237 char buf[MAXBUF]; 00238 00239 // count how many lines and put the last one into the buffer: 00240 // And figure out where the cursor is: 00241 int height = fl_height(); 00242 int threshold = height/2; 00243 int lines; 00244 int curx, cury; 00245 for (p=value(), curx=cury=lines=0; ;) { 00246 e = expand(p, buf); 00247 if (position() >= p-value() && position() <= e-value()) { 00248 curx = int(expandpos(p, value()+position(), buf, 0)+.5); 00249 if (Fl::focus()==this && !was_up_down) up_down_pos = curx; 00250 cury = lines*height; 00251 int newscroll = xscroll_; 00252 if (curx > newscroll+W-threshold) { 00253 // figure out scrolling so there is space after the cursor: 00254 newscroll = curx+threshold-W; 00255 // figure out the furthest left we ever want to scroll: 00256 int ex = int(expandpos(p, e, buf, 0))+2-W; 00257 // use minimum of both amounts: 00258 if (ex < newscroll) newscroll = ex; 00259 } else if (curx < newscroll+threshold) { 00260 newscroll = curx-threshold; 00261 } 00262 if (newscroll < 0) newscroll = 0; 00263 if (newscroll != xscroll_) { 00264 xscroll_ = newscroll; 00265 mu_p = 0; erase_cursor_only = 0; 00266 } 00267 } 00268 lines++; 00269 if (e >= value_+size_) break; 00270 p = e+1; 00271 } 00272 00273 // adjust the scrolling: 00274 if (input_type()==FL_MULTILINE_INPUT) { 00275 int newy = yscroll_; 00276 if (cury < newy) newy = cury; 00277 if (cury > newy+H-height) newy = cury-H+height; 00278 if (newy < -1) newy = -1; 00279 if (newy != yscroll_) {yscroll_ = newy; mu_p = 0; erase_cursor_only = 0;} 00280 } else { 00281 yscroll_ = -(H-height)/2; 00282 } 00283 00284 fl_push_clip(X, Y, W, H); 00285 Fl_Color tc = active_r() ? textcolor() : fl_inactive(textcolor()); 00286 00287 p = value(); 00288 // visit each line and draw it: 00289 int desc = height-fl_descent(); 00290 float xpos = (float)(X - xscroll_ + 1); 00291 int ypos = -yscroll_; 00292 for (; ypos < H;) { 00293 00294 // re-expand line unless it is the last one calculated above: 00295 if (lines>1) e = expand(p, buf); 00296 00297 if (ypos <= -height) goto CONTINUE; // clipped off top 00298 00299 if (do_mu) { // for minimal update: 00300 const char* pp = value()+mu_p; // pointer to where minimal update starts 00301 if (e < pp) goto CONTINUE2; // this line is before the changes 00302 if (readonly()) erase_cursor_only = 0; // this isn't the most efficient way 00303 if (erase_cursor_only && p > pp) goto CONTINUE2; // this line is after 00304 // calculate area to erase: 00305 float r = (float)(X+W); 00306 float xx; 00307 if (p >= pp) { 00308 xx = (float)X; 00309 if (erase_cursor_only) r = xpos+2; 00310 else if (readonly()) xx -= 3; 00311 } else { 00312 xx = xpos + (float)expandpos(p, pp, buf, 0); 00313 if (erase_cursor_only) r = xx+2; 00314 else if (readonly()) xx -= 3; 00315 } 00316 // clip to and erase it: 00317 fl_push_clip((int)xx-1-height/8, Y+ypos, (int)(r-xx+2+height/4), height); 00318 draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), 00319 W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); 00320 // it now draws entire line over it 00321 // this should not draw letters to left of erased area, but 00322 // that is nyi. 00323 } 00324 00325 // Draw selection area if required: 00326 if (selstart < selend && selstart <= e-value() && selend > p-value()) { 00327 const char* pp = value()+selstart; 00328 float x1 = xpos; 00329 int offset1 = 0; 00330 if (pp > p) { 00331 fl_color(tc); 00332 x1 += (float)expandpos(p, pp, buf, &offset1); 00333 fl_draw(buf, offset1, xpos, (float)(Y+ypos+desc)); 00334 } 00335 pp = value()+selend; 00336 float x2 = (float)(X+W); 00337 int offset2; 00338 if (pp <= e) x2 = xpos + (float)expandpos(p, pp, buf, &offset2); 00339 else offset2 = strlen(buf); 00340 fl_color(selection_color()); 00341 fl_rectf((int)(x1+0.5), Y+ypos, (int)(x2-x1+0.5), height); 00342 fl_color(fl_contrast(textcolor(), selection_color())); 00343 fl_draw(buf+offset1, offset2-offset1, x1, (float)(Y+ypos+desc)); 00344 if (pp < e) { 00345 fl_color(tc); 00346 fl_draw(buf+offset2, strlen(buf+offset2), x2, (float)(Y+ypos+desc)); 00347 } 00348 } else { 00349 // draw unselected text 00350 fl_color(tc); 00351 fl_draw(buf, strlen(buf), xpos, (float)(Y+ypos+desc)); 00352 } 00353 00354 if (do_mu) fl_pop_clip(); 00355 00356 CONTINUE2: 00357 // draw the cursor: 00358 if (Fl::focus() == this && selstart == selend && 00359 position() >= p-value() && position() <= e-value()) { 00360 fl_color(cursor_color()); 00361 // cursor position may need to be recomputed (see STR #2486) 00362 curx = int(expandpos(p, value()+position(), buf, 0)+.5); 00363 if (readonly()) { 00364 fl_line((int)(xpos+curx-2.5f), Y+ypos+height-1, 00365 (int)(xpos+curx+0.5f), Y+ypos+height-4, 00366 (int)(xpos+curx+3.5f), Y+ypos+height-1); 00367 } else { 00368 fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height); 00369 } 00370 } 00371 00372 CONTINUE: 00373 ypos += height; 00374 if (e >= value_+size_) break; 00375 if (*e == '\n' || *e == ' ') e++; 00376 p = e; 00377 } 00378 00379 // for minimal update, erase all lines below last one if necessary: 00380 if (input_type()==FL_MULTILINE_INPUT && do_mu && ypos<H 00381 && (!erase_cursor_only || p <= value()+mu_p)) { 00382 if (ypos < 0) ypos = 0; 00383 fl_push_clip(X, Y+ypos, W, H-ypos); 00384 draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), 00385 W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); 00386 fl_pop_clip(); 00387 } 00388 00389 fl_pop_clip(); 00390 if (Fl::focus() == this) { 00391 fl_set_spot(textfont(), textsize(), 00392 (int)xpos+curx, Y+ypos-fl_descent(), W, H, window()); 00393 } 00394 } 00395 00400 static int isword(char c) { 00401 return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c)); 00402 } 00403 00414 int Fl_Input_::word_end(int i) const { 00415 if (input_type() == FL_SECRET_INPUT) return size(); 00416 //while (i < size() && !isword(index(i))) i++; 00417 while (i < size() && !isword(index(i))) i++; 00418 while (i < size() && isword(index(i))) i++; 00419 return i; 00420 } 00421 00432 int Fl_Input_::word_start(int i) const { 00433 if (input_type() == FL_SECRET_INPUT) return 0; 00434 // if (i >= size() || !isword(index(i))) 00435 // while (i > 0 && !isword(index(i-1))) i--; 00436 while (i > 0 && !isword(index(i-1))) i--; 00437 while (i > 0 && isword(index(i-1))) i--; 00438 return i; 00439 } 00440 00450 int Fl_Input_::line_end(int i) const { 00451 if (input_type() != FL_MULTILINE_INPUT) return size(); 00452 00453 if (wrap()) { 00454 // go to the start of the paragraph: 00455 int j = i; 00456 while (j > 0 && index(j-1) != '\n') j--; 00457 // now measure lines until we get past i, end of that line is real eol: 00458 setfont(); 00459 for (const char* p=value()+j; ;) { 00460 char buf[MAXBUF]; 00461 p = expand(p, buf); 00462 if (p-value() >= i) return p-value(); 00463 p++; 00464 } 00465 } else { 00466 while (i < size() && index(i) != '\n') i++; 00467 return i; 00468 } 00469 } 00470 00480 int Fl_Input_::line_start(int i) const { 00481 if (input_type() != FL_MULTILINE_INPUT) return 0; 00482 int j = i; 00483 while (j > 0 && index(j-1) != '\n') j--; 00484 if (wrap()) { 00485 // now measure lines until we get past i, start of that line is real eol: 00486 setfont(); 00487 for (const char* p=value()+j; ;) { 00488 char buf[MAXBUF]; 00489 const char* e = expand(p, buf); 00490 if (e-value() >= i) return p-value(); 00491 p = e+1; 00492 } 00493 } else return j; 00494 } 00495 00500 void Fl_Input_::handle_mouse(int X, int Y, int /*W*/, int /*H*/, int drag) { 00501 was_up_down = 0; 00502 if (!size()) return; 00503 setfont(); 00504 00505 const char *p, *e; 00506 char buf[MAXBUF]; 00507 00508 int theline = (input_type()==FL_MULTILINE_INPUT) ? 00509 (Fl::event_y()-Y+yscroll_)/fl_height() : 0; 00510 00511 int newpos = 0; 00512 for (p=value();; ) { 00513 e = expand(p, buf); 00514 theline--; if (theline < 0) break; 00515 if (e >= value_+size_) break; 00516 p = e+1; 00517 } 00518 const char *l, *r, *t; double f0 = Fl::event_x()-X+xscroll_; 00519 for (l = p, r = e; l<r; ) { 00520 double f; 00521 int cw = fl_utf8len((char)l[0]); 00522 if (cw < 1) cw = 1; 00523 t = l+cw; 00524 f = X-xscroll_+expandpos(p, t, buf, 0); 00525 if (f <= Fl::event_x()) {l = t; f0 = Fl::event_x()-f;} 00526 else r = t-cw; 00527 } 00528 if (l < e) { // see if closer to character on right: 00529 double f1; 00530 int cw = fl_utf8len((char)l[0]); 00531 if (cw > 0) { 00532 f1 = X-xscroll_+expandpos(p, l + cw, buf, 0) - Fl::event_x(); 00533 if (f1 < f0) l = l+cw; 00534 } 00535 } 00536 newpos = l-value(); 00537 00538 int newmark = drag ? mark() : newpos; 00539 if (Fl::event_clicks()) { 00540 if (newpos >= newmark) { 00541 if (newpos == newmark) { 00542 if (newpos < size()) newpos++; 00543 else newmark--; 00544 } 00545 if (Fl::event_clicks() > 1) { 00546 newpos = line_end(newpos); 00547 newmark = line_start(newmark); 00548 } else { 00549 newpos = word_end(newpos); 00550 newmark = word_start(newmark); 00551 } 00552 } else { 00553 if (Fl::event_clicks() > 1) { 00554 newpos = line_start(newpos); 00555 newmark = line_end(newmark); 00556 } else { 00557 newpos = word_start(newpos); 00558 newmark = word_end(newmark); 00559 } 00560 } 00561 // if the multiple click does not increase the selection, revert 00562 // to single-click behavior: 00563 if (!drag && (mark() > position() ? 00564 (newmark >= position() && newpos <= mark()) : 00565 (newmark >= mark() && newpos <= position()))) { 00566 Fl::event_clicks(0); 00567 newmark = newpos = l-value(); 00568 } 00569 } 00570 position(newpos, newmark); 00571 } 00572 00590 int Fl_Input_::position(int p, int m) { 00591 int is_same = 0; 00592 was_up_down = 0; 00593 if (p<0) p = 0; 00594 if (p>size()) p = size(); 00595 if (m<0) m = 0; 00596 if (m>size()) m = size(); 00597 if (p == m) is_same = 1; 00598 00599 while (p < position_ && p > 0 && (size() - p) > 0 && 00600 (fl_utf8len((char)(value() + p)[0]) < 1)) { p--; } 00601 int ul = fl_utf8len((char)(value() + p)[0]); 00602 while (p < size() && p > position_ && ul < 0) { 00603 p++; 00604 ul = fl_utf8len((char)(value() + p)[0]); 00605 } 00606 00607 while (m < mark_ && m > 0 && (size() - m) > 0 && 00608 (fl_utf8len((char)(value() + m)[0]) < 1)) { m--; } 00609 ul = fl_utf8len((char)(value() + m)[0]); 00610 while (m < size() && m > mark_ && ul < 0) { 00611 m++; 00612 ul = fl_utf8len((char)(value() + m)[0]); 00613 } 00614 if (is_same) m = p; 00615 if (p == position_ && m == mark_) return 0; 00616 00617 00618 //if (Fl::selection_owner() == this) Fl::selection_owner(0); 00619 if (p != m) { 00620 if (p != position_) minimal_update(position_, p); 00621 if (m != mark_) minimal_update(mark_, m); 00622 } else { 00623 // new position is a cursor 00624 if (position_ == mark_) { 00625 // old position was just a cursor 00626 if (Fl::focus() == this && !(damage()&FL_DAMAGE_EXPOSE)) { 00627 minimal_update(position_); erase_cursor_only = 1; 00628 } 00629 } else { // old position was a selection 00630 minimal_update(position_, mark_); 00631 } 00632 } 00633 position_ = p; 00634 mark_ = m; 00635 return 1; 00636 } 00637 00650 int Fl_Input_::up_down_position(int i, int keepmark) { 00651 // unlike before, i must be at the start of the line already! 00652 00653 setfont(); 00654 char buf[MAXBUF]; 00655 const char* p = value()+i; 00656 const char* e = expand(p, buf); 00657 const char *l, *r, *t; 00658 for (l = p, r = e; l<r; ) { 00659 t = l+(r-l+1)/2; 00660 int f = (int)expandpos(p, t, buf, 0); 00661 if (f <= up_down_pos) l = t; else r = t-1; 00662 } 00663 int j = l-value(); 00664 j = position(j, keepmark ? mark_ : j); 00665 was_up_down = 1; 00666 return j; 00667 } 00668 00682 int Fl_Input_::copy(int clipboard) { 00683 int b = position(); 00684 int e = mark(); 00685 if (b != e) { 00686 if (b > e) {b = mark(); e = position();} 00687 if (input_type() == FL_SECRET_INPUT) e = b; 00688 Fl::copy(value()+b, e-b, clipboard); 00689 return 1; 00690 } 00691 return 0; 00692 } 00693 00694 #define MAXFLOATSIZE 40 00695 00696 static char* undobuffer; 00697 static int undobufferlength; 00698 static Fl_Input_* undowidget; 00699 static int undoat; // points after insertion 00700 static int undocut; // number of characters deleted there 00701 static int undoinsert; // number of characters inserted 00702 static int yankcut; // length of valid contents of buffer, even if undocut=0 00703 00704 static void undobuffersize(int n) { 00705 if (n > undobufferlength) { 00706 if (undobuffer) { 00707 do {undobufferlength *= 2;} while (undobufferlength < n); 00708 undobuffer = (char*)realloc(undobuffer, undobufferlength); 00709 } else { 00710 undobufferlength = n+9; 00711 undobuffer = (char*)malloc(undobufferlength); 00712 } 00713 } 00714 } 00715 00745 int Fl_Input_::replace(int b, int e, const char* text, int ilen) { 00746 int ul, om, op; 00747 was_up_down = 0; 00748 00749 if (b<0) b = 0; 00750 if (e<0) e = 0; 00751 if (b>size_) b = size_; 00752 if (e>size_) e = size_; 00753 if (e<b) {int t=b; b=e; e=t;} 00754 while (b != e && b > 0 && (size_ - b) > 0 && 00755 (fl_utf8len((value_ + b)[0]) < 1)) { b--; } 00756 ul = fl_utf8len((char)(value_ + e)[0]); 00757 while (e < size_ && e > 0 && ul < 0) { 00758 e++; 00759 ul = fl_utf8len((char)(value_ + e)[0]); 00760 } 00761 if (text && !ilen) ilen = strlen(text); 00762 if (e<=b && !ilen) return 0; // don't clobber undo for a null operation 00763 if (size_+ilen-(e-b) > maximum_size_) { 00764 ilen = maximum_size_-size_+(e-b); 00765 if (ilen < 0) ilen = 0; 00766 } 00767 00768 put_in_buffer(size_+ilen); 00769 00770 if (e>b) { 00771 if (undowidget == this && b == undoat) { 00772 undobuffersize(undocut+(e-b)); 00773 memcpy(undobuffer+undocut, value_+b, e-b); 00774 undocut += e-b; 00775 } else if (undowidget == this && e == undoat && !undoinsert) { 00776 undobuffersize(undocut+(e-b)); 00777 memmove(undobuffer+(e-b), undobuffer, undocut); 00778 memcpy(undobuffer, value_+b, e-b); 00779 undocut += e-b; 00780 } else if (undowidget == this && e == undoat && (e-b)<undoinsert) { 00781 undoinsert -= e-b; 00782 } else { 00783 undobuffersize(e-b); 00784 memcpy(undobuffer, value_+b, e-b); 00785 undocut = e-b; 00786 undoinsert = 0; 00787 } 00788 memmove(buffer+b, buffer+e, size_-e+1); 00789 size_ -= e-b; 00790 undowidget = this; 00791 undoat = b; 00792 if (input_type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut; 00793 } 00794 00795 if (ilen) { 00796 if (undowidget == this && b == undoat) 00797 undoinsert += ilen; 00798 else { 00799 undocut = 0; 00800 undoinsert = ilen; 00801 } 00802 memmove(buffer+b+ilen, buffer+b, size_-b+1); 00803 memcpy(buffer+b, text, ilen); 00804 size_ += ilen; 00805 } 00806 undowidget = this; 00807 om = mark_; 00808 op = position_; 00809 mark_ = position_ = undoat = b+ilen; 00810 00811 // Insertions into the word at the end of the line will cause it to 00812 // wrap to the next line, so we must indicate that the changes may start 00813 // right after the whitespace before the current word. This will 00814 // result in sub-optimal update when such wrapping does not happen 00815 // but it is too hard to figure out for now... 00816 if (wrap()) { 00817 // if there is a space in the pasted text, the whole line may have rewrapped 00818 int i; 00819 for (i=0; i<ilen; i++) 00820 if (text[i]==' ') break; 00821 if (i==ilen) 00822 while (b > 0 && !isspace(index(b) & 255) && index(b)!='\n') b--; 00823 else 00824 while (b > 0 && index(b)!='\n') b--; 00825 } 00826 00827 // make sure we redraw the old selection or cursor: 00828 if (om < b) b = om; 00829 if (op < b) b = op; 00830 00831 minimal_update(b); 00832 00833 mark_ = position_ = undoat; 00834 00835 set_changed(); 00836 if (when()&FL_WHEN_CHANGED) do_callback(); 00837 return 1; 00838 } 00839 00847 int Fl_Input_::undo() { 00848 was_up_down = 0; 00849 if ( undowidget != this || (!undocut && !undoinsert) ) return 0; 00850 00851 int ilen = undocut; 00852 int xlen = undoinsert; 00853 int b = undoat-xlen; 00854 int b1 = b; 00855 00856 put_in_buffer(size_+ilen); 00857 00858 if (ilen) { 00859 memmove(buffer+b+ilen, buffer+b, size_-b+1); 00860 memcpy(buffer+b, undobuffer, ilen); 00861 size_ += ilen; 00862 b += ilen; 00863 } 00864 00865 if (xlen) { 00866 undobuffersize(xlen); 00867 memcpy(undobuffer, buffer+b, xlen); 00868 memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1); 00869 size_ -= xlen; 00870 } 00871 00872 undocut = xlen; 00873 if (xlen) yankcut = xlen; 00874 undoinsert = ilen; 00875 undoat = b; 00876 mark_ = b /* -ilen */; 00877 position_ = b; 00878 00879 if (wrap()) 00880 while (b1 > 0 && index(b1)!='\n') b1--; 00881 minimal_update(b1); 00882 set_changed(); 00883 if (when()&FL_WHEN_CHANGED) do_callback(); 00884 return 1; 00885 } 00886 00897 int Fl_Input_::copy_cuts() { 00898 // put the yank buffer into the X clipboard 00899 if (!yankcut || input_type()==FL_SECRET_INPUT) return 0; 00900 Fl::copy(undobuffer, yankcut, 1); 00901 return 1; 00902 } 00903 00907 void Fl_Input_::maybe_do_callback() { 00908 if (changed() || (when()&FL_WHEN_NOT_CHANGED)) { 00909 do_callback(); 00910 } 00911 } 00912 00919 int Fl_Input_::handletext(int event, int X, int Y, int W, int H) { 00920 switch (event) { 00921 00922 case FL_ENTER: 00923 case FL_MOVE: 00924 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT); 00925 return 1; 00926 00927 case FL_LEAVE: 00928 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT); 00929 return 1; 00930 00931 case FL_FOCUS: 00932 fl_set_spot(textfont(), textsize(), x(), y(), w(), h(), window()); 00933 if (mark_ == position_) { 00934 minimal_update(size()+1); 00935 } else //if (Fl::selection_owner() != this) 00936 minimal_update(mark_, position_); 00937 return 1; 00938 00939 case FL_UNFOCUS: 00940 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT); 00941 if (mark_ == position_) { 00942 if (!(damage()&FL_DAMAGE_EXPOSE)) {minimal_update(position_); erase_cursor_only = 1;} 00943 } else //if (Fl::selection_owner() != this) 00944 minimal_update(mark_, position_); 00945 case FL_HIDE: 00946 fl_reset_spot(); 00947 if (!readonly() && (when() & FL_WHEN_RELEASE)) 00948 maybe_do_callback(); 00949 return 1; 00950 00951 case FL_PUSH: 00952 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT); 00953 00954 handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT)); 00955 00956 if (Fl::focus() != this) { 00957 Fl::focus(this); 00958 handle(FL_FOCUS); 00959 } 00960 return 1; 00961 00962 case FL_DRAG: 00963 handle_mouse(X, Y, W, H, 1); 00964 return 1; 00965 00966 case FL_RELEASE: 00967 copy(0); 00968 return 1; 00969 00970 case FL_PASTE: { 00971 // Don't allow pastes into readonly widgets... 00972 if (readonly()) { 00973 fl_beep(FL_BEEP_ERROR); 00974 return 1; 00975 } 00976 00977 // See if we have anything to paste... 00978 if (!Fl::event_text() || !Fl::event_length()) return 1; 00979 00980 // strip trailing control characters and spaces before pasting: 00981 const char* t = Fl::event_text(); 00982 const char* e = t+Fl::event_length(); 00983 if (input_type() != FL_MULTILINE_INPUT) while (e > t && isspace(*(e-1) & 255)) e--; 00984 if (!t || e <= t) return 1; // Int/float stuff will crash without this test 00985 if (input_type() == FL_INT_INPUT) { 00986 while (isspace(*t & 255) && t < e) t ++; 00987 const char *p = t; 00988 if (*p == '+' || *p == '-') p ++; 00989 if (strncmp(p, "0x", 2) == 0) { 00990 p += 2; 00991 while (isxdigit(*p & 255) && p < e) p ++; 00992 } else { 00993 while (isdigit(*p & 255) && p < e) p ++; 00994 } 00995 if (p < e) { 00996 fl_beep(FL_BEEP_ERROR); 00997 return 1; 00998 } else return replace(0, size(), t, e - t); 00999 } else if (input_type() == FL_FLOAT_INPUT) { 01000 while (isspace(*t & 255) && t < e) t ++; 01001 const char *p = t; 01002 if (*p == '+' || *p == '-') p ++; 01003 while (isdigit(*p & 255) && p < e) p ++; 01004 if (*p == '.') { 01005 p ++; 01006 while (isdigit(*p & 255) && p < e) p ++; 01007 if (*p == 'e' || *p == 'E') { 01008 p ++; 01009 if (*p == '+' || *p == '-') p ++; 01010 while (isdigit(*p & 255) && p < e) p ++; 01011 } 01012 } 01013 if (p < e) { 01014 fl_beep(FL_BEEP_ERROR); 01015 return 1; 01016 } else return replace(0, size(), t, e - t); 01017 } 01018 return replace(position(), mark(), t, e-t);} 01019 01020 case FL_SHORTCUT: 01021 if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut())) 01022 return 0; 01023 if (Fl::visible_focus() && handle(FL_FOCUS)) { 01024 Fl::focus(this); 01025 return 1; 01026 } // else fall through 01027 01028 default: 01029 return 0; 01030 } 01031 } 01032 01033 /*------------------------------*/ 01034 01045 Fl_Input_::Fl_Input_(int X, int Y, int W, int H, const char* l) 01046 : Fl_Widget(X, Y, W, H, l) { 01047 box(FL_DOWN_BOX); 01048 color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR); 01049 align(FL_ALIGN_LEFT); 01050 textsize_ = FL_NORMAL_SIZE; 01051 textfont_ = FL_HELVETICA; 01052 textcolor_ = FL_FOREGROUND_COLOR; 01053 cursor_color_ = FL_FOREGROUND_COLOR; // was FL_BLUE 01054 mark_ = position_ = size_ = 0; 01055 bufsize = 0; 01056 buffer = 0; 01057 value_ = ""; 01058 xscroll_ = yscroll_ = 0; 01059 maximum_size_ = 32767; 01060 shortcut_ = 0; 01061 set_flag(SHORTCUT_LABEL); 01062 tab_nav(1); 01063 } 01064 01070 void Fl_Input_::put_in_buffer(int len) { 01071 if (value_ == buffer && bufsize > len) { 01072 buffer[size_] = 0; 01073 return; 01074 } 01075 if (!bufsize) { 01076 if (len > size_) len += 9; // let a few characters insert before realloc 01077 bufsize = len+1; 01078 buffer = (char*)malloc(bufsize); 01079 } else if (bufsize <= len) { 01080 // we may need to move old value in case it points into buffer: 01081 int moveit = (value_ >= buffer && value_ < buffer+bufsize); 01082 // enlarge current buffer 01083 if (len > size_) { 01084 do {bufsize *= 2;} while (bufsize <= len); 01085 } else { 01086 bufsize = len+1; 01087 } 01088 // Note: the following code is equivalent to: 01089 // 01090 // if (moveit) value_ = value_ - buffer; 01091 // char* nbuffer = (char*)realloc(buffer, bufsize); 01092 // if (moveit) value_ = value_ + nbuffer; 01093 // buffer = nbuffer; 01094 // 01095 // We just optimized the pointer arithmetic for value_... 01096 // 01097 char* nbuffer = (char*)realloc(buffer, bufsize); 01098 if (moveit) value_ += (nbuffer-buffer); 01099 buffer = nbuffer; 01100 } 01101 memmove(buffer, value_, size_); buffer[size_] = 0; 01102 value_ = buffer; 01103 } 01104 01123 int Fl_Input_::static_value(const char* str, int len) { 01124 clear_changed(); 01125 if (undowidget == this) undowidget = 0; 01126 if (str == value_ && len == size_) return 0; 01127 if (len) { // non-empty new value: 01128 if (xscroll_ || yscroll_) { 01129 xscroll_ = yscroll_ = 0; 01130 minimal_update(0); 01131 } else { 01132 int i = 0; 01133 // find first different character: 01134 if (value_) { 01135 for (; i<size_ && i<len && str[i]==value_[i]; i++); 01136 if (i==size_ && i==len) return 0; 01137 } 01138 minimal_update(i); 01139 } 01140 value_ = str; 01141 size_ = len; 01142 } else { // empty new value: 01143 if (!size_) return 0; // both old and new are empty. 01144 size_ = 0; 01145 value_ = ""; 01146 xscroll_ = yscroll_ = 0; 01147 minimal_update(0); 01148 } 01149 position(readonly() ? 0 : size()); 01150 return 1; 01151 } 01152 01167 int Fl_Input_::static_value(const char* str) { 01168 return static_value(str, str ? strlen(str) : 0); 01169 } 01170 01186 int Fl_Input_::value(const char* str, int len) { 01187 int r = static_value(str, len); 01188 if (len) put_in_buffer(len); 01189 return r; 01190 } 01191 01203 int Fl_Input_::value(const char* str) { 01204 return value(str, str ? strlen(str) : 0); 01205 } 01206 01213 void Fl_Input_::resize(int X, int Y, int W, int H) { 01214 if (W != w()) xscroll_ = 0; 01215 if (H != h()) yscroll_ = 0; 01216 Fl_Widget::resize(X, Y, W, H); 01217 } 01218 01225 Fl_Input_::~Fl_Input_() { 01226 if (undowidget == this) undowidget = 0; 01227 if (bufsize) free((void*)buffer); 01228 } 01229 01234 int Fl_Input_::linesPerPage() { 01235 int n = 1; 01236 if (input_type() == FL_MULTILINE_INPUT) { 01237 fl_font(textfont(),textsize()); //ensure current font is set to ours 01238 n = h()/fl_height(); // number of lines to scroll 01239 if (n<=0) n = 1; 01240 } 01241 return n; 01242 } 01243 01253 unsigned int Fl_Input_::index(int i) const 01254 { 01255 int len = 0; 01256 return fl_utf8decode(value_+i, value_+size_, &len); 01257 } 01258 01259 // 01260 // End of "$Id: Fl_Input_.cxx 8068 2010-12-20 07:48:59Z greg.ercolano $". 01261 //