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)  

Fl_Input_.cxx

Go to the documentation of this file.
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 //