libyui  3.0.10
/usr/src/RPM/BUILD/libyui-3.0.10/src/YWidget.cc
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 }
 All Classes Functions Variables Enumerations Friends