File indexing completed on 2025-01-05 04:25:45
0001 /**************************************************************************************** 0002 * Copyright (c) 2010 Rick W. Chen <stuffcorpse@archlinux.us> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "UpcomingEventsStackItem" 0018 0019 #include "UpcomingEventsStackItem.h" 0020 #include "UpcomingEventsStack.h" 0021 #include "core/support/Debug.h" 0022 #include "context/widgets/TextScrollingWidget.h" 0023 0024 #include <KIconLoader> 0025 #include <Plasma/FrameSvg> 0026 #include <Plasma/IconWidget> 0027 #include <Plasma/Label> 0028 #include <Plasma/PushButton> 0029 #include <Plasma/Theme> 0030 0031 #include <QAction> 0032 #include <QFontMetrics> 0033 #include <QGraphicsLinearLayout> 0034 #include <QGraphicsSceneResizeEvent> 0035 #include <QLabel> 0036 #include <QPainter> 0037 #include <QSignalMapper> 0038 #include <QStyleOptionGraphicsItem> 0039 #include <QWeakPointer> 0040 0041 class UpcomingEventsStackItemToolBox : public QGraphicsWidget 0042 { 0043 public: 0044 UpcomingEventsStackItemToolBox( QGraphicsWidget *parent ) 0045 : QGraphicsWidget( parent ) 0046 , m_background( new Plasma::FrameSvg(this) ) 0047 { 0048 m_background->setImagePath( "widgets/extender-dragger" ); 0049 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); 0050 updateTheme(); 0051 } 0052 0053 qreal iconSize() 0054 { 0055 return m_iconSize; 0056 } 0057 0058 void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ) 0059 { 0060 m_background->paintFrame( painter, option->exposedRect, option->exposedRect ); 0061 } 0062 0063 void updateTheme() 0064 { 0065 //Read the preferred icon size hint, look at the font size, and calculate the desired title bar 0066 //icon height. 0067 m_background->resize(); 0068 QSizeF size = m_background->elementSize( "hint-preferred-icon-size" ); 0069 size = size.expandedTo( QSizeF(KIconLoader::SizeSmall,KIconLoader::SizeSmall) ); 0070 0071 Plasma::Theme *theme = Plasma::Theme::defaultTheme(); 0072 QFont font = theme->font( Plasma::Theme::DefaultFont ); 0073 QFontMetrics fm( font ); 0074 m_iconSize = qMax( size.height(), (qreal) fm.height() ); 0075 } 0076 0077 void setBackgroundPrefix( const QString &string ) 0078 { 0079 if( string.isEmpty() || m_background->hasElementPrefix(string) ) 0080 { 0081 m_background->setElementPrefix( string ); 0082 update(); 0083 } 0084 } 0085 0086 const QString backgroundPrefix() const 0087 { 0088 return m_background->prefix(); 0089 } 0090 0091 protected: 0092 void resizeEvent( QGraphicsSceneResizeEvent *event ) 0093 { 0094 Q_UNUSED( event ) 0095 m_background->resizeFrame( size() ); 0096 } 0097 0098 private: 0099 Plasma::FrameSvg *m_background; 0100 QString m_prefix; 0101 qreal m_iconSize; 0102 }; 0103 0104 class UpcomingEventsStackItemPrivate 0105 { 0106 private: 0107 UpcomingEventsStackItem *const q_ptr; 0108 Q_DECLARE_PUBLIC( UpcomingEventsStackItem ) 0109 0110 public: 0111 UpcomingEventsStackItemPrivate( UpcomingEventsStackItem *parent ); 0112 ~UpcomingEventsStackItemPrivate(); 0113 0114 Plasma::IconWidget *collapseButton; 0115 Plasma::IconWidget *destroyButton; 0116 bool destroyButtonEnabled; 0117 QHash<QString, QAction*> actions; 0118 QSignalMapper *maximizeSignalMapper; 0119 0120 bool collapsed; 0121 QGraphicsLinearLayout *layout; 0122 QGraphicsLinearLayout *toolboxLayout; 0123 QString name; 0124 QString title; 0125 QString iconName; 0126 QWeakPointer<QGraphicsWidget> widget; 0127 TextScrollingWidget *titleLabel; 0128 UpcomingEventsStack *stack; 0129 UpcomingEventsStackItemToolBox *toolbox; 0130 0131 void _themeChanged(); 0132 void _toggleCollapse(); 0133 void _updateToolbox(); 0134 }; 0135 0136 UpcomingEventsStackItemPrivate::UpcomingEventsStackItemPrivate( UpcomingEventsStackItem *parent ) 0137 : q_ptr( parent ) 0138 , collapseButton( 0 ) 0139 , destroyButton( 0 ) 0140 , destroyButtonEnabled( false ) 0141 , maximizeSignalMapper( 0 ) 0142 , collapsed( false ) 0143 , layout( 0 ) 0144 , toolboxLayout( 0 ) 0145 , titleLabel( 0 ) 0146 , stack( 0 ) 0147 , toolbox( 0 ) 0148 { 0149 } 0150 0151 UpcomingEventsStackItemPrivate::~UpcomingEventsStackItemPrivate() 0152 { 0153 } 0154 0155 void 0156 UpcomingEventsStackItemPrivate::_themeChanged() 0157 { 0158 toolbox->updateTheme(); 0159 } 0160 0161 void 0162 UpcomingEventsStackItemPrivate::_toggleCollapse() 0163 { 0164 Q_Q( UpcomingEventsStackItem ); 0165 q->setCollapsed( !q->isCollapsed() ); 0166 } 0167 0168 void 0169 UpcomingEventsStackItemPrivate::_updateToolbox() 0170 { 0171 Q_ASSERT( toolbox ); 0172 Q_ASSERT( toolboxLayout ); 0173 Q_Q( UpcomingEventsStackItem ); 0174 const int startingIndex = 2; // collapse item is index 0, title label is 1 0175 const QSizeF widgetSize = collapseButton->sizeFromIconSize( toolbox->iconSize() ); 0176 titleLabel->setText( title ); 0177 0178 QHash<QAction *, QGraphicsWidget *> actionWidgets; 0179 for( int index = startingIndex; index < toolboxLayout->count(); ++index) 0180 { 0181 QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>( toolboxLayout->itemAt(index) ); 0182 QAction *widgetAction = 0; 0183 0184 if( !widget ) 0185 continue; 0186 else if( qobject_cast<Plasma::IconWidget*>(widget) ) 0187 widgetAction = static_cast<Plasma::IconWidget*>( widget )->action(); 0188 else if( qobject_cast<Plasma::PushButton*>(widget) ) 0189 widgetAction = static_cast<Plasma::PushButton*>( widget )->action(); 0190 else 0191 { 0192 toolboxLayout->removeAt(index); 0193 widget->deleteLater(); 0194 } 0195 0196 if( widget != destroyButton ) 0197 actionWidgets.insert( widgetAction, widget ); 0198 } 0199 0200 // ensure the collapseButton is the correct size. 0201 collapseButton->setMinimumSize( widgetSize ); 0202 collapseButton->setMaximumSize( widgetSize ); 0203 0204 // add the actions that are actually set to visible. 0205 foreach( QAction *action, actions.values() ) 0206 { 0207 if( action->isVisible() ) 0208 { 0209 Plasma::IconWidget *icon = qobject_cast<Plasma::IconWidget*>( actionWidgets.value(action) ); 0210 Plasma::PushButton *button = qobject_cast<Plasma::PushButton*>( actionWidgets.value(action) ); 0211 0212 if( action->icon().isNull() && !action->text().isNull() ) 0213 { 0214 if( !button ) 0215 { 0216 button = new Plasma::PushButton; 0217 button->setAction( action ); 0218 } 0219 button->setMinimumHeight( widgetSize.height() ); 0220 button->setMaximumHeight( widgetSize.height() ); 0221 button->setCursor( Qt::ArrowCursor ); 0222 toolboxLayout->insertItem( startingIndex, button ); 0223 } 0224 else 0225 { 0226 if( !icon ) 0227 { 0228 icon = new Plasma::IconWidget; 0229 icon->setAction( action ); 0230 } 0231 if( action->icon().isNull() ) 0232 icon->setText( action->text() ); 0233 0234 icon->setMinimumSize( widgetSize ); 0235 icon->setMaximumSize( widgetSize ); 0236 icon->setCursor( Qt::ArrowCursor ); 0237 toolboxLayout->insertItem( startingIndex, icon ); 0238 } 0239 } 0240 } 0241 0242 // add destroy button last 0243 if( destroyButtonEnabled ) 0244 { 0245 if( !destroyButton ) 0246 { 0247 QAction *closeAction = new QAction( q ); 0248 destroyButton = new Plasma::IconWidget( toolbox ); 0249 destroyButton->setAction( closeAction ); 0250 destroyButton->setSvg( QLatin1String("widgets/configuration-icons"), QLatin1String("close") ); 0251 destroyButton->setMinimumSize( widgetSize ); 0252 destroyButton->setMaximumSize( widgetSize ); 0253 destroyButton->setCursor( Qt::ArrowCursor ); 0254 QObject::connect( closeAction, SIGNAL(triggered()), q, SLOT(deleteLater()) ); 0255 } 0256 toolboxLayout->addItem( destroyButton ); 0257 } 0258 toolboxLayout->invalidate(); 0259 } 0260 0261 UpcomingEventsStackItem::UpcomingEventsStackItem( const QString &name, 0262 UpcomingEventsStack *parent) 0263 : QGraphicsWidget( parent ) 0264 , d_ptr( new UpcomingEventsStackItemPrivate( this ) ) 0265 { 0266 Q_ASSERT( parent ); 0267 Q_D( UpcomingEventsStackItem ); 0268 d->stack = parent; 0269 d->name = name; 0270 0271 //create the toolbox. 0272 d->toolbox = new UpcomingEventsStackItemToolBox( this ); 0273 d->toolboxLayout = new QGraphicsLinearLayout( d->toolbox ); 0274 0275 //create own layout 0276 d->layout = new QGraphicsLinearLayout( Qt::Vertical, this ); 0277 d->layout->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); 0278 d->layout->addItem( d->toolbox ); 0279 d->layout->setContentsMargins( 0, 0, 0, 0 ); 0280 0281 //create maximize action 0282 d->maximizeSignalMapper = new QSignalMapper( d->toolbox ); 0283 connect( d->maximizeSignalMapper, SIGNAL(mapped(QString)), d->stack, SLOT(maximizeItem(QString)) ); 0284 0285 Plasma::Svg svg; 0286 svg.setImagePath( QLatin1String("widgets/configuration-icons") ); 0287 QAction *maximizeAction = new QAction( svg.pixmap(QLatin1String("restore")), QString(), d->toolbox ); 0288 maximizeAction->setToolTip( i18n( "Maximize" ) ); 0289 connect( maximizeAction, SIGNAL(triggered()), d->maximizeSignalMapper, SLOT(map()) ); 0290 d->maximizeSignalMapper->setMapping( maximizeAction, d->name ); 0291 d->actions.insert( QLatin1String("maximize"), maximizeAction ); 0292 0293 d->collapseButton = new Plasma::IconWidget( d->toolbox ); 0294 d->collapseButton->setCursor( Qt::ArrowCursor ); 0295 d->titleLabel = new TextScrollingWidget( d->toolbox ); 0296 d->titleLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); 0297 0298 d->toolboxLayout->addItem( d->collapseButton ); 0299 d->toolboxLayout->addItem( d->titleLabel ); 0300 d->toolboxLayout->setStretchFactor( d->titleLabel, 10 ); 0301 connect( d->collapseButton, SIGNAL(clicked()), SLOT(_toggleCollapse()) ); 0302 0303 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); 0304 setContentsMargins( 0, 0, 0, 0 ); 0305 0306 d->_updateToolbox(); 0307 d->_themeChanged(); 0308 connect( Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), SLOT(_themeChanged()) ); 0309 } 0310 0311 UpcomingEventsStackItem::~UpcomingEventsStackItem() 0312 { 0313 delete d_ptr; 0314 } 0315 0316 void 0317 UpcomingEventsStackItem::setWidget( QGraphicsWidget *widget ) 0318 { 0319 Q_ASSERT( widget ); 0320 Q_D( UpcomingEventsStackItem ); 0321 if( d->widget.data() ) 0322 { 0323 d->layout->removeItem( d->widget.data() ); 0324 delete d->widget.data(); 0325 } 0326 widget->setParentItem( this ); 0327 d->widget = widget; 0328 d->layout->insertItem( 1, d->widget.data() ); 0329 d->layout->setItemSpacing( 0, 2 ); 0330 d->widget.data()->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); 0331 d->widget.data()->setVisible( !d->collapsed ); 0332 } 0333 0334 QGraphicsWidget * 0335 UpcomingEventsStackItem::widget() const 0336 { 0337 Q_D( const UpcomingEventsStackItem ); 0338 return d->widget.data(); 0339 } 0340 0341 void 0342 UpcomingEventsStackItem::setTitle( const QString &title ) 0343 { 0344 Q_D( UpcomingEventsStackItem ); 0345 d->title = title; 0346 d->_updateToolbox(); 0347 } 0348 0349 QString 0350 UpcomingEventsStackItem::title() const 0351 { 0352 Q_D( const UpcomingEventsStackItem ); 0353 return d->title; 0354 } 0355 0356 void 0357 UpcomingEventsStackItem::setName( const QString &name ) 0358 { 0359 Q_D( UpcomingEventsStackItem ); 0360 d->name = name; 0361 } 0362 0363 void 0364 UpcomingEventsStackItem::addAction( const QString &name, QAction *action ) 0365 { 0366 Q_ASSERT( action ); 0367 Q_D( UpcomingEventsStackItem ); 0368 d->actions.insert( name, action ); 0369 d->_updateToolbox(); 0370 } 0371 0372 QHash<QString, QAction *> 0373 UpcomingEventsStackItem::actions() const 0374 { 0375 Q_D( const UpcomingEventsStackItem ); 0376 return d->actions; 0377 } 0378 0379 QAction * 0380 UpcomingEventsStackItem::action( const QString &name ) const 0381 { 0382 Q_D( const UpcomingEventsStackItem ); 0383 return d->actions.value( name ); 0384 } 0385 0386 QString 0387 UpcomingEventsStackItem::name() const 0388 { 0389 Q_D( const UpcomingEventsStackItem ); 0390 return d->name; 0391 } 0392 0393 void 0394 UpcomingEventsStackItem::setIcon( const QIcon &icon ) 0395 { 0396 Q_D( UpcomingEventsStackItem ); 0397 d->collapseButton->setIcon( icon ); 0398 d->collapseButton->setVisible( !icon.isNull() ); 0399 d->_updateToolbox(); 0400 } 0401 0402 void 0403 UpcomingEventsStackItem::setIcon( const QString &icon ) 0404 { 0405 Q_D( UpcomingEventsStackItem ); 0406 if( icon != d->iconName ) 0407 { 0408 d->collapseButton->setIcon( icon ); 0409 d->iconName = icon; 0410 } 0411 } 0412 0413 QIcon 0414 UpcomingEventsStackItem::icon() const 0415 { 0416 Q_D( const UpcomingEventsStackItem ); 0417 return d->collapseButton->icon(); 0418 } 0419 0420 UpcomingEventsStack * 0421 UpcomingEventsStackItem::stack() const 0422 { 0423 Q_D( const UpcomingEventsStackItem ); 0424 return d->stack; 0425 } 0426 0427 bool 0428 UpcomingEventsStackItem::isCollapsed() const 0429 { 0430 Q_D( const UpcomingEventsStackItem ); 0431 return d->collapsed; 0432 } 0433 0434 void 0435 UpcomingEventsStackItem::setCollapsed( bool collapsed ) 0436 { 0437 Q_D( UpcomingEventsStackItem ); 0438 d->collapsed = collapsed; 0439 if( d->widget ) 0440 { 0441 prepareGeometryChange(); 0442 d->widget.data()->setVisible( !collapsed ); 0443 if( collapsed ) 0444 d->layout->removeItem( d->widget.data() ); 0445 else 0446 { 0447 d->layout->insertItem( 1, d->widget.data() ); 0448 d->layout->setItemSpacing( 0, 2 ); 0449 } 0450 d->toolboxLayout->invalidate(); 0451 emit collapseChanged( collapsed ); 0452 updateGeometry(); 0453 } 0454 d->collapseButton->setToolTip( collapsed ? i18n("Expand this widget") : i18n("Collapse this widget") ); 0455 } 0456 0457 void 0458 UpcomingEventsStackItem::showCloseButton( bool show ) 0459 { 0460 Q_D( UpcomingEventsStackItem ); 0461 d->destroyButtonEnabled = show; 0462 d->_updateToolbox(); 0463 } 0464 0465 void 0466 UpcomingEventsStackItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent *event ) 0467 { 0468 Q_D( UpcomingEventsStackItem ); 0469 if( d->toolbox->boundingRect().contains( event->pos() ) ) 0470 d->_toggleCollapse(); 0471 } 0472 0473 void 0474 UpcomingEventsStackItem::mousePressEvent( QGraphicsSceneMouseEvent *event ) 0475 { 0476 Q_D( UpcomingEventsStackItem ); 0477 if( !(d->toolbox->boundingRect().contains(event->pos())) ) 0478 event->ignore(); 0479 } 0480 0481 QSizeF 0482 UpcomingEventsStackItem::sizeHint( Qt::SizeHint which, const QSizeF &constraint ) const 0483 { 0484 Q_D( const UpcomingEventsStackItem ); 0485 QSizeF size = d->toolbox->effectiveSizeHint( which, constraint ); 0486 if( !d->collapsed && d->widget ) 0487 { 0488 size.rheight() += d->layout->itemSpacing( 1 ) * 2; 0489 size.rheight() += d->widget.data()->effectiveSizeHint( which, constraint ).height(); 0490 } 0491 return size; 0492 } 0493 0494 QRectF 0495 UpcomingEventsStackItem::boundingRect() const 0496 { 0497 Q_D( const UpcomingEventsStackItem ); 0498 if( !d->collapsed && d->widget ) 0499 { 0500 QSharedPointer<QGraphicsWidget> w = d->widget.toStrongRef(); 0501 if( w ) 0502 return d->toolbox->boundingRect().united( mapRectFromItem( w.data(), w->boundingRect() ) ); 0503 } 0504 return d->toolbox->boundingRect(); 0505 } 0506