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

panda/src/pgui/pgItem.cxx

Go to the documentation of this file.
00001 // Filename: pgItem.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 "pgItem.h"
00020 #include "pgMouseWatcherParameter.h"
00021 #include "pgCullTraverser.h"
00022 #include "config_pgui.h"
00023 
00024 #include "pandaNode.h"
00025 #include "sceneGraphReducer.h"
00026 #include "throw_event.h"
00027 #include "string_utils.h"
00028 #include "nodePath.h"
00029 #include "cullTraverser.h"
00030 #include "cullTraverserData.h"
00031 #include "cullBinManager.h"
00032 #include "dcast.h"
00033 
00034 #ifdef HAVE_AUDIO
00035 #include "audioSound.h"
00036 #endif
00037 
00038 TypeHandle PGItem::_type_handle;
00039 PT(TextNode) PGItem::_text_node;
00040 PGItem *PGItem::_focus_item = (PGItem *)NULL;
00041 PGItem::BackgroundFocus PGItem::_background_focus;
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: PGItem::Constructor
00045 //       Access: Published
00046 //  Description: 
00047 ////////////////////////////////////////////////////////////////////
00048 PGItem::
00049 PGItem(const string &name) : 
00050   PandaNode(name)
00051 {
00052   _has_frame = false;
00053   _frame.set(0, 0, 0, 0);
00054   _region = new PGMouseWatcherRegion(this);
00055   _state = 0;
00056   _flags = 0;
00057 }
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: PGItem::Destructor
00061 //       Access: Public, Virtual
00062 //  Description: 
00063 ////////////////////////////////////////////////////////////////////
00064 PGItem::
00065 ~PGItem() {
00066   nassertv(_region->_item == this);
00067   _region->_item = (PGItem *)NULL;
00068 
00069   set_background_focus(false);
00070   if (_focus_item == this) {
00071     _focus_item = (PGItem *)NULL;
00072   }
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: PGItem::Copy Constructor
00077 //       Access: Public
00078 //  Description: 
00079 ////////////////////////////////////////////////////////////////////
00080 PGItem::
00081 PGItem(const PGItem &copy) :
00082   PandaNode(copy),
00083   _has_frame(copy._has_frame),
00084   _frame(copy._frame),
00085   _state(copy._state),
00086   _flags(copy._flags)
00087 {
00088   _region = new PGMouseWatcherRegion(this);
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: PGItem::make_copy
00093 //       Access: Protected, Virtual
00094 //  Description: Returns a newly-allocated Node that is a shallow copy
00095 //               of this one.  It will be a different Node pointer,
00096 //               but its internal data may or may not be shared with
00097 //               that of the original Node.
00098 ////////////////////////////////////////////////////////////////////
00099 PandaNode *PGItem::
00100 make_copy() const {
00101   return new PGItem(*this);
00102 }
00103 
00104 ////////////////////////////////////////////////////////////////////
00105 //     Function: PGItem::xform
00106 //       Access: Protected, Virtual
00107 //  Description: Transforms the contents of this node by the indicated
00108 //               matrix, if it means anything to do so.  For most
00109 //               kinds of nodes, this does nothing.
00110 ////////////////////////////////////////////////////////////////////
00111 void PGItem::
00112 xform(const LMatrix4f &mat) {
00113   // Transform the frame.
00114   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
00115   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
00116   ll = ll * mat;
00117   ur = ur * mat;
00118   _frame.set(ll[0], ur[0], ll[2], ur[2]);
00119 
00120   // Transform the individual states and their frame styles.
00121   StateDefs::iterator di;
00122   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00123     NodePath &root = (*di)._root;
00124     // Apply the matrix to the previous transform.
00125     root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
00126 
00127     // Now flatten the transform into the subgraph.
00128     SceneGraphReducer gr;
00129     gr.apply_attribs(root.node());
00130 
00131     // Transform the frame style too.
00132     if ((*di)._frame_style.xform(mat)) {
00133       (*di)._frame_stale = true;
00134     }
00135   }
00136   mark_bound_stale();
00137 }
00138 
00139 ////////////////////////////////////////////////////////////////////
00140 //     Function: PGItem::has_cull_callback
00141 //       Access: Protected, Virtual
00142 //  Description: Should be overridden by derived classes to return
00143 //               true if cull_callback() has been defined.  Otherwise,
00144 //               returns false to indicate cull_callback() does not
00145 //               need to be called for this node during the cull
00146 //               traversal.
00147 ////////////////////////////////////////////////////////////////////
00148 bool PGItem::
00149 has_cull_callback() const {
00150   return true;
00151 }
00152 
00153 ////////////////////////////////////////////////////////////////////
00154 //     Function: PGItem::cull_callback
00155 //       Access: Protected, Virtual
00156 //  Description: If has_cull_callback() returns true, this function
00157 //               will be called during the cull traversal to perform
00158 //               any additional operations that should be performed at
00159 //               cull time.  This may include additional manipulation
00160 //               of render state or additional visible/invisible
00161 //               decisions, or any other arbitrary operation.
00162 //
00163 //               By the time this function is called, the node has
00164 //               already passed the bounding-volume test for the
00165 //               viewing frustum, and the node's transform and state
00166 //               have already been applied to the indicated
00167 //               CullTraverserData object.
00168 //
00169 //               The return value is true if this node should be
00170 //               visible, or false if it should be culled.
00171 ////////////////////////////////////////////////////////////////////
00172 bool PGItem::
00173 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00174   if (has_frame() && get_active()) {
00175     // The item has a frame, so we want to generate a region for it
00176     // and update the MouseWatcher.
00177 
00178     // We can only do this if our traverser is a PGCullTraverser
00179     // (which will be the case if this node was parented somewhere
00180     // under a PGTop node).
00181     if (trav->is_exact_type(PGCullTraverser::get_class_type())) {
00182       PGCullTraverser *pg_trav;
00183       DCAST_INTO_R(pg_trav, trav, true);
00184 
00185       const LMatrix4f &transform = data._net_transform->get_mat();
00186 
00187       // Consider the cull bin this object is in.  Since the binning
00188       // affects the render order, we want bins that render later to
00189       // get higher sort values.
00190       int bin_index = data._state->get_bin_index();
00191       int sort;
00192 
00193       CullBinManager *bin_manager = CullBinManager::get_global_ptr();
00194       CullBinManager::BinType bin_type = bin_manager->get_bin_type(bin_index);
00195       if (bin_type == CullBinManager::BT_fixed) {
00196         // If the bin is a "fixed" type bin, our local sort is based
00197         // on the fixed order.
00198         sort = data._state->get_draw_order();
00199 
00200       } else if (bin_type == CullBinManager::BT_unsorted) {
00201         // If the bin is an "unsorted" type bin, we base the local
00202         // sort on the scene graph order.
00203         sort = pg_trav->_sort_index;
00204         pg_trav->_sort_index++;
00205 
00206       } else {
00207         // Otherwise, the local sort is irrelevant.
00208         sort = 0;
00209       }
00210 
00211       // Now what order does this bin sort relative to the other bins?
00212       // This becomes the high-order part of the final sort count.
00213       int bin_sort = bin_manager->get_bin_sort(data._state->get_bin_index());
00214 
00215       // Combine the two sorts into a single int.  This assumes we
00216       // only need 16 bits for each sort number, possibly an erroneous
00217       // assumption.  We should really provide two separate sort
00218       // values, both ints, in the MouseWatcherRegion; but in the
00219       // interest of expediency we work within the existing interface
00220       // which only provides one.
00221       sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
00222 
00223       activate_region(transform, sort);
00224       pg_trav->_top->add_region(get_region());
00225     }
00226   }
00227 
00228   if (has_state_def(get_state())) {
00229     // This item has a current state definition that we should use
00230     // to render the item.
00231     NodePath &root = get_state_def(get_state());
00232     CullTraverserData next_data(data, root.node());
00233     trav->traverse(next_data);
00234   }
00235 
00236   // Now continue to render everything else below this node.
00237   return true;
00238 }
00239 
00240 ////////////////////////////////////////////////////////////////////
00241 //     Function: PGItem::recompute_internal_bound
00242 //       Access: Protected, Virtual
00243 //  Description: Called when needed to recompute the node's
00244 //               _internal_bound object.  Nodes that contain anything
00245 //               of substance should redefine this to do the right
00246 //               thing.
00247 ////////////////////////////////////////////////////////////////////
00248 BoundingVolume *PGItem::
00249 recompute_internal_bound() {
00250   // First, get ourselves a fresh, empty bounding volume.
00251   BoundingVolume *bound = PandaNode::recompute_internal_bound();
00252   nassertr(bound != (BoundingVolume *)NULL, bound);
00253 
00254   // Now actually compute the bounding volume by putting it around all
00255   // of our states' bounding volumes.
00256   pvector<const BoundingVolume *> child_volumes;
00257 
00258   // We walk through the list of state defs indirectly, calling
00259   // get_state_def() on each one, to ensure that the frames are
00260   // updated correctly before we measure their bounding volumes.
00261   for (int i = 0; i < (int)_state_defs.size(); i++) {
00262     NodePath &root = get_state_def(i);
00263     if (!root.is_empty()) {
00264       child_volumes.push_back(&root.node()->get_bound());
00265     }
00266   }
00267 
00268   const BoundingVolume **child_begin = &child_volumes[0];
00269   const BoundingVolume **child_end = child_begin + child_volumes.size();
00270 
00271   bound->around(child_begin, child_end);
00272   return bound;
00273 }
00274 
00275 ////////////////////////////////////////////////////////////////////
00276 //     Function: PGItem::activate_region
00277 //       Access: Public
00278 //  Description: Applies the indicated scene graph transform and order
00279 //               as determined by the traversal from PGTop.
00280 ////////////////////////////////////////////////////////////////////
00281 void PGItem::
00282 activate_region(const LMatrix4f &transform, int sort) {
00283   // Transform all four vertices, and get the new bounding box.  This
00284   // way the region works (mostly) even if has been rotated.
00285   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
00286   LPoint3f lr(_frame[1], 0.0f, _frame[2]);
00287   LPoint3f ul(_frame[0], 0.0f, _frame[3]);
00288   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
00289   ll = ll * transform;
00290   lr = lr * transform;
00291   ul = ul * transform;
00292   ur = ur * transform;
00293   _region->set_frame(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
00294                      max(max(ll[0], lr[0]), max(ul[0], ur[0])),
00295                      min(min(ll[2], lr[2]), min(ul[2], ur[2])),
00296                      max(max(ll[2], lr[2]), max(ul[2], ur[2])));
00297                      
00298   _region->set_sort(sort);
00299   _region->set_active(true);
00300 }
00301 
00302 ////////////////////////////////////////////////////////////////////
00303 //     Function: PGItem::enter
00304 //       Access: Public, Virtual
00305 //  Description: This is a callback hook function, called whenever the
00306 //               mouse enters the region.  The mouse is only
00307 //               considered to be "entered" in one region at a time;
00308 //               in the case of nested regions, it exits the outer
00309 //               region before entering the inner one.
00310 ////////////////////////////////////////////////////////////////////
00311 void PGItem::
00312 enter(const MouseWatcherParameter &param) {
00313   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00314   string event = get_enter_event();
00315   play_sound(event);
00316   throw_event(event, EventParameter(ep));
00317 }
00318 
00319 ////////////////////////////////////////////////////////////////////
00320 //     Function: PGItem::exit
00321 //       Access: Public, Virtual
00322 //  Description: This is a callback hook function, called whenever the
00323 //               mouse exits the region.  The mouse is only considered
00324 //               to be "entered" in one region at a time; in the case
00325 //               of nested regions, it exits the outer region before
00326 //               entering the inner one.
00327 ////////////////////////////////////////////////////////////////////
00328 void PGItem::
00329 exit(const MouseWatcherParameter &param) {
00330   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00331   string event = get_exit_event();
00332   play_sound(event);
00333   throw_event(event, EventParameter(ep));
00334 }
00335 
00336 ////////////////////////////////////////////////////////////////////
00337 //     Function: PGItem::within
00338 //       Access: Public, Virtual
00339 //  Description: This is a callback hook function, called whenever the
00340 //               mouse moves within the boundaries of the region, even
00341 //               if it is also within the boundaries of a nested
00342 //               region.  This is different from "enter", which is
00343 //               only called whenever the mouse is within only that
00344 //               region.
00345 ////////////////////////////////////////////////////////////////////
00346 void PGItem::
00347 within(const MouseWatcherParameter &param) {
00348   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00349   string event = get_within_event();
00350   play_sound(event);
00351   throw_event(event, EventParameter(ep));
00352 }
00353 
00354 ////////////////////////////////////////////////////////////////////
00355 //     Function: PGItem::without
00356 //       Access: Public, Virtual
00357 //  Description: This is a callback hook function, called whenever the
00358 //               mouse moves completely outside the boundaries of the
00359 //               region.  See within().
00360 ////////////////////////////////////////////////////////////////////
00361 void PGItem::
00362 without(const MouseWatcherParameter &param) {
00363   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00364   string event = get_without_event();
00365   play_sound(event);
00366   throw_event(event, EventParameter(ep));
00367 }
00368 
00369 ////////////////////////////////////////////////////////////////////
00370 //     Function: PGItem::focus_in
00371 //       Access: Public, Virtual
00372 //  Description: This is a callback hook function, called whenever the
00373 //               widget gets the keyboard focus.
00374 ////////////////////////////////////////////////////////////////////
00375 void PGItem::
00376 focus_in() {
00377   string event = get_focus_in_event();
00378   play_sound(event);
00379   throw_event(event);
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: PGItem::focus_out
00384 //       Access: Public, Virtual
00385 //  Description: This is a callback hook function, called whenever the
00386 //               widget loses the keyboard focus.
00387 ////////////////////////////////////////////////////////////////////
00388 void PGItem::
00389 focus_out() {
00390   string event = get_focus_out_event();
00391   play_sound(event);
00392   throw_event(event);
00393 }
00394 
00395 ////////////////////////////////////////////////////////////////////
00396 //     Function: PGItem::press
00397 //       Access: Public, Virtual
00398 //  Description: This is a callback hook function, called whenever a
00399 //               mouse or keyboard button is depressed while the mouse
00400 //               is within the region.
00401 ////////////////////////////////////////////////////////////////////
00402 void PGItem::
00403 press(const MouseWatcherParameter &param, bool background) {
00404   if (!background) {
00405     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00406     string event = get_press_event(param.get_button());
00407     play_sound(event);
00408     throw_event(event, EventParameter(ep));
00409   }
00410 }
00411 
00412 ////////////////////////////////////////////////////////////////////
00413 //     Function: PGItem::release
00414 //       Access: Public, Virtual
00415 //  Description: This is a callback hook function, called whenever a
00416 //               mouse or keyboard button previously depressed with
00417 //               press() is released.
00418 ////////////////////////////////////////////////////////////////////
00419 void PGItem::
00420 release(const MouseWatcherParameter &param, bool background) {
00421   if (!background) {
00422     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00423     string event = get_release_event(param.get_button());
00424     play_sound(event);
00425     throw_event(event, EventParameter(ep));
00426   }
00427 }
00428 
00429 ////////////////////////////////////////////////////////////////////
00430 //     Function: PGItem::keystroke
00431 //       Access: Public, Virtual
00432 //  Description: This is a callback hook function, called whenever
00433 //               the user presses a key.
00434 ////////////////////////////////////////////////////////////////////
00435 void PGItem::
00436 keystroke(const MouseWatcherParameter &param, bool background) {
00437   if (!background) {
00438     PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00439     string event = get_keystroke_event();
00440     play_sound(event);
00441     throw_event(event, EventParameter(ep));
00442   }
00443 }
00444 
00445 ////////////////////////////////////////////////////////////////////
00446 //     Function: PGItem::background_press
00447 //       Access: Public, Static
00448 //  Description: Calls press() on all the PGItems with background
00449 //               focus.
00450 ////////////////////////////////////////////////////////////////////
00451 void PGItem::
00452 background_press(const MouseWatcherParameter &param) {
00453   BackgroundFocus::const_iterator fi;
00454   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00455     PGItem *item = *fi;
00456     if (!item->get_focus()) {
00457       item->press(param, true);
00458     }
00459   }
00460 }
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: PGItem::background_release
00464 //       Access: Public, Static
00465 //  Description: Calls release() on all the PGItems with background
00466 //               focus.
00467 ////////////////////////////////////////////////////////////////////
00468 void PGItem::
00469 background_release(const MouseWatcherParameter &param) {
00470   BackgroundFocus::const_iterator fi;
00471   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00472     PGItem *item = *fi;
00473     if (!item->get_focus()) {
00474       item->release(param, true);
00475     }
00476   }
00477 }
00478 
00479 ////////////////////////////////////////////////////////////////////
00480 //     Function: PGItem::background_keystroke
00481 //       Access: Public, Static
00482 //  Description: Calls keystroke() on all the PGItems with background
00483 //               focus.
00484 ////////////////////////////////////////////////////////////////////
00485 void PGItem::
00486 background_keystroke(const MouseWatcherParameter &param) {
00487   BackgroundFocus::const_iterator fi;
00488   for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00489     PGItem *item = *fi;
00490     if (!item->get_focus()) {
00491       item->keystroke(param, true);
00492     }
00493   }
00494 }
00495 
00496 ////////////////////////////////////////////////////////////////////
00497 //     Function: PGItem::set_active
00498 //       Access: Published, Virtual
00499 //  Description: Sets whether the PGItem is active for mouse watching.
00500 //               This is not necessarily related to the
00501 //               active/inactive appearance of the item, which is
00502 //               controlled by set_state(), but it does affect whether
00503 //               it responds to mouse events.
00504 ////////////////////////////////////////////////////////////////////
00505 void PGItem::
00506 set_active(bool active) {
00507   if (active) {
00508     _flags |= F_active;
00509   } else {
00510     _flags &= ~F_active;
00511     // Deactivating the item automatically defocuses it too.
00512     if (get_focus()) {
00513       set_focus(false);
00514     }
00515   }
00516 }
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: PGItem::set_focus
00520 //       Access: Published, Virtual
00521 //  Description: Sets whether the PGItem currently has keyboard focus.
00522 //               This simply means that the item may respond to
00523 //               keyboard events as well as to mouse events; precisely
00524 //               what this means is up to the individual item.  
00525 //
00526 //               Only one PGItem in the world is allowed to have focus
00527 //               at any given time.  Setting the focus on any other
00528 //               item automatically disables the focus from the
00529 //               previous item.
00530 ////////////////////////////////////////////////////////////////////
00531 void PGItem::
00532 set_focus(bool focus) {
00533   if (focus) {
00534     if (!get_active()) {
00535       // Cannot set focus on an inactive item.
00536       return;
00537     }
00538 
00539     // Set the keyboard focus to this item.
00540     if (_focus_item != this) {
00541       if (_focus_item != (PGItem *)NULL) {
00542         // Clear the focus from whatever item currently has it.
00543         _focus_item->set_focus(false);
00544       }
00545       _focus_item = this;
00546     }
00547     if (!get_focus()) {
00548       focus_in();
00549       _flags |= F_focus;
00550     }
00551 
00552   } else {
00553     if (_focus_item == this) {
00554       // Remove this item from the focus.
00555       _focus_item = (PGItem *)NULL;
00556     }
00557 
00558     if (get_focus()) {
00559       focus_out();
00560       _flags &= ~F_focus;
00561     }
00562   }
00563   _region->set_keyboard(focus);
00564 }
00565 
00566 ////////////////////////////////////////////////////////////////////
00567 //     Function: PGItem::set_background_focus
00568 //       Access: Published
00569 //  Description: Sets the background_focus flag for this item.  When
00570 //               background_focus is enabled, the item will receive
00571 //               keypress events even if it is not in focus; in fact,
00572 //               even if it is not onscreen.  Unlike normal focus,
00573 //               many items may have background_focus simultaneously.
00574 ////////////////////////////////////////////////////////////////////
00575 void PGItem::
00576 set_background_focus(bool focus) {
00577   if (focus != get_background_focus()) {
00578     if (focus) {
00579       // Activate background focus.
00580       _flags |= F_background_focus;
00581       bool inserted = _background_focus.insert(this).second;
00582       nassertv(inserted);
00583 
00584     } else {
00585       // Deactivate background focus.
00586       _flags &= ~F_background_focus;
00587       size_t num_erased = _background_focus.erase(this);
00588       nassertv(num_erased == 1);
00589     }
00590   }
00591 }
00592 
00593 ////////////////////////////////////////////////////////////////////
00594 //     Function: PGItem::get_num_state_defs
00595 //       Access: Published
00596 //  Description: Returns one more than the highest-numbered state def
00597 //               that was ever assigned to the PGItem.  The complete
00598 //               set of state defs assigned may then be retrieved by
00599 //               indexing from 0 to (get_num_state_defs() - 1).
00600 //
00601 //               This is only an upper limit on the actual number of
00602 //               state defs, since there may be holes in the list.
00603 ////////////////////////////////////////////////////////////////////
00604 int PGItem::
00605 get_num_state_defs() const {
00606   return _state_defs.size();
00607 }
00608 
00609 ////////////////////////////////////////////////////////////////////
00610 //     Function: PGItem::has_state_def
00611 //       Access: Published
00612 //  Description: Returns true if get_state_def() has ever been called
00613 //               for the indicated state (thus defining a render
00614 //               subgraph for this state index), false otherwise.
00615 ////////////////////////////////////////////////////////////////////
00616 bool PGItem::
00617 has_state_def(int state) const {
00618   if (state < 0 || state >= (int)_state_defs.size()) {
00619     return false;
00620   }
00621   return (!_state_defs[state]._root.is_empty());
00622 }
00623 
00624 ////////////////////////////////////////////////////////////////////
00625 //     Function: PGItem::clear_state_def
00626 //       Access: Published
00627 //  Description: Resets the NodePath assigned to the indicated state
00628 //               to its initial default, with only a frame
00629 //               representation if appropriate.
00630 ////////////////////////////////////////////////////////////////////
00631 void PGItem::
00632 clear_state_def(int state) {
00633   if (state < 0 || state >= (int)_state_defs.size()) {
00634     return;
00635   }
00636 
00637   _state_defs[state]._root = NodePath();
00638   _state_defs[state]._frame = NodePath();
00639   _state_defs[state]._frame_stale = true;
00640 
00641   mark_bound_stale();
00642 }
00643 
00644 ////////////////////////////////////////////////////////////////////
00645 //     Function: PGItem::get_state_def
00646 //       Access: Published
00647 //  Description: Returns the Node that is the root of the subgraph
00648 //               that will be drawn when the PGItem is in the
00649 //               indicated state.  The first time this is called for a
00650 //               particular state index, it may create the Node.
00651 ////////////////////////////////////////////////////////////////////
00652 NodePath &PGItem::
00653 get_state_def(int state) {
00654   nassertr(state >= 0 && state < 1000, get_state_def(0));  // Sanity check.
00655   slot_state_def(state);
00656 
00657   if (_state_defs[state]._root.is_empty()) {
00658     // Create a new node.
00659     _state_defs[state]._root = NodePath("state_" + format_string(state));
00660     _state_defs[state]._frame_stale = true;
00661   }
00662 
00663   if (_state_defs[state]._frame_stale) {
00664     update_frame(state);
00665   }
00666 
00667   return _state_defs[state]._root;
00668 }
00669 
00670 ////////////////////////////////////////////////////////////////////
00671 //     Function: PGItem::instance_to_state_def
00672 //       Access: Published
00673 //  Description: Parents an instance of the bottom node of the
00674 //               indicated NodePath to the indicated state index.
00675 ////////////////////////////////////////////////////////////////////
00676 NodePath PGItem::
00677 instance_to_state_def(int state, const NodePath &path) {
00678   if (path.is_empty()) {
00679     // If the source is empty, quietly do nothing.
00680     return NodePath();
00681   }
00682 
00683   mark_bound_stale();
00684 
00685   return path.instance_to(get_state_def(state));
00686 }
00687 
00688 ////////////////////////////////////////////////////////////////////
00689 //     Function: PGItem::get_frame_style
00690 //       Access: Published
00691 //  Description: Returns the kind of frame that will be drawn behind
00692 //               the item when it is in the indicated state.
00693 ////////////////////////////////////////////////////////////////////
00694 PGFrameStyle PGItem::
00695 get_frame_style(int state) {
00696   if (state < 0 || state >= (int)_state_defs.size()) {
00697     return PGFrameStyle();
00698   }
00699   return _state_defs[state]._frame_style;
00700 }
00701 
00702 ////////////////////////////////////////////////////////////////////
00703 //     Function: PGItem::set_frame_style
00704 //       Access: Published
00705 //  Description: Changes the kind of frame that will be drawn behind
00706 //               the item when it is in the indicated state.
00707 ////////////////////////////////////////////////////////////////////
00708 void PGItem::
00709 set_frame_style(int state, const PGFrameStyle &style) {
00710   // Get the state def node, mainly to ensure that this state is
00711   // slotted and listed as having been defined.
00712   NodePath &root = get_state_def(state);
00713   nassertv(!root.is_empty());
00714 
00715   _state_defs[state]._frame_style = style;
00716   _state_defs[state]._frame_stale = true;
00717 
00718   mark_bound_stale();
00719 }
00720 
00721 #ifdef HAVE_AUDIO
00722 ////////////////////////////////////////////////////////////////////
00723 //     Function: PGItem::set_sound
00724 //       Access: Published
00725 //  Description: Sets the sound that will be played whenever the
00726 //               indicated event occurs.
00727 ////////////////////////////////////////////////////////////////////
00728 void PGItem::
00729 set_sound(const string &event, AudioSound *sound) {
00730   _sounds[event] = sound;
00731 }
00732 
00733 ////////////////////////////////////////////////////////////////////
00734 //     Function: PGItem::clear_sound
00735 //       Access: Published
00736 //  Description: Removes the sound associated with the indicated
00737 //               event.
00738 ////////////////////////////////////////////////////////////////////
00739 void PGItem::
00740 clear_sound(const string &event) {
00741   _sounds.erase(event);
00742 }
00743 
00744 ////////////////////////////////////////////////////////////////////
00745 //     Function: PGItem::get_sound
00746 //       Access: Published
00747 //  Description: Returns the sound associated with the indicated
00748 //               event, or NULL if there is no associated sound.
00749 ////////////////////////////////////////////////////////////////////
00750 AudioSound *PGItem::
00751 get_sound(const string &event) const {
00752   Sounds::const_iterator si = _sounds.find(event);
00753   if (si != _sounds.end()) {
00754     return (*si).second;
00755   }
00756   return (AudioSound *)NULL;
00757 }
00758 
00759 ////////////////////////////////////////////////////////////////////
00760 //     Function: PGItem::has_sound
00761 //       Access: Published
00762 //  Description: Returns true if there is a sound associated with the
00763 //               indicated event, or false otherwise.
00764 ////////////////////////////////////////////////////////////////////
00765 bool PGItem::
00766 has_sound(const string &event) const {
00767   return (_sounds.count(event) != 0);
00768 }
00769 #endif  // HAVE_AUDIO
00770 
00771 ////////////////////////////////////////////////////////////////////
00772 //     Function: PGItem::get_text_node
00773 //       Access: Published, Static
00774 //  Description: Returns the TextNode object that will be used by all
00775 //               PGItems to generate default labels given a string.
00776 //               This can be loaded with the default font, etc.
00777 ////////////////////////////////////////////////////////////////////
00778 TextNode *PGItem::
00779 get_text_node() {
00780   if (_text_node == (TextNode *)NULL) {
00781     _text_node = new TextNode("pguiText");
00782     _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
00783 
00784     // The default TextNode is aligned to the left, for the
00785     // convenience of PGEntry.
00786     _text_node->set_align(TextNode::A_left);
00787   }
00788   return _text_node;
00789 }
00790 
00791 ////////////////////////////////////////////////////////////////////
00792 //     Function: PGItem::play_sound
00793 //       Access: Protected
00794 //  Description: Plays the sound associated with the indicated event,
00795 //               if there is one.
00796 ////////////////////////////////////////////////////////////////////
00797 void PGItem::
00798 play_sound(const string &event) {
00799 #ifdef HAVE_AUDIO
00800   Sounds::const_iterator si = _sounds.find(event);
00801   if (si != _sounds.end()) {
00802     AudioSound *sound = (*si).second;
00803     sound->play();
00804   }
00805 #endif  // HAVE_AUDIO
00806 }
00807 
00808 ////////////////////////////////////////////////////////////////////
00809 //     Function: PGItem::slot_state_def
00810 //       Access: Private
00811 //  Description: Ensures there is a slot in the array for the given
00812 //               state definition.
00813 ////////////////////////////////////////////////////////////////////
00814 void PGItem::
00815 slot_state_def(int state) {
00816   while (state >= (int)_state_defs.size()) {
00817     StateDef def;
00818     def._frame_stale = true;
00819     _state_defs.push_back(def);
00820   }
00821 }
00822 
00823 ////////////////////////////////////////////////////////////////////
00824 //     Function: PGItem::update_frame
00825 //       Access: Private
00826 //  Description: Generates a new instance of the frame geometry for
00827 //               the indicated state.
00828 ////////////////////////////////////////////////////////////////////
00829 void PGItem::
00830 update_frame(int state) {
00831   // First, remove the old frame geometry, if any.
00832   if (state >= 0 && state < (int)_state_defs.size()) {
00833     _state_defs[state]._frame.remove_node();
00834   }
00835 
00836   // We must turn off the stale flag first, before we call
00837   // get_state_def(), to prevent get_state_def() from being a
00838   // recursive call.
00839   _state_defs[state]._frame_stale = false;
00840 
00841   // Now create new frame geometry.
00842   if (has_frame()) {
00843     NodePath &root = get_state_def(state);
00844     _state_defs[state]._frame = 
00845       _state_defs[state]._frame_style.generate_into(root, _frame);
00846   }
00847 }
00848 
00849 ////////////////////////////////////////////////////////////////////
00850 //     Function: PGItem::mark_frames_stale
00851 //       Access: Private
00852 //  Description: Marks all the frames in all states stale, so that
00853 //               they will be regenerated the next time each state is
00854 //               requested.
00855 ////////////////////////////////////////////////////////////////////
00856 void PGItem::
00857 mark_frames_stale() {
00858   StateDefs::iterator di;
00859   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00860     // Remove the old frame, if any.
00861     (*di)._frame.remove_node();
00862     (*di)._frame_stale = true;
00863   }
00864   mark_bound_stale();
00865 }

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