libyui
3.0.10
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: YWidget.cc 00020 00021 Author: Stefan Hundhammer <sh@suse.de> 00022 00023 /-*/ 00024 00025 00026 #include <signal.h> 00027 #include <iostream> 00028 #include <sstream> 00029 00030 #define YUILogComponent "ui" 00031 #include "YUILog.h" 00032 00033 #include "YUISymbols.h" 00034 #include "YShortcut.h" 00035 #include "YWidget.h" 00036 #include "YDialog.h" 00037 #include "YUI.h" 00038 #include "YDialog.h" 00039 #include "YUIException.h" 00040 #include "YWidgetID.h" 00041 #include "YBothDim.h" 00042 #include "YMacroRecorder.h" 00043 00044 #include "YChildrenManager.h" 00045 00046 #define MAX_DEBUG_LABEL_LEN 50 00047 #define YWIDGET_MAGIC 42 00048 00049 #define CHECK_FOR_DUPLICATE_CHILDREN 1 00050 #define LOG_WIDGET_REP 0 00051 00052 00053 00054 struct YWidgetPrivate 00055 { 00056 /** 00057 * Constructor 00058 **/ 00059 YWidgetPrivate( YWidgetChildrenManager * manager, YWidget * parentWidget = 0 ) 00060 : childrenManager( manager ) 00061 , parent( parentWidget ) 00062 , beingDestroyed( false ) 00063 , enabled( true ) 00064 , notify( false ) 00065 , notifyContextMenu( false ) 00066 , sendKeyEvents( false ) 00067 , autoShortcut( false ) 00068 , toolkitWidgetRep( 0 ) 00069 , id( 0 ) 00070 , functionKey( 0 ) 00071 { 00072 stretch.hor = false; 00073 stretch.vert = false; 00074 weight.hor = 0; 00075 weight.vert = 0; 00076 } 00077 00078 // 00079 // Data members 00080 // 00081 00082 YWidgetChildrenManager * childrenManager; 00083 YWidget * parent; 00084 bool beingDestroyed; 00085 bool enabled; 00086 bool notify; 00087 bool notifyContextMenu; 00088 bool sendKeyEvents; 00089 bool autoShortcut; 00090 void * toolkitWidgetRep; 00091 YWidgetID * id; 00092 YBothDim<bool> stretch; 00093 YBothDim<int> weight; 00094 int functionKey; 00095 std::string helpText; 00096 }; 00097 00098 00099 00100 00101 bool YWidget::_usedOperatorNew = false; 00102 00103 00104 YWidget::YWidget( YWidget * parent ) 00105 : _magic( YWIDGET_MAGIC ) 00106 , priv( new YWidgetPrivate( new YWidgetChildrenRejector( this ), parent ) ) 00107 { 00108 YUI_CHECK_NEW( priv ); 00109 YUI_CHECK_NEW( priv->childrenManager ); 00110 00111 if ( ! _usedOperatorNew ) 00112 { 00113 yuiError() << "FATAL: Widget at " 00114 << std::hex << (void *) this << std::dec 00115 << " not created with operator new !" 00116 << std::endl; 00117 yuiError() << "Check core dump for a backtrace." << std::endl; 00118 abort(); 00119 } 00120 00121 _usedOperatorNew = false; 00122 00123 if ( parent ) 00124 parent->addChild( this ); 00125 } 00126 00127 00128 void * YWidget::operator new( size_t size ) 00129 { 00130 _usedOperatorNew = true; 00131 return ::operator new( size ); 00132 } 00133 00134 00135 YWidget::~YWidget() 00136 { 00137 YUI_CHECK_WIDGET( this ); 00138 setBeingDestroyed(); 00139 // yuiDebug() << "Destructor of YWidget " << this << std::endl; 00140 00141 deleteChildren(); 00142 YUI::ui()->deleteNotify( this ); 00143 00144 if ( parent() && ! parent()->beingDestroyed() ) 00145 parent()->removeChild( this ); 00146 00147 delete priv->childrenManager; 00148 00149 if ( priv->id ) 00150 delete priv->id; 00151 00152 invalidate(); 00153 } 00154 00155 00156 YWidgetChildrenManager * 00157 YWidget::childrenManager() const 00158 { 00159 return priv->childrenManager; 00160 } 00161 00162 00163 void 00164 YWidget::setChildrenManager( YWidgetChildrenManager * newChildrenManager ) 00165 { 00166 YUI_CHECK_PTR( newChildrenManager ); 00167 00168 delete priv->childrenManager; 00169 priv->childrenManager = newChildrenManager; 00170 } 00171 00172 00173 void 00174 YWidget::addChild( YWidget * child ) 00175 { 00176 #if CHECK_FOR_DUPLICATE_CHILDREN 00177 if ( child && childrenManager()->contains( child ) ) 00178 { 00179 yuiError() << this << " already contains " << child << std::endl; 00180 YUI_THROW( YUIInvalidChildException<YWidget>( this, child ) ); 00181 } 00182 #endif 00183 00184 childrenManager()->add( child ); 00185 } 00186 00187 00188 void 00189 YWidget::removeChild( YWidget * child ) 00190 { 00191 if ( ! beingDestroyed() ) 00192 { 00193 // yuiDebug() << "Removing " << child << " from " << this << std::endl; 00194 childrenManager()->remove( child ); 00195 } 00196 } 00197 00198 00199 void 00200 YWidget::deleteChildren() 00201 { 00202 YWidgetList::const_iterator it = childrenBegin(); 00203 00204 while ( it != childrenEnd() ) 00205 { 00206 YWidget * child = *it; 00207 ++it; 00208 00209 if ( child->isValid() ) 00210 { 00211 // yuiDebug() << "Deleting " << child << std::endl; 00212 delete child; 00213 } 00214 } 00215 00216 childrenManager()->clear(); 00217 } 00218 00219 00220 std::string 00221 YWidget::debugLabel() const 00222 { 00223 std::string label = YShortcut::cleanShortcutString( YShortcut::getShortcutString( this ) ); 00224 00225 if ( label.size() > MAX_DEBUG_LABEL_LEN ) 00226 { 00227 label.resize( MAX_DEBUG_LABEL_LEN ); 00228 label.append( "..." ); 00229 } 00230 00231 for ( unsigned i=0; i < label.size(); i++ ) 00232 { 00233 if ( label[i] == '\n' ) 00234 label[i] = ' '; 00235 } 00236 00237 return label; 00238 } 00239 00240 00241 bool 00242 YWidget::isValid() const 00243 { 00244 return _magic == YWIDGET_MAGIC; 00245 } 00246 00247 00248 void 00249 YWidget::invalidate() 00250 { 00251 _magic = 0; 00252 } 00253 00254 00255 bool 00256 YWidget::beingDestroyed() const 00257 { 00258 return priv->beingDestroyed; 00259 } 00260 00261 void 00262 YWidget::setBeingDestroyed() 00263 { 00264 priv->beingDestroyed = true; 00265 } 00266 00267 00268 YWidget * 00269 YWidget::parent() const 00270 { 00271 return priv->parent; 00272 } 00273 00274 00275 bool 00276 YWidget::hasParent() const 00277 { 00278 return priv->parent; 00279 } 00280 00281 00282 void 00283 YWidget::setParent( YWidget * newParent ) 00284 { 00285 if ( newParent && priv->parent ) 00286 { 00287 YDialog::currentDialog()->dumpWidgetTree(); 00288 yuiWarning() << "Reparenting " << this 00289 << " from " << priv->parent 00290 << " to " << newParent << std::endl; 00291 YUI_THROW( YUIException( std::string( widgetClass() ) + " already has a parent!" ) ); 00292 } 00293 00294 priv->parent = newParent; 00295 } 00296 00297 00298 bool YWidget::sendKeyEvents() const 00299 { 00300 return priv->sendKeyEvents; 00301 } 00302 00303 00304 void YWidget::setSendKeyEvents( bool doSend ) 00305 { 00306 priv->sendKeyEvents = doSend; 00307 } 00308 00309 00310 bool YWidget::autoShortcut() const 00311 { 00312 return priv->autoShortcut; 00313 } 00314 00315 00316 void YWidget::setAutoShortcut( bool newAutoShortcut ) 00317 { 00318 priv->autoShortcut = newAutoShortcut; 00319 } 00320 00321 00322 int YWidget::functionKey() const 00323 { 00324 return priv->functionKey; 00325 } 00326 00327 00328 bool YWidget::hasFunctionKey() const 00329 { 00330 return priv->functionKey > 0; 00331 } 00332 00333 00334 void YWidget::setFunctionKey( int fkey_no ) 00335 { 00336 priv->functionKey = fkey_no; 00337 } 00338 00339 00340 std::string YWidget::helpText() const 00341 { 00342 return priv->helpText; 00343 } 00344 00345 00346 void YWidget::setHelpText( const std::string & helpText ) 00347 { 00348 priv->helpText = helpText; 00349 } 00350 00351 00352 YWidgetID * 00353 YWidget::id() const 00354 { 00355 return priv->id; 00356 } 00357 00358 00359 void YWidget::setId( YWidgetID * newId ) 00360 { 00361 if ( priv->id ) 00362 delete priv->id; 00363 00364 priv->id = newId; 00365 } 00366 00367 00368 bool YWidget::hasId() const 00369 { 00370 return priv->id != 0; 00371 } 00372 00373 00374 YDialog * YWidget::findDialog() 00375 { 00376 YWidget * widget = this; 00377 00378 while ( widget ) 00379 { 00380 YDialog * dialog = dynamic_cast<YDialog *> (widget); 00381 00382 if ( dialog ) 00383 return dialog; 00384 else 00385 widget = widget->parent(); 00386 } 00387 00388 return 0; 00389 } 00390 00391 00392 const YPropertySet & 00393 YWidget::propertySet() 00394 { 00395 static YPropertySet propSet; 00396 00397 if ( propSet.isEmpty() ) 00398 { 00399 /** 00400 * @property boolean Enabled enabled/disabled state of this widget 00401 * @property boolean Notify the current notify state (see also `opt( `notify )) 00402 * @property boolean ContextMenu the current contextmenu state (see also `opt( `notifyContextMenu )) 00403 * @property std::string WidgetClass the widget class of this widget (YLabel, YPushButton, ...) 00404 * @property std::string DebugLabel (possibly translated) text describing this widget for debugging 00405 * @property std::string HelpText help text 00406 * @property integer HWeight horizontal layout weight (same as `HWeight(widget()) 00407 * @property integer VWeight vertical layout weight (same as `VWeight(widget()) 00408 * @property boolean HStretch horizontally stretchable? (same as `opt(`hstretch)) 00409 * @property boolean VStretch vertically stretchable? (same as `opt(`vstretch)) 00410 **/ 00411 00412 propSet.add( YProperty( YUIProperty_Enabled, YBoolProperty ) ); 00413 propSet.add( YProperty( YUIProperty_Notify, YBoolProperty ) ); 00414 propSet.add( YProperty( YUIProperty_WidgetClass, YStringProperty, true ) ); // read-only 00415 propSet.add( YProperty( YUIProperty_DebugLabel, YStringProperty, true ) ); // read-only 00416 propSet.add( YProperty( YUIProperty_HelpText, YStringProperty ) ); 00417 propSet.add( YProperty( YUIProperty_HWeight, YIntegerProperty ) ); 00418 propSet.add( YProperty( YUIProperty_VWeight, YIntegerProperty ) ); 00419 propSet.add( YProperty( YUIProperty_HStretch, YBoolProperty ) ); 00420 propSet.add( YProperty( YUIProperty_VStretch, YBoolProperty ) ); 00421 } 00422 00423 return propSet; 00424 } 00425 00426 00427 bool 00428 YWidget::setProperty( const std::string & propertyName, const YPropertyValue & val ) 00429 { 00430 try 00431 { 00432 propertySet().check( propertyName, val.type() ); // throws exceptions if not found or type mismatch 00433 } 00434 catch( YUIPropertyException & exception ) 00435 { 00436 exception.setWidget( this ); 00437 throw; 00438 } 00439 00440 if ( propertyName == YUIProperty_Enabled ) setEnabled( val.boolVal() ); 00441 else if ( propertyName == YUIProperty_Notify ) setNotify ( val.boolVal() ); 00442 else if ( propertyName == YUIProperty_HelpText ) setHelpText( val.stringVal() ); 00443 else if ( propertyName == YUIProperty_HWeight ) setWeight( YD_HORIZ, val.integerVal() ); 00444 else if ( propertyName == YUIProperty_VWeight ) setWeight( YD_VERT , val.integerVal() ); 00445 else if ( propertyName == YUIProperty_HStretch ) setStretchable( YD_HORIZ, val.boolVal() ); 00446 else if ( propertyName == YUIProperty_VStretch ) setStretchable( YD_VERT , val.boolVal() ); 00447 00448 return true; // success -- no special processing necessary 00449 } 00450 00451 00452 YPropertyValue 00453 YWidget::getProperty( const std::string & propertyName ) 00454 { 00455 try 00456 { 00457 propertySet().check( propertyName ); // throws exceptions if not found 00458 } 00459 catch( YUIPropertyException & exception ) 00460 { 00461 exception.setWidget( this ); 00462 throw; 00463 } 00464 00465 if ( propertyName == YUIProperty_Enabled ) return YPropertyValue( isEnabled() ); 00466 if ( propertyName == YUIProperty_Notify ) return YPropertyValue( notify() ); 00467 if ( propertyName == YUIProperty_ContextMenu ) return YPropertyValue( notifyContextMenu() ); 00468 if ( propertyName == YUIProperty_WidgetClass ) return YPropertyValue( widgetClass() ); 00469 if ( propertyName == YUIProperty_HelpText ) return YPropertyValue( helpText() ); 00470 if ( propertyName == YUIProperty_DebugLabel ) return YPropertyValue( debugLabel() ); 00471 if ( propertyName == YUIProperty_HWeight ) return YPropertyValue( weight( YD_HORIZ ) ); 00472 if ( propertyName == YUIProperty_VWeight ) return YPropertyValue( weight( YD_VERT ) ); 00473 if ( propertyName == YUIProperty_HStretch ) return YPropertyValue( stretchable( YD_HORIZ ) ); 00474 if ( propertyName == YUIProperty_VStretch ) return YPropertyValue( stretchable( YD_VERT ) ); 00475 00476 return YPropertyValue( false ); // NOTREACHED 00477 } 00478 00479 00480 void * 00481 YWidget::widgetRep() const 00482 { 00483 return priv->toolkitWidgetRep; 00484 } 00485 00486 00487 void 00488 YWidget::setWidgetRep( void * rep ) 00489 { 00490 priv->toolkitWidgetRep = rep; 00491 } 00492 00493 00494 void 00495 YWidget::setEnabled( bool enabled ) 00496 { 00497 priv->enabled = enabled; 00498 } 00499 00500 00501 bool 00502 YWidget::isEnabled() const 00503 { 00504 return priv->enabled; 00505 } 00506 00507 00508 void YWidget::setShortcutString( const std::string & str ) 00509 { 00510 yuiError() << "Default setShortcutString() method called - " 00511 << "this should be reimplemented in " 00512 << widgetClass() 00513 << std::endl; 00514 } 00515 00516 00517 void YWidget::setNotify( bool notify ) 00518 { 00519 priv->notify = notify; 00520 } 00521 00522 00523 void YWidget::setNotifyContextMenu( bool notifyContextMenu ) 00524 { 00525 priv->notifyContextMenu = notifyContextMenu; 00526 } 00527 00528 00529 bool YWidget::notify() const 00530 { 00531 return priv->notify; 00532 } 00533 00534 00535 bool YWidget::notifyContextMenu() const 00536 { 00537 return priv->notifyContextMenu; 00538 } 00539 00540 00541 int YWidget::preferredSize( YUIDimension dim ) 00542 { 00543 switch ( dim ) 00544 { 00545 case YD_HORIZ: return preferredWidth(); 00546 case YD_VERT : return preferredHeight(); 00547 00548 default: 00549 YUI_THROW( YUIInvalidDimensionException() ); 00550 return 0; 00551 } 00552 } 00553 00554 00555 void YWidget::setStretchable( YUIDimension dim, bool newStretch ) 00556 { 00557 priv->stretch[ dim ] = newStretch; 00558 } 00559 00560 00561 void YWidget::setDefaultStretchable( YUIDimension dim, bool newStretch ) 00562 { 00563 priv->stretch[ dim ] |= newStretch; 00564 } 00565 00566 00567 bool YWidget::stretchable( YUIDimension dim ) const 00568 { 00569 return priv->stretch[ dim ]; 00570 } 00571 00572 00573 int YWidget::weight( YUIDimension dim ) 00574 { 00575 return priv->weight[ dim ]; 00576 } 00577 00578 00579 void YWidget::setWeight( YUIDimension dim, int weight ) 00580 { 00581 priv->weight[ dim ] = weight; 00582 } 00583 00584 00585 bool YWidget::hasWeight( YUIDimension dim ) 00586 { 00587 // DO NOT simply return priv->weight[ dim ] here 00588 // since weight() might be overwritten in derived classes! 00589 00590 return weight( dim ) > 0; 00591 } 00592 00593 00594 bool YWidget::setKeyboardFocus() 00595 { 00596 yuiWarning() << this << " cannot accept the keyboard focus." << std::endl; 00597 return false; 00598 } 00599 00600 00601 YWidget * 00602 YWidget::findWidget( YWidgetID * id, bool doThrow ) const 00603 { 00604 if ( ! id ) 00605 { 00606 if ( doThrow ) 00607 YUI_THROW( YUIWidgetNotFoundException( "Null ID" ) ); 00608 00609 return 0; 00610 } 00611 00612 for ( YWidgetListConstIterator it = childrenBegin(); 00613 it != childrenEnd(); 00614 ++it ) 00615 { 00616 YWidget * child = *it; 00617 YUI_CHECK_WIDGET( child ); 00618 00619 if ( child->id() && child->id()->isEqual( id ) ) 00620 return child; 00621 00622 if ( child->hasChildren() ) 00623 { 00624 YWidget * found = child->findWidget( id, false ); 00625 00626 if ( found ) 00627 return found; 00628 } 00629 } 00630 00631 if ( doThrow ) 00632 YUI_THROW( YUIWidgetNotFoundException( id->toString() ) ); 00633 00634 return 0; 00635 } 00636 00637 00638 void YWidget::setChildrenEnabled( bool enabled ) 00639 { 00640 for ( YWidgetListConstIterator it = childrenBegin(); 00641 it != childrenEnd(); 00642 ++it ) 00643 { 00644 YWidget * child = *it; 00645 00646 if ( child->hasChildren() ) 00647 { 00648 // yuiDebug() << "Recursing into " << child << std::endl; 00649 child->setChildrenEnabled( enabled ); 00650 } 00651 00652 // yuiDebug() << ( enabled ? "Enabling " : "Disabling " ) << child << std::endl; 00653 child->setEnabled( enabled ); 00654 } 00655 } 00656 00657 00658 void YWidget::dumpDialogWidgetTree() 00659 { 00660 YWidget * dialog = findDialog(); 00661 00662 if ( dialog ) 00663 dialog->dumpWidgetTree(); 00664 else 00665 dumpWidgetTree(); 00666 } 00667 00668 00669 void YWidget::dumpWidgetTree( int indentationLevel ) 00670 { 00671 dumpWidget( this, indentationLevel ); 00672 00673 for ( YWidgetListConstIterator it = childrenBegin(); 00674 it != childrenEnd(); 00675 ++it ) 00676 { 00677 YWidget * child = *it; 00678 00679 if ( child->hasChildren() ) 00680 child->dumpWidgetTree ( indentationLevel + 1 ); 00681 else 00682 dumpWidget( child, indentationLevel + 1 ); 00683 } 00684 } 00685 00686 00687 void YWidget::dumpWidget( YWidget *w, int indentationLevel ) 00688 { 00689 std::ostringstream str; 00690 00691 std::string indentation ( indentationLevel * 4, ' ' ); 00692 str << "Widget tree: " << indentation << w; 00693 00694 if ( w->widgetRep() ) 00695 { 00696 str << " (widgetRep: " 00697 << std::hex << w->widgetRep() << std::dec 00698 << ")"; 00699 } 00700 00701 std::string stretch; 00702 00703 if ( w->stretchable( YD_HORIZ ) ) stretch += "hstretch "; 00704 if ( w->stretchable( YD_VERT ) ) stretch += "vstretch"; 00705 00706 if ( ! stretch.empty() ) 00707 str << " ( " << stretch << " ) "; 00708 00709 yuiMilestone() << str.str() << std::endl; 00710 } 00711 00712 00713 void 00714 YWidget::saveUserInput( YMacroRecorder *macroRecorder ) 00715 { 00716 // 00717 // Record this widget's user input property (if there is any) 00718 // 00719 00720 if ( userInputProperty() ) 00721 { 00722 macroRecorder->recordWidgetProperty( this, userInputProperty() ); 00723 } 00724 00725 // 00726 // Record the child widgets' (if there are any) user input 00727 // 00728 00729 for ( YWidgetListConstIterator it = childrenBegin(); 00730 it != childrenEnd(); 00731 ++it ) 00732 { 00733 YWidget *widget = *it; 00734 00735 if ( widget->hasChildren() || widget->hasId() ) 00736 { 00737 /* 00738 * It wouldn't do any good to save the user input of any widget 00739 * that doesn't have an ID since this ID is required to make use of 00740 * this saved data later when playing the macro. 00741 * Other than that, container widgets need to recurse over all 00742 * their children. 00743 */ 00744 00745 widget->saveUserInput( macroRecorder ); 00746 } 00747 } 00748 } 00749 00750 00751 std::ostream & operator<<( std::ostream & stream, const YWidget * w ) 00752 { 00753 if ( w ) 00754 { 00755 stream << w->widgetClass(); 00756 00757 std::string debugLabel = w->debugLabel(); 00758 00759 if ( debugLabel.empty() ) 00760 { 00761 if ( w->hasId() ) 00762 stream << " ID: \"" << w->id() << "\""; 00763 } 00764 else // Has debugLabel 00765 { 00766 stream << " \"" << debugLabel << "\""; 00767 } 00768 00769 stream << " at " << std::hex << (void *) w << std::dec; 00770 00771 #if LOG_WIDGET_REP 00772 if ( w->widgetRep() ) 00773 { 00774 stream << " (widgetRep: " 00775 << std::hex << w->widgetRep() << std::dec 00776 << ")"; 00777 } 00778 #endif 00779 } 00780 else 00781 { 00782 stream << "<NULL widget>"; 00783 } 00784 00785 return stream; 00786 }