Back to index

texmacs  1.0.7.15
input_widget.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : input_widget.cpp
00004 * DESCRIPTION: Input of data by the user in textual form
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "analyze.hpp"
00013 #include "font.hpp"
00014 #include "file.hpp"
00015 #include "window.hpp"
00016 #include "Widkit/attribute_widget.hpp"
00017 #include "Widkit/layout.hpp"
00018 #include "scheme.hpp"
00019 
00020 #ifdef OS_WIN32
00021 #define URL_CONCATER  '\\'
00022 #else
00023 #define URL_CONCATER  '/'
00024 #endif
00025 
00026 /******************************************************************************
00027 * Input widgets
00028 ******************************************************************************/
00029 
00030 class input_widget_rep: public attribute_widget_rep {
00031   string  s;           // the string being entered
00032   string  draw_s;      // the string being displayed
00033   SI      text_h;      // text height
00034   string  type;        // expected type of string
00035   array<string> def;   // default possible input values
00036   command call_back;   // routine called on <return> or <escape>
00037   int     style;       // style of widget
00038   bool    greyed;      // greyed input
00039   string  width;       // width of input field
00040   bool    persistent;  // don't complete after loss of focus
00041   bool    ok;          // input not canceled
00042   bool    done;        // call back has been called
00043   int     def_cur;     // current choice between default possible values
00044   SI      dw, dh;      // border width and height
00045   int     pos;         // cursor position
00046   SI      scroll;      // how much scrolled to the left
00047   bool    got_focus;   // got keyboard focus
00048   bool    hilit;       // hilit on keyboard focus
00049   array<string> tabs;  // tab completions
00050   int     tab_nr;      // currently visible tab-completion
00051   int     tab_pos;     // cursor position where tab was pressed
00052 
00053 public:
00054   input_widget_rep (command call_back, int style, string width, bool persist);
00055   operator tree ();
00056   void update_draw_s ();
00057   void commit ();
00058   void cancel ();
00059 
00060   void handle_get_size (get_size_event ev);
00061   void handle_repaint (repaint_event ev);
00062   void handle_keypress (keypress_event ev);
00063   void handle_mouse (mouse_event ev);
00064   void handle_keyboard_focus (keyboard_focus_event ev);
00065 
00066   void handle_set_string (set_string_event ev);
00067   void handle_get_string (get_string_event ev);
00068 };
00069 
00070 /******************************************************************************
00071 * Routines for input_widgets
00072 ******************************************************************************/
00073 
00074 #define SHRINK 3
00075 
00076 input_widget_rep::input_widget_rep (command cb2, int st2, string w2, bool p2):
00077   attribute_widget_rep (south_west),
00078   s (""), draw_s (""), type ("default"), def (),
00079   call_back (cb2), style (st2),
00080   greyed ((style & WIDGET_STYLE_INERT) != 0),
00081   width (w2), persistent (p2),
00082   ok (true), done (false), def_cur (0),
00083   dw (4*PIXEL), dh (2*PIXEL), pos (N(s)), scroll (0),
00084   got_focus (false), hilit (false)
00085 {
00086   if ((style & WIDGET_STYLE_MINI) != 0) dh= 1.5 * PIXEL;
00087   dw *= SHRINK;
00088   dh *= SHRINK;
00089   font fn= get_default_styled_font (style);
00090   text_h = (fn->y2- fn->y1+ 2*dh+ (SHRINK-1))/SHRINK;
00091 }
00092 
00093 input_widget_rep::operator tree () {
00094   return tree (TUPLE, "input", s);
00095 }
00096 
00097 void
00098 input_widget_rep::update_draw_s () {
00099   draw_s= s;
00100   if (type == "password") {
00101     draw_s= copy (s);
00102     for (int i=0; i<N(s); i++)
00103       draw_s[i]= '*';
00104   }
00105 }
00106 
00107 void
00108 input_widget_rep::commit () {
00109   ok= true;
00110   done= true;
00111   call_back (list_object (object (s)));
00112 }
00113 
00114 void
00115 input_widget_rep::cancel () {
00116   ok= false;
00117   done= true;
00118   call_back (list_object (object (false)));
00119 }
00120 
00121 void
00122 input_widget_rep::handle_get_size (get_size_event ev) {
00123   SI ww = decode_length (width, this, style);
00124   SI dww= (SI) ((2*dw) / SHRINK);
00125   if (ends (width, "w") && is_double (width (0, N(width) - 1))) {
00126     font fn= get_default_styled_font (style);
00127     if (ev->mode == -1) ev->w= 0;
00128     else if (ev->mode == 0);
00129     else if (ev->mode == 1) ev->w= ww;
00130     ev->w= max (ev->w, (4*fn->wquad) / SHRINK + dww);
00131   }
00132   else if (ends (width, "em") && is_double (width (0, N(width) - 2)))
00133     ev->w= ww + dww;
00134   else if (ends (width, "px") && is_double (width (0, N(width) - 2)))
00135     ev->w= ww + dww;
00136   else if (ev->mode == 1)
00137     ev->w= ww;
00138   ev->h= text_h;
00139   abs_round (ev->w, ev->h);
00140 }
00141 
00142 void
00143 input_widget_rep::handle_repaint (repaint_event ev) { (void) ev;
00144   renderer ren= win->get_renderer ();
00145   update_draw_s (); 
00146   SI ecart= max (0, (h - (2*dh / SHRINK) - text_h) >> 1);
00147 
00148   metric ex;
00149   font fn= get_default_styled_font (style);
00150   fn->var_get_extents (draw_s, ex);
00151   SI left= ex->x1, bottom= fn->y1, right= ex->x2;
00152   fn->var_get_extents (draw_s (0, pos), ex);
00153   SI current= ex->x2- ex->x1;
00154 
00155   SI text_width= right-left;
00156   SI width= w*SHRINK- 2*dw, height= h*SHRINK;
00157   SI marge= width>>2;
00158   if ((current-scroll) > (width-marge)) scroll= current+ marge- width;
00159   if ((current-scroll) < marge) scroll= current- marge;
00160   if (scroll > (text_width- width)) scroll= text_width- width;
00161   if (scroll < 0) scroll= 0;
00162   left    += scroll;
00163   current -= scroll;
00164 
00165   layout_default (ren, 0, 0, w, h);
00166   if (true) {
00167     SI yy= ((ecart + PIXEL/2) / PIXEL) * PIXEL;
00168     SI hh= ((h - 2*ecart + PIXEL/2) / PIXEL) * PIXEL;
00169     if (yy + hh + 2*PIXEL <= h) hh += 2 * PIXEL;
00170     else if (yy + hh + PIXEL <= h) hh += PIXEL;
00171     if (greyed) layout_default (ren, 0, yy, w, hh);
00172     else layout_pastel (ren, 0, yy, w, hh);
00173     layout_lower (ren, 0, yy, w, hh);
00174   }
00175   else if (got_focus && hilit) {
00176     layout_dark (ren, 0, 0, w, h);
00177     layout_lower (ren, 0, 0, w, h);
00178   }
00179 
00180   ren->set_color (black);
00181   if (greyed) ren->set_color (dark_grey);
00182   ren->set_shrinking_factor (SHRINK);
00183   ecart *= SHRINK;
00184   fn->var_draw (ren, draw_s, dw - left, dh - bottom + ecart);
00185   if (got_focus) {
00186     SI pixel= SHRINK*PIXEL;
00187     ren->set_color (red);
00188     ren->line (current + dw, dh + ecart,
00189               current + dw, height - pixel - dh - ecart);
00190     ren->line (current + dw - pixel, dh + ecart,
00191               current + dw + pixel, dh + ecart);
00192     ren->line (current + dw - pixel, height - pixel - dh - ecart,
00193               current + dw + pixel, height - pixel - dh - ecart);
00194   }
00195   ren->set_shrinking_factor (1);
00196 }
00197 
00198 void
00199 input_widget_rep::handle_keypress (keypress_event ev) {
00200   if (greyed) return;
00201   string key= ev->key;
00202   while ((N(key) >= 5) && (key(0,3) == "Mod") && (key[4] == '-') &&
00203         (key[3] >= '1') && (key[3] <= '5')) key= key (5, N(key));
00204   if (key == "space") key= " ";
00205   if (key == "<") key= "<less>";
00206   if (key == ">") key= "<gtr>";
00207 
00208   /* tab-completion */
00209   if ((key == "tab" || key == "S-tab") && N(tabs) != 0) {
00210     int d = (key == "tab"? 1: N(tabs)-1);
00211     tab_nr= (tab_nr + d) % N(tabs);
00212     s     = s (0, tab_pos) * tabs[tab_nr];
00213     pos   = N(s);
00214     this << emit_invalidate_all ();
00215     return;
00216   }
00217   else if (key == "tab" || key == "S-tab") {
00218     if (pos != N(s)) return;
00219     tabs= copy (def);
00220     if (ends (type, "file") || type == "directory") {
00221       url search= url_here ();
00222       url dir= (ends (s, string (URL_CONCATER))? url (s): head (url (s)));
00223       if (type == "smart-file") search= url ("$TEXMACS_FILE_PATH");
00224       if (is_rooted (dir)) search= url_here ();
00225       if (is_none (dir)) dir= url_here ();
00226       tabs= file_completions (search, dir);
00227     }
00228     tabs= strip_completions (tabs, s);
00229     tabs= close_completions (tabs);
00230     if (N (tabs) == 0);
00231     else if (N (tabs) == 1) {
00232       s   = s * tabs[0];
00233       pos = N(s);
00234       tabs= array<string> (0);
00235     }
00236     else {
00237       tab_nr = 0;
00238       tab_pos= N(s);
00239       s      = s * tabs[0];
00240       pos    = N(s);
00241       beep ();
00242     }
00243     this << emit_invalidate_all ();
00244     return;
00245   }
00246   else {
00247     tabs   = array<string> (0);
00248     tab_nr = 0;
00249     tab_pos= 0;
00250   }
00251 
00252   /* other actions */
00253   if (key == "return") commit ();
00254   else if ((key == "escape") || (key == "C-c") ||
00255           (key == "C-g")) cancel ();
00256   else if ((key == "left") || (key == "C-b")) {
00257     if (pos>0) tm_char_backwards (s, pos); }
00258   else if ((key == "right") || (key == "C-f")) {
00259     if (pos<N(s)) tm_char_forwards (s, pos); }
00260   else if ((key == "home") || (key == "C-a")) pos=0;
00261   else if ((key == "end") || (key == "C-e")) pos=N(s);
00262   else if ((key == "up") || (key == "C-p")) {
00263     if (N(def) > 0) {
00264       def_cur= (def_cur+1) % N(def);
00265       s      = copy (def[def_cur]);
00266       pos    = N(s);
00267     }
00268   }
00269   else if ((key == "down") || (key == "C-n")) {
00270     if (N(def) > 0) {
00271       def_cur= (def_cur+N(def)-1) % N(def);
00272       s      = copy (def[def_cur]);
00273       pos    = N(s);
00274     }
00275   }
00276   else if (key == "C-k") s= s (0, pos);
00277   else if ((key == "C-d") || (key == "delete")) {
00278     if ((pos<N(s)) && (N(s)>0)) {
00279       int end= pos;
00280       tm_char_forwards (s, end);
00281       s= s (0, pos) * s (end, N(s));
00282     }
00283   }
00284   else if (key == "backspace" || key == "S-backspace") {
00285     if (pos>0) {
00286       int end= pos;
00287       tm_char_backwards (s, pos);
00288       s= s (0, pos) * s (end, N(s));
00289     }
00290   }
00291   else if (key == "C-backspace") {
00292     s= "";
00293     pos= 0;
00294   }
00295   else {
00296     if (starts (key, "<#"));
00297     else if (key == "<less>" || key == "<gtr>");
00298     else {
00299       if (N(key)!=1) return;
00300       int i (key[0]);
00301       if ((i>=0) && (i<32)) return;
00302     }
00303     s= s (0, pos) * key * s(pos, N(s));
00304     pos += N(key);
00305   }
00306   this << emit_invalidate_all ();
00307 }
00308 
00309 void
00310 input_widget_rep::handle_mouse (mouse_event ev) {
00311   if (greyed) return;
00312   update_draw_s ();
00313 
00314   string type= ev->type;
00315   SI     x   = ev->x;
00316   font   fn  = get_default_styled_font (style);
00317 
00318   if (type == "press-left") {
00319     if (N(s)>0) {
00320       metric ex;
00321       SI old= 0;
00322       pos=0; tm_char_forwards (s, pos);
00323       for (; pos<=N(s); tm_char_forwards (s, pos)) {
00324        fn->var_get_extents (draw_s (0, pos), ex);
00325        if (((old+ ex->x2+ dw- ex->x1) >> 1) > (x*SHRINK+ scroll)) {
00326          tm_char_backwards (s, pos);
00327          break;
00328        }
00329        old= ex->x2+ dw- ex->x1;
00330        if (pos >= N(s)) break;
00331       }
00332     }
00333     win->set_keyboard_focus (this);
00334     this << emit_invalidate_all ();
00335   }
00336 
00337   if (type == "press-middle") {
00338     tree t; string sel;
00339     (void) get_selection ("primary", t, sel, "verbatim");
00340     if (is_tuple (t, "extern", 1)) {
00341       string ins= as_string (t[1]);
00342       ins= tm_encode (ins);
00343       s= s (0, pos) * ins * s(pos, N(s));
00344       pos += N(ins);
00345       this << emit_invalidate_all ();
00346     }
00347     else if (is_tuple (t, "texmacs", 3));
00348   }
00349 }
00350 
00351 void
00352 input_widget_rep::handle_keyboard_focus (keyboard_focus_event ev) {
00353   if (got_focus && !ev->flag && !done && !persistent)
00354     cancel ();
00355   got_focus= ev->flag;
00356   if (attached ())
00357     this << emit_invalidate_all ();
00358 }
00359 
00360 void
00361 input_widget_rep::handle_set_string (set_string_event ev) {
00362   if (ev->which == "input") {
00363     s= copy (ev->s);
00364     pos= N(s);
00365     ok= (ev->s != "#f");
00366     if (attached ()) this << emit_invalidate_all ();
00367   }
00368   else if (ev->which == "type") type= copy (ev->s);
00369   else if (ev->which == "default") def << copy (ev->s);
00370   else attribute_widget_rep::handle_set_string (ev);
00371 }
00372 
00373 void
00374 input_widget_rep::handle_get_string (get_string_event ev) {
00375   if (ev->which == "input") {
00376     if (ok) ev->s= scm_quote (s);
00377     else ev->s= "#f";
00378   }
00379   else attribute_widget_rep::handle_get_string (ev);
00380 }
00381 
00382 /******************************************************************************
00383 * Interface
00384 ******************************************************************************/
00385 
00386 event
00387 set_input_string (string s) {
00388   return set_string ("input", s);
00389 }
00390 
00391 event
00392 get_input_string (string& s) {
00393   return get_string ("input", s);
00394 }
00395 
00396 wk_widget
00397 input_text_wk_widget (command call_back,
00398                     int style, string w, bool persistent)
00399 {
00400   (void) style;
00401   return tm_new<input_widget_rep> (call_back, style, w, persistent);
00402 }
00403 
00404 wk_widget
00405 input_text_wk_widget (command cb, string type, array<string> def,
00406                     int style, string w, bool persistent)
00407 {
00408   (void) style;
00409   int i, n= N(def);
00410   wk_widget inp= input_text_wk_widget (cb, style, w, persistent);
00411   inp << set_string ("type", type);
00412   if (n>0) inp << set_string ("input", def[0]);
00413   for (i=0; i<n; i++) inp << set_string ("default", def[i]);
00414   return inp;
00415 }