00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "pgItem.h"
00016 #include "pgMouseWatcherParameter.h"
00017 #include "pgCullTraverser.h"
00018 #include "config_pgui.h"
00019 #include "boundingVolume.h"
00020 #include "pandaNode.h"
00021 #include "sceneGraphReducer.h"
00022 #include "throw_event.h"
00023 #include "string_utils.h"
00024 #include "nodePath.h"
00025 #include "cullTraverser.h"
00026 #include "cullTraverserData.h"
00027 #include "cullBinManager.h"
00028 #include "clipPlaneAttrib.h"
00029 #include "scissorAttrib.h"
00030 #include "dcast.h"
00031 #include "boundingSphere.h"
00032 #include "boundingBox.h"
00033 #include "config_mathutil.h"
00034
00035 #ifdef HAVE_AUDIO
00036 #include "audioSound.h"
00037 #endif
00038
00039 TypeHandle PGItem::_type_handle;
00040 PT(TextNode) PGItem::_text_node;
00041 PGItem *PGItem::_focus_item = (PGItem *)NULL;
00042 PGItem::BackgroundFocus PGItem::_background_focus;
00043
00044
00045
00046
00047
00048
00049 INLINE bool
00050 is_right(const LVector2 &v1, const LVector2 &v2) {
00051 return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
00052 }
00053
00054
00055
00056
00057
00058
00059 PGItem::
00060 PGItem(const string &name) :
00061 PandaNode(name),
00062 _lock(name)
00063 {
00064 set_cull_callback();
00065
00066 _notify = NULL;
00067 _has_frame = false;
00068 _frame.set(0, 0, 0, 0);
00069 _region = new PGMouseWatcherRegion(this);
00070 _state = 0;
00071 _flags = 0;
00072 }
00073
00074
00075
00076
00077
00078
00079 PGItem::
00080 ~PGItem() {
00081 if (_notify != (PGItemNotify *)NULL) {
00082 _notify->remove_item(this);
00083 _notify = NULL;
00084 }
00085
00086 nassertv(_region->_item == this);
00087 _region->_item = (PGItem *)NULL;
00088
00089 set_background_focus(false);
00090 if (_focus_item == this) {
00091 _focus_item = (PGItem *)NULL;
00092 }
00093 }
00094
00095
00096
00097
00098
00099
00100 PGItem::
00101 PGItem(const PGItem ©) :
00102 PandaNode(copy),
00103 _has_frame(copy._has_frame),
00104 _frame(copy._frame),
00105 _state(copy._state),
00106 _flags(copy._flags)
00107 #ifdef HAVE_AUDIO
00108 , _sounds(copy._sounds)
00109 #endif
00110 {
00111 _notify = NULL;
00112 _region = new PGMouseWatcherRegion(this);
00113
00114
00115
00116
00117 _region->set_name(copy._region->get_name());
00118
00119
00120 size_t num_state_defs = copy._state_defs.size();
00121 _state_defs.reserve(num_state_defs);
00122 for (size_t i = 0; i < num_state_defs; ++i) {
00123
00124
00125
00126 StateDef &old_sd = (StateDef &)(copy._state_defs[i]);
00127 old_sd._frame.remove_node();
00128 old_sd._frame_stale = true;
00129
00130 StateDef new_sd;
00131 new_sd._root = old_sd._root.copy_to(NodePath());
00132 new_sd._frame_style = old_sd._frame_style;
00133
00134 _state_defs.push_back(new_sd);
00135 }
00136 }
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 PandaNode *PGItem::
00147 make_copy() const {
00148 LightReMutexHolder holder(_lock);
00149 return new PGItem(*this);
00150 }
00151
00152
00153
00154
00155
00156
00157
00158
00159 void PGItem::
00160 transform_changed() {
00161 LightReMutexHolder holder(_lock);
00162 PandaNode::transform_changed();
00163 if (has_notify()) {
00164 get_notify()->item_transform_changed(this);
00165 }
00166 }
00167
00168
00169
00170
00171
00172
00173
00174
00175 void PGItem::
00176 draw_mask_changed() {
00177 LightReMutexHolder holder(_lock);
00178 PandaNode::draw_mask_changed();
00179 if (has_notify()) {
00180 get_notify()->item_draw_mask_changed(this);
00181 }
00182 }
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209 bool PGItem::
00210 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00211 LightReMutexHolder holder(_lock);
00212 bool this_node_hidden = data.is_this_node_hidden(trav);
00213 if (!this_node_hidden && has_frame() && get_active()) {
00214
00215
00216
00217
00218
00219
00220 if (trav->is_exact_type(PGCullTraverser::get_class_type())) {
00221 PGCullTraverser *pg_trav;
00222 DCAST_INTO_R(pg_trav, trav, true);
00223
00224 CPT(TransformState) net_transform = data.get_net_transform(trav);
00225 const LMatrix4 &transform = net_transform->get_mat();
00226
00227
00228
00229
00230 int bin_index = data._state->get_bin_index();
00231 int sort;
00232
00233 CullBinManager *bin_manager = CullBinManager::get_global_ptr();
00234 CullBinManager::BinType bin_type = bin_manager->get_bin_type(bin_index);
00235 if (bin_type == CullBinManager::BT_fixed) {
00236
00237
00238 sort = data._state->get_draw_order();
00239
00240 } else if (bin_type == CullBinManager::BT_unsorted) {
00241
00242
00243 sort = pg_trav->_sort_index;
00244 pg_trav->_sort_index++;
00245
00246 } else {
00247
00248 sort = 0;
00249 }
00250
00251
00252
00253 int bin_sort = bin_manager->get_bin_sort(data._state->get_bin_index());
00254
00255
00256
00257
00258
00259
00260
00261 sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
00262
00263 if (activate_region(transform, sort,
00264 DCAST(ClipPlaneAttrib, data._state->get_attrib(ClipPlaneAttrib::get_class_slot())),
00265 DCAST(ScissorAttrib, data._state->get_attrib(ScissorAttrib::get_class_slot())))) {
00266 pg_trav->_top->add_region(get_region());
00267 }
00268 }
00269 }
00270
00271 if (has_state_def(get_state())) {
00272
00273
00274 NodePath &root = get_state_def(get_state());
00275 CullTraverserData next_data(data, root.node());
00276 trav->traverse(next_data);
00277 }
00278
00279
00280 return true;
00281 }
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293 bool PGItem::
00294 is_renderable() const {
00295 return true;
00296 }
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306 void PGItem::
00307 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
00308 int &internal_vertices,
00309 int pipeline_stage,
00310 Thread *current_thread) const {
00311 LightReMutexHolder holder(_lock, current_thread);
00312 int num_vertices = 0;
00313
00314
00315 PT(BoundingVolume) bound;
00316
00317 BoundingVolume::BoundsType btype = get_bounds_type();
00318 if (btype == BoundingVolume::BT_default) {
00319 btype = bounds_type;
00320 }
00321
00322 if (btype == BoundingVolume::BT_sphere) {
00323 bound = new BoundingSphere;
00324 } else {
00325 bound = new BoundingBox;
00326 }
00327
00328
00329
00330 pvector<const BoundingVolume *> child_volumes;
00331
00332
00333
00334
00335 for (int i = 0; i < (int)_state_defs.size(); i++) {
00336 NodePath &root = ((PGItem *)this)->get_state_def(i);
00337 if (!root.is_empty()) {
00338 PandaNode *node = root.node();
00339 child_volumes.push_back(node->get_bounds(current_thread));
00340 num_vertices += node->get_nested_vertices(current_thread);
00341 }
00342 }
00343
00344 const BoundingVolume **child_begin = &child_volumes[0];
00345 const BoundingVolume **child_end = child_begin + child_volumes.size();
00346
00347 bound->around(child_begin, child_end);
00348
00349 internal_bounds = bound;
00350 internal_vertices = num_vertices;
00351 }
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 void PGItem::
00362 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
00363 GeomTransformer &transformer, Thread *current_thread) {
00364 LightReMutexHolder holder(_lock);
00365 StateDefs::iterator di;
00366 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00367 NodePath &root = (*di)._root;
00368 if (!root.is_empty()) {
00369 PandaNode *child = root.node();
00370 CPT(RenderState) child_state = node_state->compose(child->get_state());
00371 child->r_prepare_scene(gsg, child_state, transformer, current_thread);
00372 }
00373 }
00374
00375 PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
00376 }
00377
00378
00379
00380
00381
00382
00383
00384
00385 void PGItem::
00386 xform(const LMatrix4 &mat) {
00387 LightReMutexHolder holder(_lock);
00388
00389 LPoint3 ll(_frame[0], 0.0f, _frame[2]);
00390 LPoint3 ur(_frame[1], 0.0f, _frame[3]);
00391 ll = ll * mat;
00392 ur = ur * mat;
00393 _frame.set(ll[0], ur[0], ll[2], ur[2]);
00394
00395
00396 StateDefs::iterator di;
00397 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
00398 NodePath &root = (*di)._root;
00399
00400 root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
00401
00402
00403 SceneGraphReducer gr;
00404 gr.apply_attribs(root.node());
00405
00406
00407 if ((*di)._frame_style.xform(mat)) {
00408 (*di)._frame_stale = true;
00409 }
00410 }
00411 mark_internal_bounds_stale();
00412 }
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423 bool PGItem::
00424 activate_region(const LMatrix4 &transform, int sort,
00425 const ClipPlaneAttrib *cpa,
00426 const ScissorAttrib *sa) {
00427 LightReMutexHolder holder(_lock);
00428
00429
00430 LPoint3 ll = LPoint3::rfu(_frame[0], 0.0f, _frame[2]) * transform;
00431 LPoint3 lr = LPoint3::rfu(_frame[1], 0.0f, _frame[2]) * transform;
00432 LPoint3 ul = LPoint3::rfu(_frame[0], 0.0f, _frame[3]) * transform;
00433 LPoint3 ur = LPoint3::rfu(_frame[1], 0.0f, _frame[3]) * transform;
00434 LVector3 up = LVector3::up();
00435 int up_axis;
00436 if (up[1]) {
00437 up_axis = 1;
00438 }
00439 else if (up[2]) {
00440 up_axis = 2;
00441 }
00442 else {
00443 up_axis = 0;
00444 }
00445 LVector3 right = LVector3::right();
00446 int right_axis;
00447 if (right[0]) {
00448 right_axis = 0;
00449 }
00450 else if (right[2]) {
00451 right_axis = 2;
00452 }
00453 else {
00454 right_axis = 1;
00455 }
00456
00457 LVecBase4 frame;
00458 if (cpa != (ClipPlaneAttrib *)NULL && cpa->get_num_on_planes() != 0) {
00459
00460
00461
00462 ClipPoints points;
00463 points.reserve(4);
00464 points.push_back(LPoint2(ll[right_axis], ll[up_axis]));
00465 points.push_back(LPoint2(lr[right_axis], lr[up_axis]));
00466 points.push_back(LPoint2(ur[right_axis], ur[up_axis]));
00467 points.push_back(LPoint2(ul[right_axis], ul[up_axis]));
00468
00469 int num_on_planes = cpa->get_num_on_planes();
00470 for (int i = 0; i < num_on_planes; ++i) {
00471 NodePath plane_path = cpa->get_on_plane(i);
00472 LPlane plane = DCAST(PlaneNode, plane_path.node())->get_plane();
00473 plane.xform(plane_path.get_net_transform()->get_mat());
00474
00475
00476
00477
00478 clip_frame(points, plane);
00479 }
00480
00481 if (points.empty()) {
00482
00483 return false;
00484 }
00485
00486 ClipPoints::iterator pi;
00487 pi = points.begin();
00488 frame.set((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
00489 ++pi;
00490 while (pi != points.end()) {
00491 frame[0] = min(frame[0], (*pi)[0]);
00492 frame[1] = max(frame[1], (*pi)[0]);
00493 frame[2] = min(frame[2], (*pi)[1]);
00494 frame[3] = max(frame[3], (*pi)[1]);
00495 ++pi;
00496 }
00497 } else {
00498
00499 frame.set(min(min(ll[right_axis], lr[right_axis]), min(ul[right_axis], ur[right_axis])),
00500 max(max(ll[right_axis], lr[right_axis]), max(ul[right_axis], ur[right_axis])),
00501 min(min(ll[up_axis], lr[up_axis]), min(ul[up_axis], ur[up_axis])),
00502 max(max(ll[up_axis], lr[up_axis]), max(ul[up_axis], ur[up_axis])));
00503 }
00504
00505 if (sa != (ScissorAttrib *)NULL) {
00506
00507 const LVecBase4 &sf = sa->get_frame();
00508
00509 frame.set(max(frame[0], sf[0] * 2.0f - 1.0f),
00510 min(frame[1], sf[1] * 2.0f - 1.0f),
00511 max(frame[2], sf[2] * 2.0f - 1.0f),
00512 min(frame[3], sf[3] * 2.0f - 1.0f));
00513 if (frame[1] <= frame[0] || frame[3] <= frame[2]) {
00514
00515 return false;
00516 }
00517 }
00518
00519 _region->set_frame(frame);
00520
00521 _region->set_sort(sort);
00522 _region->set_active(true);
00523
00524
00525
00526 _frame_inv_xform.invert_from(transform);
00527
00528 return true;
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540 void PGItem::
00541 enter_region(const MouseWatcherParameter ¶m) {
00542 LightReMutexHolder holder(_lock);
00543 if (pgui_cat.is_debug()) {
00544 pgui_cat.debug()
00545 << *this << "::enter_region(" << param << ")\n";
00546 }
00547
00548 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00549 string event = get_enter_event();
00550 play_sound(event);
00551 throw_event(event, EventParameter(ep));
00552
00553 if (has_notify()) {
00554 get_notify()->item_enter(this, param);
00555 }
00556 }
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567 void PGItem::
00568 exit_region(const MouseWatcherParameter ¶m) {
00569 LightReMutexHolder holder(_lock);
00570 if (pgui_cat.is_debug()) {
00571 pgui_cat.debug()
00572 << *this << "::exit_region(" << param << ")\n";
00573 }
00574
00575 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00576 string event = get_exit_event();
00577 play_sound(event);
00578 throw_event(event, EventParameter(ep));
00579
00580 if (has_notify()) {
00581 get_notify()->item_exit(this, param);
00582 }
00583
00584
00585 }
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597 void PGItem::
00598 within_region(const MouseWatcherParameter ¶m) {
00599 LightReMutexHolder holder(_lock);
00600 if (pgui_cat.is_debug()) {
00601 pgui_cat.debug()
00602 << *this << "::within_region(" << param << ")\n";
00603 }
00604
00605 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00606 string event = get_within_event();
00607 play_sound(event);
00608 throw_event(event, EventParameter(ep));
00609
00610 if (has_notify()) {
00611 get_notify()->item_within(this, param);
00612 }
00613 }
00614
00615
00616
00617
00618
00619
00620
00621
00622 void PGItem::
00623 without_region(const MouseWatcherParameter ¶m) {
00624 LightReMutexHolder holder(_lock);
00625 if (pgui_cat.is_debug()) {
00626 pgui_cat.debug()
00627 << *this << "::without_region(" << param << ")\n";
00628 }
00629
00630 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00631 string event = get_without_event();
00632 play_sound(event);
00633 throw_event(event, EventParameter(ep));
00634
00635 if (has_notify()) {
00636 get_notify()->item_without(this, param);
00637 }
00638 }
00639
00640
00641
00642
00643
00644
00645
00646 void PGItem::
00647 focus_in() {
00648 LightReMutexHolder holder(_lock);
00649 if (pgui_cat.is_debug()) {
00650 pgui_cat.debug()
00651 << *this << "::focus_in()\n";
00652 }
00653
00654 string event = get_focus_in_event();
00655 play_sound(event);
00656 throw_event(event);
00657
00658 if (has_notify()) {
00659 get_notify()->item_focus_in(this);
00660 }
00661 }
00662
00663
00664
00665
00666
00667
00668
00669 void PGItem::
00670 focus_out() {
00671 LightReMutexHolder holder(_lock);
00672 if (pgui_cat.is_debug()) {
00673 pgui_cat.debug()
00674 << *this << "::focus_out()\n";
00675 }
00676
00677 string event = get_focus_out_event();
00678 play_sound(event);
00679 throw_event(event);
00680
00681 if (has_notify()) {
00682 get_notify()->item_focus_out(this);
00683 }
00684 }
00685
00686
00687
00688
00689
00690
00691
00692
00693 void PGItem::
00694 press(const MouseWatcherParameter ¶m, bool background) {
00695 LightReMutexHolder holder(_lock);
00696 if (pgui_cat.is_debug()) {
00697 pgui_cat.debug()
00698 << *this << "::press(" << param << ", " << background << ")\n";
00699 }
00700
00701 if (!background) {
00702 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00703 string event;
00704 if (param.is_keyrepeat()) {
00705 event = get_repeat_event(param.get_button());
00706 } else {
00707 event = get_press_event(param.get_button());
00708 }
00709 play_sound(event);
00710 throw_event(event, EventParameter(ep));
00711 }
00712
00713 if (has_notify()) {
00714 get_notify()->item_press(this, param);
00715 }
00716 }
00717
00718
00719
00720
00721
00722
00723
00724
00725 void PGItem::
00726 release(const MouseWatcherParameter ¶m, bool background) {
00727 LightReMutexHolder holder(_lock);
00728 if (pgui_cat.is_debug()) {
00729 pgui_cat.debug()
00730 << *this << "::release(" << param << ", " << background << ")\n";
00731 }
00732
00733 if (!background) {
00734 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00735 string event = get_release_event(param.get_button());
00736 play_sound(event);
00737 throw_event(event, EventParameter(ep));
00738 }
00739
00740 if (has_notify()) {
00741 get_notify()->item_release(this, param);
00742 }
00743 }
00744
00745
00746
00747
00748
00749
00750
00751 void PGItem::
00752 keystroke(const MouseWatcherParameter ¶m, bool background) {
00753 LightReMutexHolder holder(_lock);
00754 if (pgui_cat.is_debug()) {
00755 pgui_cat.debug()
00756 << *this << "::keystroke(" << param << ", " << background << ")\n";
00757 }
00758
00759 if (!background) {
00760 PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
00761 string event = get_keystroke_event();
00762 play_sound(event);
00763 throw_event(event, EventParameter(ep));
00764
00765 if (has_notify()) {
00766 get_notify()->item_keystroke(this, param);
00767 }
00768 }
00769 }
00770
00771
00772
00773
00774
00775
00776
00777 void PGItem::
00778 candidate(const MouseWatcherParameter ¶m, bool background) {
00779 LightReMutexHolder holder(_lock);
00780 if (pgui_cat.is_debug()) {
00781 pgui_cat.debug()
00782 << *this << "::candidate(" << param << ", " << background << ")\n";
00783 }
00784
00785
00786 if (!background) {
00787 if (has_notify()) {
00788 get_notify()->item_candidate(this, param);
00789 }
00790 }
00791 }
00792
00793
00794
00795
00796
00797
00798
00799 void PGItem::
00800 move(const MouseWatcherParameter ¶m) {
00801 LightReMutexHolder holder(_lock);
00802 if (pgui_cat.is_debug()) {
00803 pgui_cat.debug()
00804 << *this << "::move(" << param << ")\n";
00805 }
00806
00807 if (has_notify()) {
00808 get_notify()->item_move(this, param);
00809 }
00810 }
00811
00812
00813
00814
00815
00816
00817
00818 void PGItem::
00819 background_press(const MouseWatcherParameter ¶m) {
00820 BackgroundFocus::const_iterator fi;
00821 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00822 PGItem *item = *fi;
00823 if (!item->get_focus()) {
00824 item->press(param, true);
00825 }
00826 }
00827 }
00828
00829
00830
00831
00832
00833
00834
00835 void PGItem::
00836 background_release(const MouseWatcherParameter ¶m) {
00837 BackgroundFocus::const_iterator fi;
00838 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00839 PGItem *item = *fi;
00840 if (!item->get_focus()) {
00841 item->release(param, true);
00842 }
00843 }
00844 }
00845
00846
00847
00848
00849
00850
00851
00852 void PGItem::
00853 background_keystroke(const MouseWatcherParameter ¶m) {
00854 BackgroundFocus::const_iterator fi;
00855 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00856 PGItem *item = *fi;
00857 if (!item->get_focus()) {
00858 item->keystroke(param, true);
00859 }
00860 }
00861 }
00862
00863
00864
00865
00866
00867
00868
00869 void PGItem::
00870 background_candidate(const MouseWatcherParameter ¶m) {
00871 BackgroundFocus::const_iterator fi;
00872 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
00873 PGItem *item = *fi;
00874 if (!item->get_focus()) {
00875 item->candidate(param, true);
00876 }
00877 }
00878 }
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889 void PGItem::
00890 set_active(bool active) {
00891 LightReMutexHolder holder(_lock);
00892 if (active) {
00893 _flags |= F_active;
00894 } else {
00895 _flags &= ~F_active;
00896
00897 if (get_focus()) {
00898 set_focus(false);
00899 }
00900 }
00901 }
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916 void PGItem::
00917 set_focus(bool focus) {
00918 LightReMutexHolder holder(_lock);
00919 if (focus) {
00920 if (!get_active()) {
00921
00922 return;
00923 }
00924
00925
00926 if (_focus_item != this) {
00927 if (_focus_item != (PGItem *)NULL) {
00928
00929 _focus_item->set_focus(false);
00930 }
00931 _focus_item = this;
00932 }
00933 if (!get_focus()) {
00934 focus_in();
00935 _flags |= F_focus;
00936 }
00937
00938 } else {
00939 if (_focus_item == this) {
00940
00941 _focus_item = (PGItem *)NULL;
00942 }
00943
00944 if (get_focus()) {
00945 focus_out();
00946 _flags &= ~F_focus;
00947 }
00948 }
00949 _region->set_keyboard(focus);
00950 }
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961 void PGItem::
00962 set_background_focus(bool focus) {
00963 LightReMutexHolder holder(_lock);
00964 if (focus != get_background_focus()) {
00965 if (focus) {
00966
00967 _flags |= F_background_focus;
00968 bool inserted = _background_focus.insert(this).second;
00969 nassertv(inserted);
00970
00971 } else {
00972
00973 _flags &= ~F_background_focus;
00974 size_t num_erased = _background_focus.erase(this);
00975 nassertv(num_erased == 1);
00976 }
00977 }
00978 }
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991 int PGItem::
00992 get_num_state_defs() const {
00993 LightReMutexHolder holder(_lock);
00994 return _state_defs.size();
00995 }
00996
00997
00998
00999
01000
01001
01002
01003
01004 bool PGItem::
01005 has_state_def(int state) const {
01006 LightReMutexHolder holder(_lock);
01007 if (state < 0 || state >= (int)_state_defs.size()) {
01008 return false;
01009 }
01010 return (!_state_defs[state]._root.is_empty());
01011 }
01012
01013
01014
01015
01016
01017
01018
01019
01020 void PGItem::
01021 clear_state_def(int state) {
01022 LightReMutexHolder holder(_lock);
01023 if (state < 0 || state >= (int)_state_defs.size()) {
01024 return;
01025 }
01026
01027 _state_defs[state]._root = NodePath();
01028 _state_defs[state]._frame = NodePath();
01029 _state_defs[state]._frame_stale = true;
01030
01031 mark_internal_bounds_stale();
01032 }
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042 NodePath &PGItem::
01043 get_state_def(int state) {
01044 LightReMutexHolder holder(_lock);
01045 nassertr(state >= 0 && state < 1000, get_state_def(0));
01046 slot_state_def(state);
01047
01048 if (_state_defs[state]._root.is_empty()) {
01049
01050 _state_defs[state]._root = NodePath("state_" + format_string(state));
01051 _state_defs[state]._frame_stale = true;
01052 }
01053
01054 if (_state_defs[state]._frame_stale) {
01055 update_frame(state);
01056 }
01057
01058 return _state_defs[state]._root;
01059 }
01060
01061
01062
01063
01064
01065
01066
01067 NodePath PGItem::
01068 instance_to_state_def(int state, const NodePath &path) {
01069 LightReMutexHolder holder(_lock);
01070 if (path.is_empty()) {
01071
01072 return NodePath();
01073 }
01074
01075 mark_internal_bounds_stale();
01076
01077 return path.instance_to(get_state_def(state));
01078 }
01079
01080
01081
01082
01083
01084
01085
01086 PGFrameStyle PGItem::
01087 get_frame_style(int state) {
01088 LightReMutexHolder holder(_lock);
01089 if (state < 0 || state >= (int)_state_defs.size()) {
01090 return PGFrameStyle();
01091 }
01092 return _state_defs[state]._frame_style;
01093 }
01094
01095
01096
01097
01098
01099
01100
01101 void PGItem::
01102 set_frame_style(int state, const PGFrameStyle &style) {
01103 LightReMutexHolder holder(_lock);
01104
01105
01106 NodePath &root = get_state_def(state);
01107 nassertv(!root.is_empty());
01108
01109 _state_defs[state]._frame_style = style;
01110 _state_defs[state]._frame_stale = true;
01111
01112 mark_internal_bounds_stale();
01113 }
01114
01115 #ifdef HAVE_AUDIO
01116
01117
01118
01119
01120
01121
01122 void PGItem::
01123 set_sound(const string &event, AudioSound *sound) {
01124 LightReMutexHolder holder(_lock);
01125 _sounds[event] = sound;
01126 }
01127
01128
01129
01130
01131
01132
01133
01134 void PGItem::
01135 clear_sound(const string &event) {
01136 LightReMutexHolder holder(_lock);
01137 _sounds.erase(event);
01138 }
01139
01140
01141
01142
01143
01144
01145
01146 AudioSound *PGItem::
01147 get_sound(const string &event) const {
01148 LightReMutexHolder holder(_lock);
01149 Sounds::const_iterator si = _sounds.find(event);
01150 if (si != _sounds.end()) {
01151 return (*si).second;
01152 }
01153 return (AudioSound *)NULL;
01154 }
01155
01156
01157
01158
01159
01160
01161
01162 bool PGItem::
01163 has_sound(const string &event) const {
01164 LightReMutexHolder holder(_lock);
01165 return (_sounds.count(event) != 0);
01166 }
01167 #endif // HAVE_AUDIO
01168
01169
01170
01171
01172
01173
01174
01175
01176 TextNode *PGItem::
01177 get_text_node() {
01178 if (_text_node == (TextNode *)NULL) {
01179 _text_node = new TextNode("pguiText");
01180 _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
01181
01182
01183
01184 _text_node->set_align(TextNode::A_left);
01185 }
01186 return _text_node;
01187 }
01188
01189
01190
01191
01192
01193
01194
01195 void PGItem::
01196 play_sound(const string &event) {
01197 #ifdef HAVE_AUDIO
01198 LightReMutexHolder holder(_lock);
01199 Sounds::const_iterator si = _sounds.find(event);
01200 if (si != _sounds.end()) {
01201 AudioSound *sound = (*si).second;
01202 sound->play();
01203 }
01204 #endif // HAVE_AUDIO
01205 }
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223 void PGItem::
01224 reduce_region(LVecBase4 &frame, PGItem *obscurer) const {
01225 if (obscurer != (PGItem *)NULL && !obscurer->is_overall_hidden()) {
01226 LVecBase4 oframe = get_relative_frame(obscurer);
01227
01228
01229
01230 LVecBase4 right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
01231 LVecBase4 left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
01232 LVecBase4 above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
01233 LVecBase4 below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
01234
01235
01236 const LVecBase4 *largest = &right;
01237 PN_stdfloat largest_area = compute_area(*largest);
01238 compare_largest(largest, largest_area, &left);
01239 compare_largest(largest, largest_area, &above);
01240 compare_largest(largest, largest_area, &below);
01241
01242 frame = *largest;
01243 }
01244 }
01245
01246
01247
01248
01249
01250
01251
01252
01253 LVecBase4 PGItem::
01254 get_relative_frame(PGItem *item) const {
01255 NodePath this_np = NodePath::any_path((PGItem *)this);
01256 NodePath item_np = this_np.find_path_to(item);
01257 if (item_np.is_empty()) {
01258 item_np = NodePath::any_path(item);
01259 }
01260 const LVecBase4 &orig_frame = item->get_frame();
01261 LMatrix4 transform = item_np.get_mat(this_np);
01262
01263
01264
01265
01266
01267 LPoint3 ll(orig_frame[0], 0.0f, orig_frame[2]);
01268 LPoint3 lr(orig_frame[1], 0.0f, orig_frame[2]);
01269 LPoint3 ul(orig_frame[0], 0.0f, orig_frame[3]);
01270 LPoint3 ur(orig_frame[1], 0.0f, orig_frame[3]);
01271 ll = ll * transform;
01272 lr = lr * transform;
01273 ul = ul * transform;
01274 ur = ur * transform;
01275
01276 return LVecBase4(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
01277 max(max(ll[0], lr[0]), max(ul[0], ur[0])),
01278 min(min(ll[2], lr[2]), min(ul[2], ur[2])),
01279 max(max(ll[2], lr[2]), max(ul[2], ur[2])));
01280 }
01281
01282
01283
01284
01285
01286
01287
01288 LPoint3 PGItem::
01289 mouse_to_local(const LPoint2 &mouse_point) const {
01290
01291
01292 NodePath this_np((PGItem *)this);
01293 CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
01294 return inv_transform->get_mat().xform_point(LVector3::rfu(mouse_point[0], 0, mouse_point[1]));
01295 }
01296
01297
01298
01299
01300
01301
01302 void PGItem::
01303 frame_changed() {
01304 mark_frames_stale();
01305 if (has_notify()) {
01306 get_notify()->item_frame_changed(this);
01307 }
01308 }
01309
01310
01311
01312
01313
01314
01315
01316 void PGItem::
01317 slot_state_def(int state) {
01318 while (state >= (int)_state_defs.size()) {
01319 _state_defs.push_back(StateDef());
01320 }
01321 }
01322
01323
01324
01325
01326
01327
01328
01329 void PGItem::
01330 update_frame(int state) {
01331
01332 if (state >= 0 && state < (int)_state_defs.size()) {
01333 _state_defs[state]._frame.remove_node();
01334 }
01335
01336
01337
01338
01339 _state_defs[state]._frame_stale = false;
01340
01341
01342 if (has_frame()) {
01343 NodePath &root = get_state_def(state);
01344 _state_defs[state]._frame =
01345 _state_defs[state]._frame_style.generate_into(root, _frame);
01346 }
01347 }
01348
01349
01350
01351
01352
01353
01354
01355
01356 void PGItem::
01357 mark_frames_stale() {
01358 StateDefs::iterator di;
01359 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
01360
01361 (*di)._frame.remove_node();
01362 (*di)._frame_stale = true;
01363 }
01364 mark_internal_bounds_stale();
01365 }
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378 bool PGItem::
01379 clip_frame(ClipPoints &source_points, const LPlane &plane) const {
01380 if (source_points.empty()) {
01381 return true;
01382 }
01383
01384 LPoint3 from3d;
01385 LVector3 delta3d;
01386 if (!plane.intersects_plane(from3d, delta3d, LPlane(LVector3(0, 1, 0), LPoint3::zero()))) {
01387
01388
01389 if (plane.dist_to_plane(LPoint3::zero()) < 0.0) {
01390
01391
01392 return true;
01393 }
01394 return false;
01395 }
01396
01397
01398
01399 LPoint2 from2d(from3d[0], from3d[2]);
01400 LVector2 delta2d(delta3d[0], delta3d[2]);
01401
01402 PN_stdfloat a = -delta2d[1];
01403 PN_stdfloat b = delta2d[0];
01404 PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
01405
01406
01407
01408
01409
01410
01411
01412
01413 ClipPoints new_points;
01414 new_points.reserve(source_points.size() + 1);
01415
01416 LPoint2 last_point(source_points.back());
01417 bool last_is_in = is_right(last_point - from2d, delta2d);
01418 bool all_in = last_is_in;
01419 ClipPoints::const_iterator pi;
01420 for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
01421 LPoint2 this_point(*pi);
01422 bool this_is_in = is_right(this_point - from2d, delta2d);
01423
01424
01425
01426 bool crossed_over = (this_is_in != last_is_in);
01427 if (crossed_over) {
01428
01429
01430 LVector2 d = this_point - last_point;
01431 PN_stdfloat denom = (a * d[0] + b * d[1]);
01432 if (denom != 0.0) {
01433 PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
01434 LPoint2 p = last_point + t * d;
01435
01436 new_points.push_back(p);
01437 last_is_in = this_is_in;
01438 }
01439 }
01440
01441 if (this_is_in) {
01442
01443 new_points.push_back(this_point);
01444 } else {
01445 all_in = false;
01446 }
01447
01448 last_point = this_point;
01449 }
01450
01451 source_points.swap(new_points);
01452 return all_in;
01453 }