Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pgui/pgEntry.cxx

Go to the documentation of this file.
00001 // Filename: pgEntry.cxx
00002 // Created by:  drose (13Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "pgEntry.h"
00020 #include "pgMouseWatcherParameter.h"
00021 
00022 #include "cullTraverser.h"
00023 #include "cullTraverserData.h"
00024 #include "throw_event.h"
00025 #include "transformState.h"
00026 #include "mouseWatcherParameter.h"
00027 #include "keyboardButton.h"
00028 #include "mouseButton.h"
00029 #include "lineSegs.h"
00030 
00031 #include <math.h>
00032 
00033 TypeHandle PGEntry::_type_handle;
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: PGEntry::Constructor
00037 //       Access: Published
00038 //  Description: 
00039 ////////////////////////////////////////////////////////////////////
00040 PGEntry::
00041 PGEntry(const string &name) : 
00042   PGItem(name)
00043 {
00044   _cursor_position = 0;
00045   _cursor_stale = true;
00046   _max_chars = 0;
00047   _max_width = 0.0f;
00048   _num_lines = 1;
00049   _last_text_def = (TextNode *)NULL;
00050   _text_geom_stale = true;
00051   _blink_start = 0.0f;
00052   _blink_rate = 1.0f;
00053 
00054   _text_render_root = NodePath("text_root");
00055   _cursor_def = _text_render_root.attach_new_node("cursor");
00056   _cursor_visible = true;
00057 
00058   _cursor_keys_active = true;
00059   _obscure_mode = false;
00060 
00061   set_active(true);
00062   update_state();
00063 }
00064 
00065 ////////////////////////////////////////////////////////////////////
00066 //     Function: PGEntry::Destructor
00067 //       Access: Public, Virtual
00068 //  Description: 
00069 ////////////////////////////////////////////////////////////////////
00070 PGEntry::
00071 ~PGEntry() {
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: PGEntry::Copy Constructor
00076 //       Access: Protected
00077 //  Description: 
00078 ////////////////////////////////////////////////////////////////////
00079 PGEntry::
00080 PGEntry(const PGEntry &copy) :
00081   PGItem(copy),
00082   _wtext(copy._wtext),
00083   _obscured_wtext(copy._obscured_wtext),
00084   _cursor_position(copy._cursor_position),
00085   _max_chars(copy._max_chars),
00086   _max_width(copy._max_width),
00087   _text_defs(copy._text_defs),
00088   _blink_start(copy._blink_start),
00089   _blink_rate(copy._blink_rate),
00090   _cursor_keys_active(copy._cursor_keys_active),
00091   _obscure_mode(copy._obscure_mode)
00092 {
00093   _cursor_stale = true;
00094   _last_text_def = (TextNode *)NULL;
00095   _text_geom_stale = true;
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: PGEntry::make_copy
00100 //       Access: Public, Virtual
00101 //  Description: Returns a newly-allocated Node that is a shallow copy
00102 //               of this one.  It will be a different Node pointer,
00103 //               but its internal data may or may not be shared with
00104 //               that of the original Node.
00105 ////////////////////////////////////////////////////////////////////
00106 PandaNode *PGEntry::
00107 make_copy() const {
00108   return new PGEntry(*this);
00109 }
00110 
00111 ////////////////////////////////////////////////////////////////////
00112 //     Function: PGEntry::has_cull_callback
00113 //       Access: Protected, Virtual
00114 //  Description: Should be overridden by derived classes to return
00115 //               true if cull_callback() has been defined.  Otherwise,
00116 //               returns false to indicate cull_callback() does not
00117 //               need to be called for this node during the cull
00118 //               traversal.
00119 ////////////////////////////////////////////////////////////////////
00120 bool PGEntry::
00121 has_cull_callback() const {
00122   return true;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: PGEntry::cull_callback
00127 //       Access: Protected, Virtual
00128 //  Description: If has_cull_callback() returns true, this function
00129 //               will be called during the cull traversal to perform
00130 //               any additional operations that should be performed at
00131 //               cull time.  This may include additional manipulation
00132 //               of render state or additional visible/invisible
00133 //               decisions, or any other arbitrary operation.
00134 //
00135 //               By the time this function is called, the node has
00136 //               already passed the bounding-volume test for the
00137 //               viewing frustum, and the node's transform and state
00138 //               have already been applied to the indicated
00139 //               CullTraverserData object.
00140 //
00141 //               The return value is true if this node should be
00142 //               visible, or false if it should be culled.
00143 ////////////////////////////////////////////////////////////////////
00144 bool PGEntry::
00145 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00146   PGItem::cull_callback(trav, data);
00147   update_text();
00148   update_cursor();
00149 
00150   // Now render the text.
00151   CullTraverserData next_data(data, _text_render_root.node());
00152   trav->traverse(next_data);
00153 
00154   // Now continue to render everything else below this node.
00155   return true;
00156 }
00157 
00158 ////////////////////////////////////////////////////////////////////
00159 //     Function: PGEntry::press
00160 //       Access: Public, Virtual
00161 //  Description: This is a callback hook function, called whenever a
00162 //               mouse or keyboard entry is depressed while the mouse
00163 //               is within the region.
00164 ////////////////////////////////////////////////////////////////////
00165 void PGEntry::
00166 press(const MouseWatcherParameter &param, bool background) {
00167   if (get_active()) {
00168     if (param.has_button()) {
00169       ButtonHandle button = param.get_button();
00170       
00171       if (button == MouseButton::one() ||
00172           button == MouseButton::two() ||
00173           button == MouseButton::three()) {
00174         // Mouse button; set focus.
00175         set_focus(true);
00176         
00177       } else if ((!background && get_focus()) || 
00178                  (background && get_background_focus())) {
00179         // Keyboard button.
00180         _cursor_position = min(_cursor_position, (int)_wtext.length());
00181         _blink_start = ClockObject::get_global_clock()->get_frame_time();
00182         if (button == KeyboardButton::enter()) {
00183           // Enter.  Accept the entry.
00184           accept(param);
00185           
00186         } else if (button == KeyboardButton::backspace()) {
00187           // Backspace.  Remove the character to the left of the cursor.
00188           if (_cursor_position > 0) {
00189             if (_obscure_mode && _obscured_wtext.length() == _wtext.length()) {
00190               _obscured_wtext.erase(_obscured_wtext.begin() + _obscured_wtext.length() - 1);
00191             }
00192             _wtext.erase(_wtext.begin() + _cursor_position - 1);
00193             _cursor_position--;
00194             _cursor_stale = true;
00195             _text_geom_stale = true;
00196             erase(param);
00197           }
00198           
00199         } else if (button == KeyboardButton::del()) {
00200           // Delete.  Remove the character to the right of the cursor.
00201           if (_cursor_position < (int)_wtext.length()) {
00202             if (_obscure_mode && _obscured_wtext.length() == _wtext.length()) {
00203               _obscured_wtext.erase(_obscured_wtext.begin() + _obscured_wtext.length() - 1);
00204             }
00205             _wtext.erase(_wtext.begin() + _cursor_position);
00206             _text_geom_stale = true;
00207             erase(param);
00208           }
00209           
00210         } else if (button == KeyboardButton::left()) {
00211           if (_cursor_keys_active) {
00212             // Left arrow.  Move the cursor position to the left.
00213             _cursor_position = max(_cursor_position - 1, 0);
00214             _cursor_stale = true;
00215           }
00216           
00217         } else if (button == KeyboardButton::right()) {
00218           if (_cursor_keys_active) {
00219             // Right arrow.  Move the cursor position to the right.
00220             _cursor_position = min(_cursor_position + 1, (int)_wtext.length());
00221             _cursor_stale = true;
00222           }
00223           
00224         } else if (button == KeyboardButton::home()) {
00225           if (_cursor_keys_active) {
00226             // Home.  Move the cursor position to the beginning.
00227             _cursor_position = 0;
00228             _cursor_stale = true;
00229           }
00230           
00231         } else if (button == KeyboardButton::end()) {
00232           if (_cursor_keys_active) {
00233             // End.  Move the cursor position to the end.
00234             _cursor_position = _wtext.length();
00235             _cursor_stale = true;
00236           }
00237           
00238         } else if (!use_keystrokes && button.has_ascii_equivalent()) {
00239           // This part of the code is deprecated and will be removed
00240           // soon.  It only supports the old button up/down method of
00241           // sending keystrokes, instead of the new keystroke method.
00242           wchar_t key = button.get_ascii_equivalent();
00243           if (isprint(key)) {
00244             // A normal visible character.  Add a new character to the
00245             // text entry, if there's room.
00246             
00247             if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) {
00248               overflow(param);
00249             } else {
00250               wstring new_text = 
00251                 _wtext.substr(0, _cursor_position) + key +
00252                 _wtext.substr(_cursor_position);
00253 
00254               // Get a string to measure its length.  In normal mode,
00255               // we measure the text itself.  In obscure mode, we
00256               // measure a string of n asterisks.
00257               wstring measure_text;
00258               if (_obscure_mode) {
00259                 measure_text = get_display_wtext() + (wchar_t)'*';
00260               } else {
00261                 measure_text = new_text;
00262               }
00263               
00264               // Check the length.
00265               bool too_long = false;
00266               if (_max_width > 0.0f) {
00267                 TextNode *text_node = get_text_def(S_focus);
00268                 if (_num_lines <= 1) {
00269                   // If we have only one line, we can check the length
00270                   // by simply measuring the width of the text.
00271                   too_long = (text_node->calc_width(measure_text) > _max_width);
00272 
00273                 } else {
00274                   // If we have multiple lines, we have to check the
00275                   // length by wordwrapping it and counting up the
00276                   // number of lines.
00277                   wstring ww_text = text_node->wordwrap_to(measure_text, _max_width, true);
00278                   int num_lines = 1;
00279                   size_t last_line_start = 0;
00280                   for (size_t p = 0;
00281                        p < ww_text.length() && !too_long;
00282                        ++p) {
00283                     if (ww_text[p] == '\n') {
00284                       last_line_start = p + 1;
00285                       num_lines++;
00286                       too_long = (num_lines > _num_lines);
00287                     }
00288                   }
00289 
00290                   if (!too_long) {
00291                     // We must also ensure that the last line is not too
00292                     // long (it might be, because of additional
00293                     // whitespace on the end).
00294                     wstring last_line = ww_text.substr(last_line_start);
00295                     float last_line_width = text_node->calc_width(last_line);
00296                     if (num_lines == _num_lines) {
00297                       // Mainly we only care about this if we're on
00298                       // the very last line.
00299                       too_long = (last_line_width > _max_width);
00300 
00301                     } else {
00302                       // If we're not on the very last line, the width
00303                       // is still important, just so we don't allow an
00304                       // infinite number of spaces to accumulate.
00305                       // However, we must allow at least *one* space
00306                       // on the end of a line.
00307                       if (_wtext.length() >= 1 && 
00308                           _wtext[_wtext.length() - 1] == ' ') {
00309                         if (last_line_width > _max_width) {
00310                           // In this case, however, it's not exactly
00311                           // an overflow; we just want to reject the
00312                           // space.
00313                           return;
00314                         }
00315                       }
00316                     }
00317                   }
00318                 }
00319               }
00320               
00321               if (too_long) {
00322                 overflow(param);
00323                 
00324               } else {
00325                 _wtext = new_text;
00326                 if (_obscure_mode) {
00327                   _obscured_wtext = measure_text;
00328                 }
00329                 
00330                 _cursor_position++;
00331                 _cursor_stale = true;
00332                 _text_geom_stale = true;
00333                 type(param);
00334               }
00335             }
00336           }
00337         }
00338       }
00339     }
00340   }
00341   PGItem::press(param, background);
00342 }
00343 
00344 ////////////////////////////////////////////////////////////////////
00345 //     Function: PGEntry::keystroke
00346 //       Access: Public, Virtual
00347 //  Description: This is a callback hook function, called whenever
00348 //               the user types a key.
00349 ////////////////////////////////////////////////////////////////////
00350 void PGEntry::
00351 keystroke(const MouseWatcherParameter &param, bool background) {
00352   if (get_active()) {
00353     if (param.has_keycode()) {
00354       int keycode = param.get_keycode();
00355           
00356       if (use_keystrokes) {
00357         if (!isascii(keycode) || isprint(keycode)) {
00358           // A normal visible character.  Add a new character to the
00359           // text entry, if there's room.
00360           wstring new_char(1, (wchar_t)keycode);
00361 
00362           if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) {
00363             overflow(param);
00364           } else {
00365             _cursor_position = min(_cursor_position, (int)_wtext.length());
00366             wstring new_text = 
00367               _wtext.substr(0, _cursor_position) + new_char +
00368               _wtext.substr(_cursor_position);
00369             
00370             // Get a string to measure its length.  In normal mode,
00371             // we measure the text itself.  In obscure mode, we
00372             // measure a string of n asterisks.
00373             wstring measure_text;
00374             if (_obscure_mode) {
00375               measure_text = get_display_wtext() + (wchar_t)'*';
00376             } else {
00377               measure_text = new_text;
00378             }
00379 
00380             // Check the length.
00381             bool too_long = false;
00382             if (_max_width > 0.0f) {
00383               TextNode *text_node = get_text_def(S_focus);
00384               if (_num_lines <= 1) {
00385                 // If we have only one line, we can check the length
00386                 // by simply measuring the width of the text.
00387                 too_long = (text_node->calc_width(measure_text) > _max_width);
00388                 
00389               } else {
00390                 // If we have multiple lines, we have to check the
00391                 // length by wordwrapping it and counting up the
00392                 // number of lines.
00393                 wstring ww_text = text_node->wordwrap_to(measure_text, _max_width, true);
00394                 int num_lines = 1;
00395                 size_t last_line_start = 0;
00396                 for (size_t p = 0;
00397                      p < ww_text.length() && !too_long;
00398                      ++p) {
00399                   if (ww_text[p] == '\n') {
00400                     last_line_start = p + 1;
00401                     num_lines++;
00402                     too_long = (num_lines > _num_lines);
00403                   }
00404                 }
00405                 
00406                 if (!too_long) {
00407                   // We must also ensure that the last line is not too
00408                   // long (it might be, because of additional
00409                   // whitespace on the end).
00410                   wstring last_line = ww_text.substr(last_line_start);
00411                   float last_line_width = text_node->calc_width(last_line);
00412                   if (num_lines == _num_lines) {
00413                     // Mainly we only care about this if we're on
00414                     // the very last line.
00415                     too_long = (last_line_width > _max_width);
00416                     
00417                   } else {
00418                     // If we're not on the very last line, the width
00419                     // is still important, just so we don't allow an
00420                     // infinite number of spaces to accumulate.
00421                     // However, we must allow at least *one* space
00422                     // on the end of a line.
00423                     if (_wtext.length() >= 1 && 
00424                         _wtext[_wtext.length() - 1] == ' ') {
00425                       if (last_line_width > _max_width) {
00426                         // In this case, however, it's not exactly
00427                         // an overflow; we just want to reject the
00428                         // space.
00429                         return;
00430                       }
00431                     }
00432                   }
00433                 }
00434               }
00435             }
00436 
00437             if (too_long) {
00438               overflow(param);
00439               
00440             } else {
00441               _wtext = new_text;
00442               if (_obscure_mode) {
00443                 _obscured_wtext = measure_text;
00444               }
00445               
00446               _cursor_position += new_char.length();
00447               _cursor_stale = true;
00448               _text_geom_stale = true;
00449               type(param);
00450             }
00451           }
00452         }
00453       }
00454     }
00455   }
00456   PGItem::keystroke(param, background);
00457 }
00458 
00459 ////////////////////////////////////////////////////////////////////
00460 //     Function: PGEntry::accept
00461 //       Access: Public, Virtual
00462 //  Description: This is a callback hook function, called whenever the
00463 //               entry is accepted by the user pressing Enter normally.
00464 ////////////////////////////////////////////////////////////////////
00465 void PGEntry::
00466 accept(const MouseWatcherParameter &param) {
00467   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00468   string event = get_accept_event(param.get_button());
00469   play_sound(event);
00470   throw_event(event, EventParameter(ep));
00471   set_focus(false);
00472 }
00473 
00474 ////////////////////////////////////////////////////////////////////
00475 //     Function: PGEntry::overflow
00476 //       Access: Public, Virtual
00477 //  Description: This is a callback hook function, called whenever the
00478 //               entry is overflowed because the user attempts to type
00479 //               too many characters, exceeding either set_max_chars()
00480 //               or set_max_width().
00481 ////////////////////////////////////////////////////////////////////
00482 void PGEntry::
00483 overflow(const MouseWatcherParameter &param) {
00484   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00485   string event = get_overflow_event();
00486   play_sound(event);
00487   throw_event(event, EventParameter(ep));
00488 }
00489 
00490 ////////////////////////////////////////////////////////////////////
00491 //     Function: PGEntry::type
00492 //       Access: Public, Virtual
00493 //  Description: This is a callback hook function, called whenever the
00494 //               user extends the text by typing.
00495 ////////////////////////////////////////////////////////////////////
00496 void PGEntry::
00497 type(const MouseWatcherParameter &param) {
00498   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00499   string event = get_type_event();
00500   play_sound(event);
00501   throw_event(event, EventParameter(ep));
00502 }
00503 
00504 ////////////////////////////////////////////////////////////////////
00505 //     Function: PGEntry::erase
00506 //       Access: Public, Virtual
00507 //  Description: This is a callback hook function, called whenever the
00508 //               user erase characters in the text.
00509 ////////////////////////////////////////////////////////////////////
00510 void PGEntry::
00511 erase(const MouseWatcherParameter &param) {
00512   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00513   string event = get_erase_event();
00514   play_sound(event);
00515   throw_event(event, EventParameter(ep));
00516 }
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: PGEntry::setup
00520 //       Access: Published
00521 //  Description: Sets up the entry for normal use.  The width is the
00522 //               maximum width of characters that will be typed, and
00523 //               num_lines is the integer number of lines of text of
00524 //               the entry.  Both of these together determine the size
00525 //               of the entry, based on the TextNode in effect.
00526 ////////////////////////////////////////////////////////////////////
00527 void PGEntry::
00528 setup(float width, int num_lines) {
00529   set_text(string());
00530   _cursor_position = 0;
00531   set_max_chars(0);
00532   set_max_width(width);
00533   set_num_lines(num_lines);
00534 
00535   TextNode *text_node = get_text_def(S_focus);
00536   float line_height = text_node->get_line_height();
00537 
00538   // Define determine the four corners of the frame.
00539   LPoint3f ll(0.0f, 0.0f, -0.3f * line_height - (line_height * (num_lines - 1)));
00540   LPoint3f ur(width, 0.0f, line_height);
00541   LPoint3f lr(ur[0], 0.0f, ll[2]);
00542   LPoint3f ul(ll[0], 0.0f, ur[2]);
00543 
00544   // Transform each corner by the TextNode's transform.
00545   LMatrix4f mat = text_node->get_transform();
00546   ll = ll * mat;
00547   ur = ur * mat;
00548   lr = lr * mat;
00549   ul = ul * mat;
00550 
00551   // And get the new minmax to define the frame.  We do all this work
00552   // instead of just using the lower-left and upper-right corners,
00553   // just in case the text was rotated.
00554   LVecBase4f frame;
00555   frame[0] = min(min(ll[0], ur[0]), min(lr[0], ul[0]));
00556   frame[1] = max(max(ll[0], ur[0]), max(lr[0], ul[0]));
00557   frame[2] = min(min(ll[2], ur[2]), min(lr[2], ul[2]));
00558   frame[3] = max(max(ll[2], ur[2]), max(lr[2], ul[2]));
00559 
00560   switch (text_node->get_align()) {
00561   case TextNode::A_left:
00562     // The default case.
00563     break;
00564 
00565   case TextNode::A_center:
00566     frame[0] = -width / 2.0;
00567     frame[1] = width / 2.0;
00568     break;
00569 
00570   case TextNode::A_right:
00571     frame[0] = -width;
00572     frame[1] = 0.0f;
00573     break;
00574   }
00575 
00576   set_frame(frame[0] - 0.15f, frame[1] + 0.15f, frame[2], frame[3]);
00577 
00578   PGFrameStyle style;
00579   style.set_width(0.1f, 0.1f);
00580   style.set_type(PGFrameStyle::T_bevel_in);
00581   style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
00582 
00583   set_frame_style(S_no_focus, style);
00584 
00585   style.set_color(0.9f, 0.9f, 0.9f, 1.0f);
00586   set_frame_style(S_focus, style);
00587 
00588   style.set_color(0.6f, 0.6f, 0.6f, 1.0f);
00589   set_frame_style(S_inactive, style);
00590 
00591   // Set up a default cursor: a vertical bar.
00592   clear_cursor_def();
00593 
00594   LineSegs ls;
00595   ls.set_color(0.0f, 0.0f, 0.0f, 1.0f);
00596   ls.move_to(0.0f, 0.0f, -0.15f * line_height);
00597   ls.draw_to(0.0f, 0.0f, 0.85f * line_height);
00598   get_cursor_def().attach_new_node(ls.create());
00599   
00600   /*
00601   // An underscore cursor would work too.
00602   text_node->set_text("_");
00603   get_cursor_def().attach_new_node(text_node->generate());
00604   */
00605 }
00606 
00607 ////////////////////////////////////////////////////////////////////
00608 //     Function: PGEntry::set_text_def
00609 //       Access: Published
00610 //  Description: Changes the TextNode that will be used to render the
00611 //               text within the entry when the entry is in the
00612 //               indicated state.  The default if nothing is specified
00613 //               is the same TextNode returned by
00614 //               PGItem::get_text_node().
00615 //
00616 //               It is the responsibility of the user to ensure that
00617 //               this TextNode has been frozen by a call to freeze().
00618 //               Passing in an unfrozen TextNode will result in
00619 //               needless work.
00620 ////////////////////////////////////////////////////////////////////
00621 void PGEntry::
00622 set_text_def(int state, TextNode *node) {
00623   nassertv(state >= 0 && state < 1000);  // Sanity check.
00624   if (node == (TextNode *)NULL && state >= (int)_text_defs.size()) {
00625     // If we're setting it to NULL, we don't need to slot a new one.
00626     return;
00627   }
00628   slot_text_def(state);
00629 
00630   _text_defs[state] = node;
00631 }
00632 
00633 ////////////////////////////////////////////////////////////////////
00634 //     Function: PGEntry::get_text_def
00635 //       Access: Published
00636 //  Description: Returns the TextNode that will be used to render the
00637 //               text within the entry when the entry is in the
00638 //               indicated state.  See set_text_def().
00639 ////////////////////////////////////////////////////////////////////
00640 TextNode *PGEntry:: 
00641 get_text_def(int state) const {
00642   if (state < 0 || state >= (int)_text_defs.size()) {
00643     // If we don't have a definition, use the global one.
00644     return get_text_node();
00645   }
00646   if (_text_defs[state] == (TextNode *)NULL) {
00647     return get_text_node();
00648   }
00649   return _text_defs[state];
00650 }
00651 
00652 ////////////////////////////////////////////////////////////////////
00653 //     Function: PGEntry::set_active
00654 //       Access: Published, Virtual
00655 //  Description: Toggles the active/inactive state of the entry.  In
00656 //               the case of a PGEntry, this also changes its visual
00657 //               appearance.
00658 ////////////////////////////////////////////////////////////////////
00659 void PGEntry:: 
00660 set_active(bool active) {
00661   PGItem::set_active(active);
00662   update_state();
00663 }
00664 
00665 ////////////////////////////////////////////////////////////////////
00666 //     Function: PGEntry::set_focus
00667 //       Access: Published, Virtual
00668 //  Description: Toggles the focus state of the entry.  In the case of
00669 //               a PGEntry, this also changes its visual appearance.
00670 ////////////////////////////////////////////////////////////////////
00671 void PGEntry:: 
00672 set_focus(bool focus) {
00673   PGItem::set_focus(focus);
00674   _blink_start = ClockObject::get_global_clock()->get_frame_time();
00675   update_state();
00676 }
00677 
00678 ////////////////////////////////////////////////////////////////////
00679 //     Function: PGEntry::get_display_wtext
00680 //       Access: Private
00681 //  Description: Returns the string that should be displayed within
00682 //               the entry.  This is normally _wtext, but it may be
00683 //               _obscured_wtext.
00684 ////////////////////////////////////////////////////////////////////
00685 const wstring &PGEntry::
00686 get_display_wtext() {
00687   if (_obscure_mode) {
00688     // If obscure mode is enabled, we should just display a bunch of
00689     // asterisks.
00690     if (_obscured_wtext.length() != _wtext.length()) {
00691       _obscured_wtext = wstring();
00692       wstring::const_iterator ti;
00693       for (ti = _wtext.begin(); ti != _wtext.end(); ++ti) {
00694         _obscured_wtext += (wchar_t)'*';
00695       }
00696     }
00697 
00698     return _obscured_wtext;
00699 
00700   } else {
00701     // In normal, non-obscure mode, we display the actual text.
00702     return _wtext;
00703   }
00704 }
00705 
00706 ////////////////////////////////////////////////////////////////////
00707 //     Function: PGEntry::slot_text_def
00708 //       Access: Private
00709 //  Description: Ensures there is a slot in the array for the given
00710 //               text definition.
00711 ////////////////////////////////////////////////////////////////////
00712 void PGEntry::
00713 slot_text_def(int state) {
00714   while (state >= (int)_text_defs.size()) {
00715     _text_defs.push_back((TextNode *)NULL);
00716   }
00717 }
00718 
00719 ////////////////////////////////////////////////////////////////////
00720 //     Function: PGEntry::update_text
00721 //       Access: Private
00722 //  Description: Causes the PGEntry to recompute its text, if
00723 //               necessary.
00724 ////////////////////////////////////////////////////////////////////
00725 void PGEntry:: 
00726 update_text() {
00727   TextNode *node = get_text_def(get_state());
00728   nassertv(node != (TextNode *)NULL);
00729 
00730   if (_text_geom_stale || node != _last_text_def) {
00731     const wstring &display_wtext = get_display_wtext();
00732 
00733     // We need to regenerate.
00734     _last_text_def = node;
00735 
00736     if (_max_width > 0.0f && _num_lines > 1) {
00737       // Fold the text into multiple lines.
00738       wstring ww_text = 
00739         _last_text_def->wordwrap_to(display_wtext, _max_width, true);
00740 
00741       // And chop the lines up into pieces.
00742       _ww_lines.clear();
00743       size_t p = 0;
00744       size_t q = ww_text.find((wchar_t)'\n');
00745       while (q != string::npos) {
00746         _ww_lines.push_back(WWLine());
00747         WWLine &line = _ww_lines.back();
00748         line._str = ww_text.substr(p, q - p);
00749 
00750         // Get the left edge of the text at this line.
00751         line._left = 0.0f;
00752         if (_last_text_def->get_align() != TextNode::A_left) {
00753           _last_text_def->set_wtext(line._str);
00754           line._left = _last_text_def->get_left();
00755         }
00756 
00757         p = q + 1;
00758         q = ww_text.find('\n', p);
00759       }
00760       _ww_lines.push_back(WWLine());
00761       WWLine &line = _ww_lines.back();
00762       line._str = ww_text.substr(p);
00763       
00764       // Get the left edge of the text at this line.
00765       line._left = 0.0f;
00766       if (_last_text_def->get_align() != TextNode::A_left) {
00767         _last_text_def->set_wtext(line._str);
00768         line._left = _last_text_def->get_left();
00769       }
00770 
00771       _last_text_def->set_wtext(ww_text);
00772 
00773     } else {
00774       // Only one line.
00775       _ww_lines.clear();
00776       _ww_lines.push_back(WWLine());
00777       WWLine &line = _ww_lines.back();
00778       line._str = display_wtext;
00779 
00780       _last_text_def->set_wtext(display_wtext);
00781       line._left = _last_text_def->get_left();
00782     }
00783 
00784     if (!_current_text.is_empty()) {
00785       _current_text.remove_node();
00786     }
00787     _current_text = 
00788       _text_render_root.attach_new_node(_last_text_def->generate());
00789     _text_geom_stale = false;
00790     _cursor_stale = true;
00791   }
00792 }
00793 
00794 ////////////////////////////////////////////////////////////////////
00795 //     Function: PGEntry::update_cursor
00796 //       Access: Private
00797 //  Description: Moves the cursor to its correct position.
00798 ////////////////////////////////////////////////////////////////////
00799 void PGEntry:: 
00800 update_cursor() {
00801   TextNode *node = get_text_def(get_state());
00802   nassertv(node != (TextNode *)NULL);
00803 
00804   if (_cursor_stale || node != _last_text_def) {
00805     update_text();
00806 
00807     _cursor_position = min(_cursor_position, (int)_wtext.length());
00808 
00809     // Determine the row and column of the cursor.
00810     int row = 0;
00811     int column = _cursor_position;
00812     while (row + 1 < (int)_ww_lines.size() &&
00813            column > (int)_ww_lines[row]._str.length()) {
00814       column -= _ww_lines[row]._str.length();
00815       row++;
00816     }
00817 
00818     nassertv(row >= 0 && row < (int)_ww_lines.size());
00819     nassertv(column >= 0 && column <= (int)_ww_lines[row]._str.length());
00820 
00821     float width = 
00822       _last_text_def->calc_width(_ww_lines[row]._str.substr(0, column));
00823     float line_height = _last_text_def->get_line_height();
00824 
00825     LVecBase3f trans(_ww_lines[row]._left + width, 0.0f, -line_height * row);
00826     _cursor_def.set_pos(trans);
00827 
00828     _cursor_stale = false;
00829   }
00830 
00831   // Should the cursor be visible?
00832   if (!get_focus()) {
00833     show_hide_cursor(false);
00834   } else {
00835     double elapsed_time = 
00836       ClockObject::get_global_clock()->get_frame_time() - _blink_start;
00837     int cycle = (int)(elapsed_time * _blink_rate * 2.0f);
00838     bool visible = ((cycle & 1) == 0);
00839     show_hide_cursor(visible);
00840   }
00841 }
00842 
00843 ////////////////////////////////////////////////////////////////////
00844 //     Function: PGEntry::show_hide_cursor
00845 //       Access: Private
00846 //  Description: Makes the cursor visible or invisible, e.g. during a
00847 //               blink cycle.
00848 ////////////////////////////////////////////////////////////////////
00849 void PGEntry:: 
00850 show_hide_cursor(bool visible) {
00851   if (visible != _cursor_visible) {
00852     if (visible) {
00853       _cursor_def.show();
00854     } else {
00855       _cursor_def.hide();
00856     }
00857     _cursor_visible = visible;
00858   }
00859 }
00860 
00861 ////////////////////////////////////////////////////////////////////
00862 //     Function: PGEntry::update_state
00863 //       Access: Private
00864 //  Description: Determines what the correct state for the PGEntry
00865 //               should be.
00866 ////////////////////////////////////////////////////////////////////
00867 void PGEntry:: 
00868 update_state() {
00869   if (get_active()) {
00870     if (get_focus()) {
00871       set_state(S_focus);
00872     } else {
00873       set_state(S_no_focus);
00874     }
00875   } else {
00876     set_state(S_inactive);
00877   }
00878 }

Generated on Fri May 2 00:42:37 2003 for Panda by doxygen1.3