libyui  3.0.10
/usr/src/RPM/BUILD/libyui-3.0.10/src/YButtonBox.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:         YButtonBox.cc
00020 
00021   Author:       Stefan Hundhammer <sh@suse.de>
00022 
00023 /-*/
00024 
00025 
00026 #include <algorithm>    // max()
00027 #include <vector>
00028 #include <list>
00029 
00030 #define YUILogComponent "ui"
00031 #include "YUILog.h"
00032 
00033 #include "YButtonBox.h"
00034 #include "YPushButton.h"
00035 #include "YUI.h"
00036 #include "YApplication.h"
00037 
00038 using std::max;
00039 
00040 
00041 YButtonBoxLayoutPolicy  YButtonBox::_layoutPolicy = kdeLayoutPolicy();
00042 YButtonBoxMargins       YButtonBox::_defaultMargins;
00043 
00044 
00045 struct YButtonBoxPrivate
00046 {
00047     /**
00048      * Constructor
00049      **/
00050     YButtonBoxPrivate()
00051         : sanityCheckRelaxed( false )
00052         , margins( YButtonBox::_defaultMargins )
00053         {}
00054 
00055     //
00056     // Data members
00057     //
00058 
00059     bool                sanityCheckRelaxed;
00060     YButtonBoxMargins   margins;
00061 };
00062 
00063 
00064 
00065 
00066 YButtonBox::YButtonBox( YWidget * parent )
00067     : YWidget( parent )
00068     , priv( new YButtonBoxPrivate() )
00069 {
00070     YUI_CHECK_NEW( priv );
00071     setChildrenManager( new YWidgetChildrenManager( this ) );
00072 }
00073 
00074 
00075 YButtonBox::~YButtonBox()
00076 {
00077     // NOP
00078 }
00079 
00080 
00081 void
00082 YButtonBox::setLayoutPolicy( const YButtonBoxLayoutPolicy & layoutPolicy )
00083 {
00084     _layoutPolicy = layoutPolicy;
00085 }
00086 
00087 
00088 YButtonBoxLayoutPolicy
00089 YButtonBox::layoutPolicy()
00090 {
00091     return _layoutPolicy;
00092 }
00093 
00094 
00095 YButtonBoxLayoutPolicy
00096 YButtonBox::kdeLayoutPolicy()
00097 {
00098     YButtonBoxLayoutPolicy policy;
00099 
00100     policy.buttonOrder           = YKDEButtonOrder;
00101     policy.equalSizeButtons      = false;
00102     policy.alignment[ YD_HORIZ ] = YAlignCenter;
00103     policy.alignment[ YD_VERT  ] = YAlignBegin; // Align top
00104 
00105     return policy;
00106 }
00107 
00108 
00109 YButtonBoxLayoutPolicy
00110 YButtonBox::gnomeLayoutPolicy()
00111 {
00112     YButtonBoxLayoutPolicy policy;
00113 
00114     policy.buttonOrder           = YGnomeButtonOrder;
00115     policy.equalSizeButtons      = true;
00116     policy.alignment[ YD_HORIZ ] = YAlignEnd;   // Align right
00117     policy.alignment[ YD_VERT  ] = YAlignBegin; // Align top
00118     policy.addExcessSpaceToHelpButtonExtraMargin = true;
00119 
00120     return policy;
00121 }
00122 
00123 
00124 void
00125 YButtonBox::setDefaultMargins( const YButtonBoxMargins & margins )
00126 {
00127     _defaultMargins = margins;
00128 }
00129 
00130 
00131 YButtonBoxMargins
00132 YButtonBox::defaultMargins()
00133 {
00134     return _defaultMargins;
00135 }
00136 
00137 
00138 void
00139 YButtonBox::setMargins( const YButtonBoxMargins & margins )
00140 {
00141     priv->margins = margins;
00142 }
00143 
00144 
00145 YButtonBoxMargins
00146 YButtonBox::margins() const
00147 {
00148     return priv->margins;
00149 }
00150 
00151 
00152 void
00153 YButtonBox::setSize( int newWidth, int newHeight )
00154 {
00155     sanityCheck();
00156     doLayout( newWidth, newHeight );
00157 }
00158 
00159 
00160 void
00161 YButtonBox::doLayout( int width, int height )
00162 {
00163     std::vector<YPushButton *> buttons = buttonsByButtonOrder();
00164 
00165     if ( buttons.empty() )
00166         return;
00167 
00168     YPushButton * helpButton = findButton( YHelpButton );
00169 
00170     int prefWidth  = preferredWidth();
00171     int prefHeight = preferredHeight();
00172     YButtonBoxMargins margins = priv->margins;
00173     bool equalSizeButtons = _layoutPolicy.equalSizeButtons;
00174 
00175 
00176     //
00177     // Horizontal layout
00178     //
00179 
00180     if ( width < prefWidth ) // Not enough horizontal space
00181     {
00182         if ( equalSizeButtons )
00183         {
00184             int buttonWidthWithoutMargins = maxChildSize( YD_HORIZ ) * buttons.size();
00185 
00186             if ( width < buttonWidthWithoutMargins )
00187             {
00188                 // The missing width can't be compensated by reducing margins and spacings.
00189                 // Try not enforcing the same width:
00190                 //
00191                 // If one button is very much larger than most others, that one
00192                 // button will greatly distort the overall layout. If we simply cut
00193                 // some pixels off every button, for sure that one very large
00194                 // button will become unreadable. So let's try first with buttons
00195                 // getting just the size they really need.
00196                 //
00197                 // Of course, we might still have cut some pixels off all buttons
00198                 // if that also is too wide, but in that case we can't do very much
00199                 // anyway.
00200 
00201                 equalSizeButtons = false;
00202                 prefWidth = preferredWidth( equalSizeButtons );
00203             }
00204         }
00205     }
00206 
00207     int widthLoss = 0;
00208 
00209     if ( width < prefWidth ) // Not enough horizontal space
00210     {
00211         // Try reducing margins
00212 
00213         int missing = prefWidth - width;
00214 
00215         if ( missing <= margins.left + margins.right )
00216         {
00217             margins.left  -= missing / 2;
00218             margins.right -= missing / 2;
00219             missing = 0;
00220         }
00221         else
00222         {
00223             missing -= margins.left;
00224             missing -= margins.right;
00225             margins.left  = 0;
00226             margins.right = 0;
00227         }
00228 
00229         if ( missing > 0 && buttons.size() > 1 )
00230         {
00231             // Try reducing spacing
00232 
00233             int totalSpacing = ( buttons.size() - 1 ) * margins.spacing;
00234 
00235             if ( missing <= totalSpacing )
00236             {
00237                 totalSpacing -= missing;
00238                 margins.spacing = totalSpacing / ( buttons.size() - 1 );
00239                 missing = 0;
00240             }
00241             else
00242             {
00243                 missing -= totalSpacing;
00244                 margins.spacing = 0;
00245             }
00246         }
00247 
00248         if ( missing > 0 && helpButton )
00249         {
00250             // Try reducing help button extra spacing
00251 
00252             if ( missing <= margins.helpButtonExtraSpacing )
00253             {
00254                 margins.helpButtonExtraSpacing -= missing;
00255                 missing = 0;
00256             }
00257             else
00258             {
00259                 missing -= margins.helpButtonExtraSpacing;
00260                 margins.helpButtonExtraSpacing = 0;
00261             }
00262         }
00263 
00264 
00265         // Distribute missing width among all buttons
00266 
00267         if ( missing > 0 )
00268             widthLoss = missing / buttons.size();
00269     }
00270 
00271     if ( width > prefWidth ) // Excess horizontal space
00272     {
00273         int excessWidth = width - prefWidth;
00274 
00275         if ( _layoutPolicy.addExcessSpaceToHelpButtonExtraMargin && helpButton )
00276         {
00277             margins.helpButtonExtraSpacing += excessWidth;
00278         }
00279         else
00280         {
00281             switch ( _layoutPolicy.alignment[ YD_HORIZ ] )
00282             {
00283                 case YAlignCenter:
00284                     margins.left  += excessWidth / 2;
00285                     margins.right += excessWidth / 2;
00286                     break;
00287 
00288                 case YAlignBegin:
00289                 case YAlignUnchanged:
00290                     margins.right += excessWidth;
00291                     break;
00292 
00293                 case YAlignEnd:
00294                     margins.left  += excessWidth;
00295                     break;
00296             }
00297         }
00298     }
00299 
00300 
00301     //
00302     // Vertical layout
00303     //
00304 
00305     int buttonHeight = maxChildSize( YD_VERT );
00306 
00307     if ( height < prefHeight ) // Not enough vertical space
00308     {
00309         // Reduce margins
00310 
00311         int missing = prefHeight - height;
00312 
00313         if ( missing < margins.top + margins.bottom )
00314         {
00315             margins.top    -= missing / 2;
00316             margins.bottom -= missing / 2;
00317         }
00318         else
00319         {
00320             margins.top    = 0;
00321             margins.bottom = 0;
00322         }
00323     }
00324 
00325     if ( height < buttonHeight )
00326     {
00327         buttonHeight = height;
00328     }
00329 
00330     int y_pos = margins.top;
00331 
00332     if ( height > prefHeight ) // Excess vertical space
00333     {
00334         // Distribute excess vertical space
00335 
00336         int excessHeight = height - buttonHeight;
00337         excessHeight -= margins.top;
00338         excessHeight -= margins.bottom;
00339 
00340         switch ( _layoutPolicy.alignment[ YD_VERT ] )
00341         {
00342             case YAlignBegin:           // Align top
00343             case YAlignUnchanged:
00344                 break;
00345 
00346             case YAlignCenter:
00347                 y_pos += excessHeight / 2;
00348                 break;
00349 
00350             case YAlignEnd:             // Align bottom
00351                 y_pos += excessHeight;
00352                 break;
00353         }
00354     }
00355 
00356 
00357     //
00358     // Set child widget positions and sizes from left to right
00359     //
00360 
00361     int x_pos        = margins.left;
00362     int buttonWidth  = 0;
00363 
00364     if ( equalSizeButtons )
00365     {
00366         buttonWidth  = maxChildSize( YD_HORIZ );
00367         buttonWidth -= widthLoss;
00368     }
00369 
00370     bool reverseLayout = YUI::app()->reverseLayout();
00371 
00372     for ( std::vector<YPushButton *>::iterator it = buttons.begin();
00373           it != buttons.end();
00374           ++it )
00375     {
00376         YPushButton * button = *it;
00377 
00378         // Extra spacing left of [Help] button
00379         // (Only if this isn't the first button)
00380 
00381         if ( button == helpButton && button != buttons.front() )
00382             x_pos += margins.helpButtonExtraSpacing;
00383 
00384         if ( ! equalSizeButtons )
00385         {
00386             buttonWidth  = button->preferredWidth();
00387             buttonWidth -= widthLoss;
00388         }
00389 
00390         button->setSize( buttonWidth, buttonHeight );
00391 
00392         if ( reverseLayout )
00393             moveChild( button, width - x_pos - buttonWidth, y_pos );
00394         else
00395             moveChild( button, x_pos, y_pos );
00396 
00397         x_pos += buttonWidth;
00398         x_pos += margins.spacing;
00399 
00400 
00401         // Extra spacing right of [Help] button
00402 
00403         if ( button == helpButton )
00404             x_pos += margins.helpButtonExtraSpacing;
00405     }
00406 }
00407 
00408 
00409 std::vector<YPushButton *>
00410 YButtonBox::buttonsByButtonOrder()
00411 {
00412     std::vector<YPushButton *> specialButtons( YMaxButtonRole, (YPushButton *) 0 );
00413     std::vector<YPushButton *> customButtons;
00414 
00415     for ( YWidgetListConstIterator it = childrenBegin();
00416           it != childrenEnd();
00417           ++it )
00418     {
00419         YPushButton * button = dynamic_cast<YPushButton *> (*it);
00420 
00421         if ( ! button )
00422             YUI_THROW( YUIInvalidChildException<YWidget>( this, *it ) );
00423 
00424         switch ( button->role() )
00425         {
00426             case YOKButton:
00427             case YCancelButton:
00428             case YApplyButton:
00429             case YHelpButton:
00430 
00431                 if ( specialButtons[ button->role() ] ) // Only one of each of those is allowed
00432                 {
00433                     std::string msg = "Multiple buttons with that role [";
00434                     msg += button->debugLabel();
00435                     msg += "]";
00436                     YUI_THROW( YUIButtonRoleMismatchException( msg ) );
00437                 }
00438                 else
00439                 {
00440                     specialButtons[ button->role() ] = button;
00441                 }
00442                 break;
00443 
00444             case YCustomButton:
00445                 customButtons.push_back( button );
00446                 break;
00447 
00448             case YMaxButtonRole:
00449                 YUI_THROW( YUIButtonRoleMismatchException( "Invalid button role" ) );
00450                 break;
00451         }
00452     }
00453 
00454     std::vector<YPushButton *> buttons;
00455 
00456     if ( _layoutPolicy.buttonOrder == YKDEButtonOrder )
00457     {
00458         if ( specialButtons[ YOKButton     ] )  buttons.push_back( specialButtons[ YOKButton     ] );
00459         if ( specialButtons[ YApplyButton  ] )  buttons.push_back( specialButtons[ YApplyButton  ] );
00460         if ( specialButtons[ YCancelButton ] )  buttons.push_back( specialButtons[ YCancelButton ] );
00461 
00462         buttons.insert( buttons.end(), customButtons.begin(), customButtons.end() );
00463 
00464         if ( specialButtons[ YHelpButton   ] )  buttons.push_back( specialButtons[ YHelpButton   ] );
00465     }
00466     else // YGnomeButtonOrder
00467     {
00468         if ( specialButtons[ YHelpButton   ] )  buttons.push_back( specialButtons[ YHelpButton   ] );
00469 
00470         buttons.insert( buttons.end(), customButtons.begin(), customButtons.end() );
00471 
00472         if ( specialButtons[ YApplyButton  ] )  buttons.push_back( specialButtons[ YApplyButton  ] );
00473         if ( specialButtons[ YCancelButton ] )  buttons.push_back( specialButtons[ YCancelButton ] );
00474         if ( specialButtons[ YOKButton     ] )  buttons.push_back( specialButtons[ YOKButton     ] );
00475     }
00476 
00477     return buttons;
00478 }
00479 
00480 
00481 
00482 int
00483 YButtonBox::preferredWidth( bool equalSizeButtons )
00484 {
00485     if ( childrenCount() < 1 )
00486         return 0;
00487 
00488     int width = ( childrenCount() - 1 ) * priv->margins.spacing;
00489 
00490     if ( equalSizeButtons )
00491         width += maxChildSize( YD_HORIZ ) * childrenCount();
00492     else
00493         width += totalChildrenWidth();
00494 
00495     width += priv->margins.left;
00496     width += priv->margins.right;
00497 
00498     if ( priv->margins.helpButtonExtraSpacing )
00499     {
00500         if ( findButton( YHelpButton ) )
00501             width += priv->margins.helpButtonExtraSpacing;
00502     }
00503 
00504     return width;
00505 }
00506 
00507 
00508 int
00509 YButtonBox::preferredWidth()
00510 {
00511     return preferredWidth( _layoutPolicy.equalSizeButtons );
00512 }
00513 
00514 
00515 int
00516 YButtonBox::preferredHeight()
00517 {
00518     int height = maxChildSize( YD_VERT );
00519     height += priv->margins.top;
00520     height += priv->margins.bottom;
00521 
00522     return height;
00523 }
00524 
00525 
00526 int
00527 YButtonBox::maxChildSize( YUIDimension dim ) const
00528 {
00529     int maxSize = 0;
00530 
00531     for ( YWidgetListConstIterator it = childrenBegin();
00532           it != childrenEnd();
00533           ++it )
00534     {
00535         maxSize = max( maxSize, (*it)->preferredSize( dim ) );
00536     }
00537 
00538     return maxSize;
00539 }
00540 
00541 
00542 int
00543 YButtonBox::totalChildrenWidth() const
00544 {
00545     int totalWidth = 0;
00546 
00547     for ( YWidgetListConstIterator it = childrenBegin();
00548           it != childrenEnd();
00549           ++it )
00550     {
00551         totalWidth += (*it)->preferredWidth();
00552     }
00553 
00554     return totalWidth;
00555 }
00556 
00557 
00558 bool
00559 YButtonBox::stretchable( YUIDimension dimension ) const
00560 {
00561     switch ( dimension )
00562     {
00563         case YD_HORIZ:  return true;
00564         case YD_VERT :  return false;
00565 
00566         default:
00567             YUI_THROW( YUIInvalidDimensionException() );
00568             return 0;
00569     }
00570 }
00571 
00572 
00573 YPushButton *
00574 YButtonBox::findButton( YButtonRole role )
00575 {
00576     for ( YWidgetListConstIterator it = childrenBegin();
00577           it != childrenEnd();
00578           ++it )
00579     {
00580         YPushButton * button = dynamic_cast<YPushButton *> (*it);
00581 
00582         if ( button && button->role() == role )
00583             return button;
00584     }
00585 
00586     return 0;
00587 }
00588 
00589 
00590 void
00591 YButtonBox::setSanityCheckRelaxed( bool relaxed )
00592 {
00593     priv->sanityCheckRelaxed = relaxed;
00594 }
00595 
00596 
00597 bool
00598 YButtonBox::sanityCheckRelaxed() const
00599 {
00600     return priv->sanityCheckRelaxed;
00601 }
00602 
00603 
00604 void
00605 YButtonBox::sanityCheck()
00606 {
00607     YPushButton * okButton     = 0;
00608     YPushButton * cancelButton = 0;
00609 
00610     for ( YWidgetListConstIterator it = childrenBegin();
00611           it != childrenEnd();
00612           ++it )
00613     {
00614         YPushButton * button = dynamic_cast<YPushButton *> (*it);
00615 
00616         if ( ! button )
00617             YUI_THROW( YUIInvalidChildException<YWidget>( this, *it ) );
00618 
00619         switch ( button->role() )
00620         {
00621             case YOKButton:
00622 
00623                 if ( okButton )
00624                     YUI_THROW( YUIButtonRoleMismatchException( "Multiple buttons with role [OK]" ) );
00625                 else
00626                     okButton = button;
00627                 break;
00628 
00629 
00630             case YCancelButton:
00631 
00632                 if ( cancelButton )
00633                     YUI_THROW( YUIButtonRoleMismatchException( "Multiple buttons with role [Cancel]" ) );
00634                 else
00635                     cancelButton = button;
00636                 break;
00637 
00638 
00639             default:
00640                 break;
00641         }
00642     }
00643 
00644     if ( childrenCount() > 1 && ! sanityCheckRelaxed() )
00645     {
00646         if ( ! okButton || ! cancelButton )
00647             YUI_THROW( YUIButtonRoleMismatchException( "Button role mismatch: Must have both [OK] and [Cancel] roles" ) );
00648     }
00649 }
 All Classes Functions Variables Enumerations Friends