![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference cGuiCanvas.cppGo to the documentation of this file.00001 /************************************************************************ 00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library 00003 Copyright (C) 2003 Aaron Cameron 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 00019 A copy of the GNU Lesser General Public License has been provided with 00020 this library in the file 'COPYING'. 00021 00022 Contact information for the author of this library has been provided 00023 with this library in the file 'AUTHOR'. 00024 ************************************************************************/ 00025 #include "gui/cGuiCanvas.h" 00026 00027 #include "GL/gl.h" 00028 00029 #include "n2l/dynVars.h" 00030 #include "n2l/resourceManagement.h" 00031 00032 #include "gui/cGuiFactory.h" 00033 00034 #include <queue> 00035 using std::queue; 00036 00037 namespace n2l 00038 { 00039 00040 # ifdef N2L_PASSIVE_GUI_REG 00041 tBool cGuiCanvas::smRegistered = 00042 cGuiFactory::current().registerLoader( 00043 "n2l::cGuiCanvas", cGuiElement::loadNew<cGuiCanvas> ); 00044 # endif 00045 00046 /**************************************************************************/ 00047 cGuiCanvas::cGuiCanvas() : 00048 cGuiElement(), 00049 mInnerOffset(0.0f,0.0f), 00050 mClipping(false), 00051 mEnabled(true) 00052 { 00053 } 00054 00055 /**************************************************************************/ 00056 cGuiCanvas::cGuiCanvas( const tGuiPos &iPos, const tGuiPos &iSize ) : 00057 cGuiElement(iPos,iSize), 00058 mInnerOffset(0.0f,0.0f), 00059 mClipping(false), 00060 mEnabled(true) 00061 { 00062 } 00063 00064 /**************************************************************************/ 00065 cGuiCanvas::cGuiCanvas( const cVfsNodeInterface &iNode ) : 00066 cGuiElement(), 00067 mInnerOffset(0.0f,0.0f), 00068 mClipping(false), 00069 mEnabled(true) 00070 { 00071 load( iNode ); 00072 } 00073 00074 /**************************************************************************/ 00075 cGuiCanvas::cGuiCanvas( const cDynVar &iDefinition ) : 00076 cGuiElement(), 00077 mInnerOffset(0.0f,0.0f), 00078 mClipping(false), 00079 mEnabled(true) 00080 { 00081 load( iDefinition ); 00082 } 00083 00084 /**************************************************************************/ 00085 cGuiCanvas::~cGuiCanvas() 00086 { 00087 // Do this so that children will be unlinked 00088 clear(); 00089 } 00090 00091 /**************************************************************************/ 00092 void cGuiCanvas::load( const cVfsNodeInterface &iNode ) 00093 { 00094 cDynVar def; 00095 validateAndDecode( def, iNode, "n2l::cGuiCanvas" ); 00096 load( def ); 00097 } 00098 00099 /**************************************************************************/ 00100 void cGuiCanvas::load( const cDynVar &iDefinition ) 00101 { 00102 // Load parent properties 00103 cGuiElement::load( iDefinition ); 00104 00105 // Now my properties 00106 if (iDefinition.keyExists("members")) { 00107 cDynVar::tConstIterator pair( iDefinition["members"].begin() ); 00108 const cDynVar::tConstIterator LastPair = 00109 iDefinition["members"].end(); 00110 00111 for (; pair!=LastPair; ++pair) { 00112 if (!pair->second.isArray()) { 00113 add( pair->first, cResourceManager::getGuiElement( 00114 pair->second)->clone() ); 00115 } else { 00116 tString name; 00117 if (pair->second.keyExists("name")) 00118 name = tString(pair->second["name"]); 00119 else 00120 name = pair->first; 00121 if (pair->second.keyExists("type")) { 00122 add( name, cGuiFactory::current().load(pair->second) ); 00123 } else { 00124 add( name, cResourceManager::getGuiElement( 00125 pair->second["file"])->clone() ); 00126 } 00127 } 00128 } // For each pair 00129 } 00130 00131 if (iDefinition["innerOffset"]) 00132 innerOffset( tGuiPos( iDefinition["innerOffset"][0], 00133 iDefinition["innerOffset"][1]) ); 00134 00135 if (iDefinition.keyExists("clipping")) 00136 clipping( iDefinition["clipping"] ); 00137 00138 enabled( iDefinition.keyValueOr("enabled",true) ); 00139 } 00140 00141 /**************************************************************************/ 00142 void cGuiCanvas::add( const tGuiName &iName, 00143 const cAutoPtr<cGuiElement> &i_ioElement ) 00144 { 00145 if (i_ioElement.dumbPtr() == this) 00146 throw cBadDataUseException( "cGuiCanvas::add", 00147 "Added canvas to itself: \"" + iName + "\"" ); 00148 00149 const tUint NewID = mElements.size(); 00150 tElementLookup::value_type newValue(iName,NewID); 00151 if (i_ioElement->canvas()) 00152 throw cException( "cGuiCanvas::add", tString("Element: \"") + 00153 iName + "\" already belongs to a canvas: " + 00154 asString(tUint32(i_ioElement->canvas())) ); 00155 i_ioElement->canvas( this ); 00156 i_ioElement->name(iName); 00157 mElements.push_back(i_ioElement); 00158 mElementLookup.insert(newValue); 00159 if (i_ioElement->wantsFocus()) 00160 mFocusOrder.push_back(i_ioElement); 00161 00162 mRenderOrder[i_ioElement->renderOrder()].push_back( NewID ); 00163 } 00164 00165 /**************************************************************************/ 00166 const cAutoPtr<cGuiElement> cGuiCanvas::remove( const tGuiName &iName ) 00167 { 00168 cAutoPtr<cGuiElement> tmp = get( iName ); 00169 if (!tmp.isSet()) 00170 throw cOutOfBoundsException( "cGuiCanvas::remove", 00171 "There is no such entity to remove in this canvas.", 00172 iName ); 00173 00174 if (!tmp->canvas()) 00175 throw cBadDataUseException( "cGuiCanvas::remove", 00176 "The object requested was found but isn\'t in a canvas " 00177 "which is really, really bad news." ); 00178 00179 tmp->canvas()->_real_remove( iName ); 00180 tmp->canvas( 0 ); 00181 return tmp; 00182 } 00183 00184 /**************************************************************************/ 00185 void cGuiCanvas::clipping( const tBool iClip ) 00186 { 00187 mClipping = iClip; 00188 } 00189 00190 /**************************************************************************/ 00191 const tBool cGuiCanvas::clipping() const 00192 { 00193 return mClipping; 00194 } 00195 00196 /**************************************************************************/ 00197 void cGuiCanvas::innerOffset( const tGuiPos &iPos ) 00198 { 00199 mInnerOffset = iPos; 00200 } 00201 00202 00203 /**************************************************************************/ 00204 const tGuiPos &cGuiCanvas::innerOffset() const 00205 { 00206 return mInnerOffset; 00207 } 00208 00209 /**************************************************************************/ 00210 const cAutoPtr<cGuiElement> cGuiCanvas::get( const tGuiName &iName ) 00211 { 00212 tElementLookup::iterator element( mElementLookup.find(iName) ); 00213 if (element!=mElementLookup.end()) return mElements[element->second]; 00214 00215 // Scan through all the elements with children, and look in those 00216 for (tElementList::const_iterator it = mElements.begin(); 00217 it!=mElements.end(); ++it) 00218 { 00219 const cAutoPtr<cGuiElement> elementPtr( (*it)->get(iName) ); 00220 if (elementPtr.isSet()) return elementPtr; 00221 } 00222 return 0; 00223 } 00224 00225 /**************************************************************************/ 00226 cGuiElement::tOElementList cGuiCanvas::getByType( const type_info &iType ) 00227 { 00228 tOElementList tmp; 00229 return getByType( tmp, iType ); 00230 } 00231 00232 /**************************************************************************/ 00233 cGuiElement::tOElementList &cGuiCanvas::getByType( tOElementList &oList, 00234 const type_info &iType ) 00235 { 00236 for (tElementList::const_iterator it = mElements.begin(); 00237 it!=mElements.end(); ++it) 00238 { 00239 if (typeid(**it) == iType) 00240 oList.push_back( *it ); 00241 00242 (*it)->getByType( oList, iType ); 00243 } 00244 return oList; 00245 00246 } 00247 00248 /**************************************************************************/ 00249 void cGuiCanvas::clear() 00250 { 00251 // Unlink all children 00252 for (tElementList::iterator eIt = mElements.begin(); 00253 eIt!=mElements.end(); ++eIt) 00254 { 00255 (*eIt)->canvas(0); 00256 } 00257 00258 // Empty our reference lists 00259 mElements.clear(); 00260 mElementLookup.clear(); 00261 mFocusOrder.clear(); 00262 mRenderOrder.clear(); 00263 } 00264 00265 /**************************************************************************/ 00266 void cGuiCanvas::draw() const 00267 { 00268 if (!enabled()) return; 00269 glPushMatrix(); 00270 glPushAttrib( GL_ALL_ATTRIB_BITS ); 00271 if (clipping()) { 00272 glDepthMask( true ); 00273 glClearDepth(1.0f); 00274 glClear( GL_DEPTH_BUFFER_BIT ); 00275 glEnable( GL_DEPTH_TEST ); 00276 glColorMask( false,false,false,false ); 00277 glDepthFunc( GL_ALWAYS ); 00278 glBegin( GL_QUADS ); 00279 glVertex3f( innerPos().x(), innerPos().y(), 0 ); 00280 glVertex3f( innerPos().x()+innerSize().x(), innerPos().y(), 00281 0 ); 00282 glVertex3f( innerPos().x()+innerSize().x(), innerPos().y() + 00283 innerSize().y(), 0 ); 00284 glVertex3f( innerPos().x(),innerPos().y()+innerSize().y(), 0 ); 00285 glEnd(); 00286 glColorMask( true,true,true,true ); 00287 glDepthMask( false ); 00288 glDepthFunc( GL_EQUAL ); 00289 outerPos().glTranslatef(); 00290 innerOffset().glTranslatef(); 00291 00292 tGuiPos lrClip( outerSize() ); 00293 lrClip -= innerOffset(); 00294 tUint i = 0; 00295 00296 const tRenderOrder::const_iterator LastRGroup = mRenderOrder.end(); 00297 for (tRenderOrder::const_iterator rGroup = mRenderOrder.begin(); 00298 rGroup!=LastRGroup; ++rGroup) 00299 { 00300 const tElementIDList::const_iterator LastE = 00301 rGroup->second.end(); 00302 00303 for (tElementIDList::const_iterator eIt = 00304 rGroup->second.begin(); eIt!=LastE; ++eIt) 00305 { 00306 const cGuiElement &Element = *(mElements[*eIt]); 00307 tGuiPos ulClip( Element.pos() ); 00308 ulClip += Element.size(); 00309 ulClip += innerOffset(); 00310 if (Element.pos().x()>lrClip.x() || 00311 Element.pos().y()>lrClip.y() || 00312 ulClip.x()<0 || ulClip.y()<0 ) { 00313 ++i; 00314 continue; 00315 } 00316 Element.draw(); 00317 } 00318 } 00319 00320 } else { 00321 outerPos().glTranslatef(); 00322 innerOffset().glTranslatef(); 00323 const tRenderOrder::const_iterator LastRGroup = mRenderOrder.end(); 00324 for (tRenderOrder::const_iterator rGroup = mRenderOrder.begin(); 00325 rGroup!=LastRGroup; ++rGroup) 00326 { 00327 const tElementIDList::const_iterator LastE = 00328 rGroup->second.end(); 00329 00330 for (tElementIDList::const_iterator eIt = 00331 rGroup->second.begin(); eIt!=LastE; ++eIt) 00332 { 00333 mElements[*eIt]->draw(); 00334 } 00335 } 00336 } 00337 glPopAttrib(); 00338 glPopMatrix(); 00339 } 00340 00341 /**************************************************************************/ 00342 void cGuiCanvas::systemEvent( 00343 const cAutoPtr<const cEventInterface> &i_iEvent ) 00344 { 00345 if (!enabled()) return; 00346 cAutoPtr<const cEventInterface> modifedEvent; 00347 tBool inside( true ); 00348 switch (i_iEvent->type()) 00349 { 00350 //----------------------------------------------- 00351 // Warp the mouse location to this canvas 00352 case EventType_MouseButton: { 00353 const cAutoPtr<const cMouseButtonEvent> Button( i_iEvent ); 00354 cMouseButtonEvent *tempEvent = 00355 new cMouseButtonEvent( *Button ); 00356 tempEvent->pos( tempEvent->pos()-pos() ); 00357 // Are we still on the canvas? 00358 if (clipping() && ( 00359 tempEvent->pos().x()<0 || 00360 tempEvent->pos().y()<0 || 00361 tempEvent->pos().x()>=outerSize().x() || 00362 tempEvent->pos().y()>=outerSize().y() ) ) 00363 { 00364 // Outside, send this as an outside event instead. 00365 inside = false; 00366 } 00367 tempEvent->pos( tempEvent->pos()-innerOffset() ); 00368 // Use the modified event 00369 modifedEvent = tempEvent; 00370 break; 00371 } 00372 00373 //----------------------------------------------- 00374 // Warp the mouse location to this canvas 00375 case EventType_MouseMotion: { 00376 const cAutoPtr<const cMouseMotionEvent> Motion( i_iEvent ); 00377 cMouseMotionEvent *tempEvent = 00378 new cMouseMotionEvent( *Motion ); 00379 tempEvent->pos( tempEvent->pos()-pos() ); 00380 // Are we still on the canvas? 00381 if (clipping() && ( 00382 tempEvent->pos().x()<0 || 00383 tempEvent->pos().y()<0 || 00384 tempEvent->pos().x()>=outerSize().x() || 00385 tempEvent->pos().y()>=outerSize().y() ) ) 00386 { 00387 // Outside, send this as an outside event instead. 00388 inside = false; 00389 } 00390 tempEvent->pos( tempEvent->pos()-innerOffset() ); 00391 // Use the modified event 00392 modifedEvent = tempEvent; 00393 break; 00394 } 00395 00396 //----------------------------------------------- 00397 // We have to check for a focus change 00398 case EventType_Key: { 00399 const cAutoPtr<const cKeyEvent> Key( i_iEvent ); 00400 if (Key->key()==Key_Tab && Key->pressed()) { 00401 if (Key->mods()&KMod_Shift) 00402 focusBack(); 00403 else 00404 focusForward(); 00405 } 00406 // And pass it along unmodifed 00407 modifedEvent = i_iEvent; 00408 break; 00409 } 00410 00411 //----------------------------------------------- 00412 // Nothing special to do, just pass it along 00413 default: 00414 modifedEvent = i_iEvent; 00415 break; 00416 } 00417 00418 if (inside) { 00419 for (tElementList::iterator eIt = mElements.begin(); 00420 eIt!=mElements.end(); ++eIt) 00421 (*eIt)->systemEvent( modifedEvent ); 00422 } else { 00423 for (tElementList::iterator eIt = mElements.begin(); 00424 eIt!=mElements.end(); ++eIt) 00425 (*eIt)->systemEventOutside( modifedEvent ); 00426 } 00427 } 00428 00429 00430 /**************************************************************************/ 00431 void cGuiCanvas::systemEventOutside( 00432 const cAutoPtr<const cEventInterface> &i_iEvent ) 00433 { 00434 if (!enabled()) return; 00435 cAutoPtr<const cEventInterface> modifedEvent; 00436 switch (i_iEvent->type()) 00437 { 00438 //----------------------------------------------- 00439 // Warp the mouse location to this canvas 00440 case EventType_MouseButton: { 00441 const cAutoPtr<const cMouseButtonEvent> Button = i_iEvent; 00442 cMouseButtonEvent *tempEvent = 00443 new cMouseButtonEvent( *Button ); 00444 tempEvent->pos( (tempEvent->pos()-pos())-innerOffset() ); 00445 // Use the modified event 00446 modifedEvent = tempEvent; 00447 break; 00448 } 00449 00450 //----------------------------------------------- 00451 // Warp the mouse location to this canvas 00452 case EventType_MouseMotion: { 00453 const cAutoPtr<const cMouseMotionEvent> Motion = i_iEvent; 00454 cMouseMotionEvent *tempEvent = 00455 new cMouseMotionEvent( *Motion ); 00456 tempEvent->pos( (tempEvent->pos()-pos())-innerOffset() ); 00457 // Use the modified event 00458 modifedEvent = tempEvent; 00459 break; 00460 } 00461 00462 //----------------------------------------------- 00463 // Nothing special to do, just pass it along 00464 default: 00465 modifedEvent = i_iEvent; 00466 break; 00467 } 00468 // Pass the event along 00469 for (tElementList::iterator eIt = mElements.begin(); 00470 eIt!=mElements.end(); ++eIt) 00471 (*eIt)->systemEventOutside( modifedEvent ); 00472 } 00473 00474 /**************************************************************************/ 00475 void cGuiCanvas::update( const tUint iTimePassed ) 00476 { 00477 queue<tString> delNames; 00478 00479 if (!enabled()) return; 00480 cGuiElement::update( iTimePassed ); 00481 for (tElementList::iterator eIt = mElements.begin(); 00482 eIt!=mElements.end(); ++eIt) 00483 { 00484 (*eIt)->update( iTimePassed ); 00485 if ((*eIt)->suicidal()) delNames.push( (*eIt)->name() ); 00486 } 00487 00488 while (!delNames.empty()) { 00489 remove( delNames.front() ); 00490 delNames.pop(); 00491 } 00492 } 00493 00494 /**************************************************************************/ 00495 void cGuiCanvas::dropAllFocus( const cGuiElement *const ioFrom ) 00496 { 00497 for (tElementList::iterator eIt = mElements.begin(); 00498 eIt!=mElements.end(); ++eIt) 00499 { 00500 if (ioFrom!=eIt->dumbPtr()) 00501 (*eIt)->focus( false ); 00502 } 00503 if (canvas()) 00504 canvas()->dropAllFocus( this ); 00505 } 00506 00507 /**************************************************************************/ 00508 void cGuiCanvas::focusForward() 00509 { 00510 const tElementFocusOrder::size_type ListSize = mFocusOrder.size(); 00511 if (ListSize<2) return; 00512 tElementFocusOrder::size_type focusedIndex = 0; 00513 tBool found = false; 00514 while (focusedIndex!=ListSize && !found) { 00515 if (mFocusOrder[focusedIndex]->focus()) found=true; 00516 else ++focusedIndex; 00517 } 00518 00519 if (focusedIndex==ListSize) { 00520 // Nothing had focus, so set the first and quit 00521 mFocusOrder[0]->focus(true); 00522 return; 00523 } 00524 // Do the switch 00525 mFocusOrder[focusedIndex]->focus(false); 00526 ++focusedIndex; 00527 if (focusedIndex==ListSize) focusedIndex = 0; 00528 mFocusOrder[focusedIndex]->focus(true); 00529 } 00530 00531 /**************************************************************************/ 00532 void cGuiCanvas::focusBack() 00533 { 00534 const tElementFocusOrder::size_type ListSize = mFocusOrder.size(); 00535 if (ListSize<2) return; 00536 tElementFocusOrder::size_type focusedIndex = 0; 00537 tBool found = false; 00538 while (focusedIndex!=ListSize && !found) { 00539 if (mFocusOrder[focusedIndex]->focus()) found=true; 00540 else ++focusedIndex; 00541 } 00542 00543 if (focusedIndex==ListSize) { 00544 // Nothing had focus, so set the first and quit 00545 mFocusOrder[0]->focus(true); 00546 return; 00547 } 00548 // Do the switch 00549 mFocusOrder[focusedIndex]->focus(false); 00550 if (focusedIndex==ListSize) focusedIndex = 0; 00551 else if (focusedIndex==0) focusedIndex = ListSize-1; 00552 else --focusedIndex; 00553 mFocusOrder[focusedIndex]->focus(true); 00554 } 00555 00556 00557 /**************************************************************************/ 00558 const cAutoPtr<cGuiElement> cGuiCanvas::clone() const 00559 { 00560 cAutoPtr<cGuiElement> newElement( new cGuiCanvas ); 00561 cloneInto( newElement ); 00562 return newElement; 00563 } 00564 00565 /**************************************************************************/ 00566 void cGuiCanvas::cloneInto( const cAutoPtr<cGuiCanvas> &i_ioElement ) const 00567 { 00568 // Parent Properties first 00569 cGuiElement::cloneInto( i_ioElement ); 00570 00571 for (tElementList::const_iterator eIt = mElements.begin(); 00572 eIt!=mElements.end(); ++eIt) 00573 i_ioElement->add( (*eIt)->name(), (*eIt)->clone() ); 00574 i_ioElement->innerOffset( innerOffset() ); 00575 i_ioElement->clipping( clipping() ); 00576 i_ioElement->enabled( enabled() ); 00577 } 00578 00579 /**************************************************************************/ 00580 const tBool cGuiCanvas::hasChildren() const 00581 { 00582 return true; 00583 } 00584 00585 /**************************************************************************/ 00586 void cGuiCanvas::enabled( const tBool iEnabled ) 00587 { 00588 mEnabled = iEnabled; 00589 } 00590 00591 /**************************************************************************/ 00592 void cGuiCanvas::prop( const tString &iName, const cDynVar &iVal, 00593 const tString &iInnerKey ) 00594 { 00595 if (iName == "enabled") 00596 enabled( iVal ); 00597 00598 else cGuiElement::prop( iName, iVal ); 00599 } 00600 00601 /**************************************************************************/ 00602 const cDynVar cGuiCanvas::prop( const tString &iName, 00603 const tString &iInnerKey ) const 00604 { 00605 if (iName == "enabled") 00606 return enabled(); 00607 00608 return cGuiElement::prop( iName ); 00609 } 00610 00611 /**************************************************************************/ 00612 void cGuiCanvas::_real_remove( const tGuiName &iName ) 00613 { 00614 // This is punishment for something, I'm just not sure what. 00615 cAutoPtr<cGuiElement> toBeDel = get(iName); 00616 tUint eID; 00617 { 00618 tElementLookup::iterator tmp = mElementLookup.find(iName); 00619 if (tmp == mElementLookup.end()) 00620 throw cOutOfBoundsException( "cGuiCanvas::_real_remove", 00621 "I have no idea how this happened. Sorry." ); 00622 eID = tmp->second; 00623 mElementLookup.erase( tmp ); 00624 } 00625 00626 // It may very well not be included here. That's fine. 00627 for (tElementFocusOrder::iterator i = mFocusOrder.begin(); 00628 i!=mFocusOrder.end(); ) 00629 { 00630 if (*i == toBeDel) i = mFocusOrder.erase( i ); 00631 else ++i; 00632 } 00633 00634 mElements.erase( tElementList::iterator(&(mElements[eID])) ); 00635 00636 // Drop all tElementLookup types by one if larger than eID. 00637 for (tElementLookup::iterator i = mElementLookup.begin(); 00638 i!=mElementLookup.end(); ++i) 00639 { 00640 if (i->second>eID) --(i->second); 00641 } 00642 00643 // Drop all tElementIDList types by one if larger than eID. 00644 for (tRenderOrder::iterator i = mRenderOrder.begin(); 00645 i!=mRenderOrder.end(); ++i) 00646 { 00647 for (tElementIDList::iterator i2 = i->second.begin(); 00648 i2!=i->second.end();) 00649 { 00650 if (*i2==eID) i2 = i->second.erase(i2); 00651 else ++i2; 00652 } 00653 for (tElementIDList::iterator i2 = i->second.begin(); 00654 i2!=i->second.end(); ++i2) 00655 { 00656 if (*i2>eID) --(*i2); 00657 } 00658 } 00659 } 00660 00661 } // namespace n2l |