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 ©) : 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m, 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 ¶m, 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 ¶m, 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 }