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