![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference cGuiTextDisplay.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/cGuiTextDisplay.h" 00026 00027 #include "n2l/dynVars.h" 00028 #include "n2l/resourceManagement.h" 00029 #include "n2l/events.h" 00030 00031 #include "gui/cGuiSlider.h" 00032 #include "gui/cGuiSEInt.h" 00033 #include "gui/cGuiACInt.h" 00034 00035 #include "gui/cGuiFactory.h" 00036 00037 namespace n2l 00038 { 00039 00040 # ifdef N2L_PASSIVE_GUI_REG 00041 tBool cGuiTextDisplay::smRegistered = 00042 cGuiFactory::current().registerLoader( "n2l::cGuiTextDisplay", 00043 cGuiElement::loadNew<cGuiTextDisplay> ); 00044 # endif 00045 00046 /**************************************************************************/ 00047 cGuiTextDisplay::cGuiTextDisplay() 00048 { 00049 reset(); 00050 } 00051 00052 /**************************************************************************/ 00053 cGuiTextDisplay::~cGuiTextDisplay() 00054 { 00055 reset(); 00056 } 00057 00058 /**************************************************************************/ 00059 cGuiTextDisplay::cGuiTextDisplay( const cVfsNodeInterface &iNode ) 00060 { 00061 reset(); 00062 load( iNode ); 00063 } 00064 00065 /**************************************************************************/ 00066 cGuiTextDisplay::cGuiTextDisplay( const cDynVar &iDefinition ) 00067 { 00068 reset(); 00069 load( iDefinition ); 00070 } 00071 00072 /**************************************************************************/ 00073 void cGuiTextDisplay::load( const cVfsNodeInterface &iNode ) 00074 { 00075 cDynVar def; 00076 validateAndDecode( def, iNode, "n2l::cGuiTextDisplay" ); 00077 load( def ); 00078 } 00079 00080 /**************************************************************************/ 00081 void cGuiTextDisplay::load( const cDynVar &iDefinition ) 00082 { 00083 // Load parent properties 00084 cGuiInteractive::load( iDefinition ); 00085 00086 // Now my properties 00087 if (iDefinition["textStyle"]) { 00088 if (iDefinition["textStyle"].isArray()) 00089 textStyle( cGuiTextStyle( iDefinition["textStyle"] ) ); 00090 else textStyle( *cResourceManager::get<cGuiTextStyle>( 00091 iDefinition["textStyle"]) ); 00092 } 00093 00094 if (iDefinition.keyExists("slider")) { 00095 if (iDefinition["slider"].isArray()) 00096 slider( cGuiSlider(iDefinition["slider"]) ); 00097 else { 00098 // Cleanly catch bad casts so we can show an error, instead 00099 // of just exploding anonymously. 00100 try { 00101 const cAutoPtr<const cGuiSlider> Slider = 00102 cResourceManager::getGuiElement(iDefinition["slider"]); 00103 slider( *Slider ); 00104 } 00105 catch (const cBadCastException & iException) { 00106 throw cParsingException( "cGuiTextDisplay::load", 00107 "Provided slider template file didn\'t " 00108 "define a slider"); 00109 } 00110 } 00111 } // slider was an array 00112 00113 // Specifically do the text last, so the 'lines' reflow happens as 00114 // few times as possible. 00115 if (iDefinition.keyExists("text")) 00116 text( iDefinition["text"] ); 00117 } 00118 00119 /**************************************************************************/ 00120 void cGuiTextDisplay::draw() const 00121 { 00122 tGuiPos textPos( innerPos() ); 00123 const tFloat LineSeperation = 00124 mTextStyle.font()->glyphRowSeperation( mTextStyle.fontHeight() ); 00125 00126 const tFloat BottomEdge = 00127 (innerPos()+innerSize()).y()-mTextStyle.fontHeight(); 00128 00129 tGuiPos lineSize( innerSize().x(), mTextStyle.fontHeight() ); 00130 00131 tUint line=0; 00132 if (mSlider.isSet()) { 00133 line = tUint(mSlider->value()+0.5f); 00134 lineSize.x() -= mSlider->size().x(); 00135 } else { 00136 line = mLineOffset; 00137 } 00138 00139 while (line<mLines.size() && textPos.y()<BottomEdge) 00140 { 00141 // Catch the zero size, because draw() will see it as lease to 00142 // draw as much as it can. 00143 // not none. 00144 if (mLines[line].mSize) 00145 mTextStyle.draw( textPos, lineSize, mText, 00146 mLines[line].mSPos, mLines[line].mSize ); 00147 ++line; 00148 textPos.y( textPos.y()+lineSize.y()+LineSeperation ); 00149 } 00150 00151 if (mSlider.isSet()) 00152 mSlider->draw(); 00153 } 00154 00155 /**************************************************************************/ 00156 void cGuiTextDisplay::textStyle( const cGuiTextStyle &iStyle ) 00157 { 00158 mTextStyle = iStyle; 00159 propertyChanged(); 00160 } 00161 00162 /**************************************************************************/ 00163 const cGuiTextStyle & cGuiTextDisplay::textStyle() const 00164 { 00165 return mTextStyle; 00166 } 00167 00168 /**************************************************************************/ 00169 void cGuiTextDisplay::slider( const cGuiSlider &iSliderTemplate ) 00170 { 00171 mSlider = iSliderTemplate.clone(); 00172 // Down on the wheel and kp should go deeper into the text 00173 mSlider->swapWheel( true ); 00174 propertyChanged(); 00175 dimensionChanged(); 00176 } 00177 00178 /**************************************************************************/ 00179 void cGuiTextDisplay::text( const tString & iText ) 00180 { 00181 mText = substringReplace("\t"," ",iText); 00182 propertyChanged(); 00183 } 00184 00185 /**************************************************************************/ 00186 const tString & cGuiTextDisplay::text() const 00187 { 00188 return mText; 00189 } 00190 00191 /**************************************************************************/ 00192 void cGuiTextDisplay::lineInfo( const tUint &iLine, 00193 tUint &oSPos, tUint &oLen ) const 00194 { 00195 if (iLine >= mLines.size()) 00196 throw cOutOfBoundsException( "cGuiTextDisplay::lineInfo", 00197 "No such line to get info for.", asString(iLine) ); 00198 oSPos = mLines[iLine].mSPos; 00199 oLen = mLines[iLine].mSize; 00200 } 00201 00202 /**************************************************************************/ 00203 const tUint cGuiTextDisplay::numLines() const 00204 { 00205 return mLines.size(); 00206 } 00207 00208 /**************************************************************************/ 00209 const tUint &cGuiTextDisplay::lineOffset() const 00210 { 00211 return mLineOffset; 00212 } 00213 00214 /**************************************************************************/ 00215 void cGuiTextDisplay::lineOffset( const tUint &iOffset ) 00216 { 00217 mLineOffset = n2lMin(iOffset, mLines.size()); 00218 } 00219 00220 /**************************************************************************/ 00221 const cAutoPtr<cGuiElement> cGuiTextDisplay::clone() const 00222 { 00223 cAutoPtr<cGuiElement> newElement( new cGuiTextDisplay ); 00224 cloneInto( newElement ); 00225 return newElement; 00226 } 00227 00228 /**************************************************************************/ 00229 void cGuiTextDisplay::prop( const tString &iName, const cDynVar &iVal, 00230 const tString &iInnerKey ) 00231 { 00232 if (iName == "textStyle") { 00233 if (iInnerKey.empty()) { 00234 if (iVal.isArray()) 00235 textStyle( iVal ); 00236 else 00237 textStyle( *cResourceManager::get<cGuiTextStyle>(iVal) ); 00238 } else 00239 mTextStyle.prop( iInnerKey, iVal ); 00240 00241 } else 00242 cGuiElement::prop( iName, iVal ); 00243 } 00244 00245 /**************************************************************************/ 00246 const cDynVar cGuiTextDisplay::prop( const tString &iName, 00247 const tString &iInnerKey ) const 00248 { 00249 if (iName == "textStyle") 00250 return mTextStyle.prop( iInnerKey ); 00251 00252 return cGuiElement::prop( iName ); 00253 } 00254 00255 /**************************************************************************/ 00256 void cGuiTextDisplay::systemEvent( 00257 const cAutoPtr<const cEventInterface> &i_iEvent ) 00258 { 00259 cGuiInteractive::systemEvent( i_iEvent ); 00260 00261 if (i_iEvent->type()==EventType_MouseButton) 00262 { 00263 const cAutoPtr<const cMouseButtonEvent> Click( i_iEvent ); 00264 // Intercept up and down clicks and send them to the 00265 // slider instead of the canvas so that the wheel works 00266 // anywhere on the widget, rather than just on the 00267 // slide bar 00268 if ((Click->button()==MouseButton_Up || 00269 Click->button()==MouseButton_Down) && 00270 collides( Click->pos() ) ) 00271 { 00272 if (mSlider.isSet()) { 00273 cAutoPtr<cMouseButtonEvent> fakeClick = 00274 new cMouseButtonEvent(*Click); 00275 fakeClick->pos( mSlider->pos() ); 00276 mSlider->systemEvent( fakeClick ); 00277 } 00278 } else { 00279 // Slider is in here so we don't double send to it if 00280 // the up or down wheel is used. 00281 if (mSlider.isSet()) mSlider->systemEvent( i_iEvent ); 00282 } 00283 } else {// if this is a mouse button. 00284 if (mSlider.isSet()) mSlider->systemEvent( i_iEvent ); 00285 } 00286 00287 } 00288 00289 /**************************************************************************/ 00290 void cGuiTextDisplay::systemEventOutside( 00291 const cAutoPtr<const cEventInterface> &i_iEvent ) 00292 { 00293 cGuiInteractive::systemEventOutside( i_iEvent ); 00294 if (mSlider.isSet()) mSlider->systemEventOutside( i_iEvent ); 00295 } 00296 00297 /**************************************************************************/ 00298 void cGuiTextDisplay::cloneInto( 00299 const cAutoPtr<cGuiTextDisplay> &i_ioElement ) const 00300 { 00301 // Parent Properties first 00302 cGuiInteractive::cloneInto( i_ioElement ); 00303 00304 // Now our properties. 00305 i_ioElement->textStyle(mTextStyle); 00306 if (mSlider.isSet()) 00307 i_ioElement->slider( *mSlider ); 00308 i_ioElement->text(mText); 00309 00310 i_ioElement->mLineOffset = mLineOffset; 00311 } 00312 00313 /**************************************************************************/ 00314 void cGuiTextDisplay::dimensionChanged() 00315 { 00316 // Call our parent first 00317 cGuiInteractive::dimensionChanged(); 00318 00319 if (mSlider.isSet()) { 00320 mSlider->pos( 00321 tGuiPos(innerPos().x()+innerSize().x()-mSlider->size().x(), 00322 innerPos().y()) ); 00323 mSlider->size( tGuiPos(mSlider->size().x(),innerSize().y()) ); 00324 } 00325 } 00326 00327 /**************************************************************************/ 00328 void cGuiTextDisplay::propertyChanged() 00329 { 00330 // Call our parent first 00331 cGuiInteractive::propertyChanged(); 00332 00333 // Rebuild the line indicies. 00334 mLines.clear(); 00335 00336 const tFloat LineSeperation = mTextStyle.font()->glyphRowSeperation( 00337 mTextStyle.fontHeight() ); 00338 tGuiPos lineSize( innerSize().x(), mTextStyle.fontHeight() ); 00339 // If se have a slider, leave room for it. 00340 if (mSlider.isSet()) lineSize.x() -= mSlider->size().x(); 00341 00342 tString::size_type lastShownChar(0); 00343 tLineInfo lineInfo; 00344 const tLineInfo EmptyLine = {0,0}; 00345 00346 while (lastShownChar<mText.size()) 00347 { 00348 // Find the next 'newline' character, this can be optimized 00349 // in the future. 00350 tString::size_type nextNewline( mText.find_first_of('\n', 00351 lastShownChar) ); 00352 // If there was a newline, seek until we hit it, rather than 00353 // showing the whole line. 00354 if (nextNewline!=tString::npos) { 00355 lineInfo.mSPos = lastShownChar; 00356 lineInfo.mSize = mTextStyle.wouldDraw( lineSize, mText, 00357 lastShownChar, nextNewline-lastShownChar ); 00358 lastShownChar += (lineInfo.mSize); 00359 // Strip the first newline, if there is one. We don't 00360 // want to render two. 00361 if (mText[lastShownChar] == '\n') ++lastShownChar; 00362 mLines.push_back(lineInfo); 00363 00364 while (mText[lastShownChar] == '\n') { 00365 mLines.push_back(EmptyLine); 00366 ++lastShownChar; 00367 } 00368 } else { 00369 lineInfo.mSPos = lastShownChar; 00370 lineInfo.mSize = mTextStyle.wouldDraw( lineSize, mText, 00371 lastShownChar ); 00372 //++lineInfo.mSize; 00373 lastShownChar += lineInfo.mSize; 00374 mLines.push_back(lineInfo); 00375 if (mText[lastShownChar]==' ' || 00376 mText[lastShownChar]=='\n') ++lastShownChar; 00377 } 00378 } 00379 00380 // Set the slider min/max/range based on the line calculations 00381 if (mSlider.isSet()) { 00382 mSlider->min(0); 00383 mSlider->value(0); 00384 mSlider->max( mLines.size() ); 00385 mSlider->range( 00386 tUint(innerSize().y()/(lineSize.y()+LineSeperation)) ); 00387 } 00388 } 00389 00390 /**************************************************************************/ 00391 void cGuiTextDisplay::reset() 00392 { 00393 pos( tGuiPos(0,0) ); 00394 size( tGuiPos(0,0) ); 00395 mText = ""; 00396 mSlider = 0; 00397 mLineOffset = 0; 00398 mLineOffset = 0; 00399 } 00400 00401 } // namespace n2l |