File indexing completed on 2024-05-12 04:38:17

0001 /*
0002     SPDX-FileCopyrightText: 2004 Till Adam <adam@kde.org>,
0003     SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only
0006 */
0007 
0008 #include "progressdialog.h"
0009 #include "progressmanager.h"
0010 
0011 #include <KLocalizedString>
0012 
0013 #include <QCloseEvent>
0014 #include <QFrame>
0015 #include <QHBoxLayout>
0016 #include <QLabel>
0017 #include <QObject>
0018 #include <QProgressBar>
0019 #include <QPushButton>
0020 #include <QScrollBar>
0021 #include <QTimer>
0022 #include <QToolButton>
0023 #include <QVBoxLayout>
0024 
0025 namespace KDevelop {
0026 
0027 static const int MAX_LABEL_WIDTH = 650;
0028 
0029 class TransactionItem;
0030 
0031 TransactionItemView::TransactionItemView( QWidget *parent, const char *name )
0032     : QScrollArea( parent )
0033 {
0034     setObjectName(QString::fromUtf8(name));
0035     setFrameStyle( NoFrame );
0036     mBigBox = new QWidget( this );
0037     auto layout = new QVBoxLayout(mBigBox);
0038     layout->setContentsMargins(0, 0, 0, 0);
0039     setWidget( mBigBox );
0040     setWidgetResizable( true );
0041     setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
0042 }
0043 
0044 TransactionItem *TransactionItemView::addTransactionItem( ProgressItem *item, bool first )
0045 {
0046     auto *ti = new TransactionItem( mBigBox, item, first );
0047     mBigBox->layout()->addWidget( ti );
0048 
0049     resize( mBigBox->width(), mBigBox->height() );
0050 
0051     return ti;
0052 }
0053 
0054 void TransactionItemView::resizeEvent ( QResizeEvent *event )
0055 {
0056     // Tell the layout in the parent (progressdialog) that our size changed
0057     updateGeometry();
0058 
0059     QSize sz = parentWidget()->sizeHint();
0060     int currentWidth = parentWidget()->width();
0061 
0062     // Don't resize to sz.width() every time when it only reduces a little bit
0063     if ( currentWidth < sz.width() || currentWidth > sz.width() + 100 ) {
0064         currentWidth = sz.width();
0065     }
0066     parentWidget()->resize( currentWidth, sz.height() );
0067 
0068     QScrollArea::resizeEvent( event );
0069 }
0070 
0071 QSize TransactionItemView::sizeHint() const
0072 {
0073     return minimumSizeHint();
0074 }
0075 
0076 QSize TransactionItemView::minimumSizeHint() const
0077 {
0078     int f = 2 * frameWidth();
0079     // Make room for a vertical scrollbar in all cases, to avoid a horizontal one
0080     int vsbExt = verticalScrollBar()->sizeHint().width();
0081     QSize sz( mBigBox->minimumSizeHint() );
0082     sz.setWidth( sz.width() + f + vsbExt );
0083     sz.setHeight( sz.height() + f );
0084     return sz;
0085 }
0086 
0087 void TransactionItemView::slotItemCompleted(TransactionItem* item)
0088 {
0089     // If completed item is the first, hide separator line for the one that will become first now
0090     if (mBigBox->layout()->indexOf(item) == 0) {
0091         auto *secondItem = mBigBox->layout()->itemAt(1);
0092         if (secondItem) {
0093             static_cast<TransactionItem *>(secondItem->widget())->hideHLine();
0094         }
0095     }
0096 
0097     mBigBox->layout()->removeWidget(item);
0098     delete item;
0099 
0100     //This slot is called whenever a TransactionItem is deleted, so this is a
0101     //good place to call updateGeometry(), so our parent takes the new size
0102     //into account and resizes.
0103     updateGeometry();
0104 }
0105 
0106 // ----------------------------------------------------------------------------
0107 
0108 TransactionItem::TransactionItem( QWidget *parent,
0109                                   ProgressItem *item, bool first )
0110     : QWidget( parent ), mCancelButton( nullptr ), mItem( item )
0111 {
0112     auto vbox = new QVBoxLayout(this);
0113     vbox->setSpacing( 2 );
0114     vbox->setContentsMargins(2, 2, 2, 2);
0115     setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
0116 
0117     mFrame = new QFrame( this );
0118     mFrame->setFrameShape( QFrame::HLine );
0119     mFrame->setFrameShadow( QFrame::Raised );
0120     mFrame->show();
0121     vbox->setStretchFactor( mFrame, 3 );
0122     vbox->addWidget( mFrame );
0123 
0124     auto* h = new QWidget( this );
0125     auto hboxLayout = new QHBoxLayout(h);
0126     hboxLayout->setContentsMargins(0, 0, 0, 0);
0127     hboxLayout->setSpacing( 5 );
0128     vbox->addWidget( h );
0129 
0130     mItemLabel =
0131             new QLabel( fontMetrics().elidedText( item->label(), Qt::ElideRight, MAX_LABEL_WIDTH ), h );
0132     h->layout()->addWidget( mItemLabel );
0133     h->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
0134 
0135     mProgress = new QProgressBar( h );
0136     hboxLayout->addWidget(mProgress);
0137     mProgress->setMaximum( 100 );
0138     mProgress->setValue( item->progress() );
0139     h->layout()->addWidget( mProgress );
0140 
0141     if ( item->canBeCanceled() ) {
0142         mCancelButton = new QPushButton( QIcon::fromTheme( QStringLiteral("dialog-cancel") ), QString(), h );
0143         hboxLayout->addWidget(mCancelButton);
0144         mCancelButton->setToolTip( i18nc("@info:tooltip", "Cancel this operation" ) );
0145         connect ( mCancelButton, &QPushButton::clicked,
0146                   this, &TransactionItem::slotItemCanceled);
0147         h->layout()->addWidget( mCancelButton );
0148     }
0149 
0150     h = new QWidget( this );
0151     hboxLayout = new QHBoxLayout(h);
0152     hboxLayout->setContentsMargins(0, 0, 0, 0);
0153     hboxLayout->setSpacing( 5 );
0154     h->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
0155     vbox->addWidget( h );
0156     mItemStatus = new QLabel( h );
0157     hboxLayout->addWidget(mItemStatus);
0158     mItemStatus->setTextFormat( Qt::RichText );
0159     mItemStatus->setText(
0160                 fontMetrics().elidedText( item->status(), Qt::ElideRight, MAX_LABEL_WIDTH ) );
0161     h->layout()->addWidget( mItemStatus );
0162     if ( first ) {
0163         hideHLine();
0164     }
0165 }
0166 
0167 TransactionItem::~TransactionItem()
0168 {
0169 }
0170 
0171 void TransactionItem::hideHLine()
0172 {
0173     mFrame->hide();
0174 }
0175 
0176 void TransactionItem::setProgress( int progress )
0177 {
0178     mProgress->setValue( progress );
0179 }
0180 
0181 void TransactionItem::setLabel( const QString &label )
0182 {
0183     mItemLabel->setText( fontMetrics().elidedText( label, Qt::ElideRight, MAX_LABEL_WIDTH ) );
0184 }
0185 
0186 void TransactionItem::setStatus( const QString &status )
0187 {
0188     mItemStatus->setText( fontMetrics().elidedText( status, Qt::ElideRight, MAX_LABEL_WIDTH ) );
0189 }
0190 
0191 void TransactionItem::setTotalSteps( int totalSteps )
0192 {
0193     mProgress->setMaximum( totalSteps );
0194 }
0195 
0196 void TransactionItem::slotItemCanceled()
0197 {
0198     if ( mItem ) {
0199         mItem->cancel();
0200     }
0201 }
0202 
0203 void TransactionItem::addSubTransaction( ProgressItem *item )
0204 {
0205     Q_UNUSED( item );
0206 }
0207 
0208 // ---------------------------------------------------------------------------
0209 
0210 ProgressDialog::ProgressDialog( QWidget *alignWidget, QWidget *parent, const char *name )
0211     : OverlayWidget( alignWidget, parent, name ), mWasLastShown( false )
0212 {
0213     setAutoFillBackground( true );
0214 
0215     mScrollView = new TransactionItemView( this, "ProgressScrollView" );
0216     layout()->addWidget( mScrollView );
0217 
0218     // No more close button for now, since there is no more autoshow
0219     /*
0220     QVBox* rightBox = new QVBox( this );
0221     QToolButton* pbClose = new QToolButton( rightBox );
0222     pbClose->setAutoRaise(true);
0223     pbClose->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
0224     pbClose->setFixedSize( 16, 16 );
0225     pbClose->setIcon( KIconLoader::global()->loadIconSet( "window-close", KIconLoader::Small, 14 ) );
0226     pbClose->setToolTip( i18n( "Hide detailed progress window" ) );
0227     connect(pbClose, SIGNAL(clicked()), this, SLOT(slotClose()));
0228     QWidget* spacer = new QWidget( rightBox ); // don't let the close button take up all the height
0229     rightBox->setStretchFactor( spacer, 100 );
0230   */
0231 
0232     /*
0233    * Get the singleton ProgressManager item which will inform us of
0234    * appearing and vanishing items.
0235    */
0236     ProgressManager *pm = ProgressManager::instance();
0237     connect ( pm, &ProgressManager::progressItemAdded,
0238               this, &ProgressDialog::slotTransactionAdded );
0239     connect ( pm, &ProgressManager::progressItemCompleted,
0240               this, &ProgressDialog::slotTransactionCompleted );
0241     connect ( pm, &ProgressManager::progressItemProgress,
0242               this, &ProgressDialog::slotTransactionProgress );
0243     connect ( pm, &ProgressManager::progressItemStatus,
0244               this, &ProgressDialog::slotTransactionStatus );
0245     connect ( pm, &ProgressManager::progressItemLabel,
0246               this, &ProgressDialog::slotTransactionLabel );
0247     connect ( pm, &ProgressManager::progressItemUsesBusyIndicator,
0248               this, &ProgressDialog::slotTransactionUsesBusyIndicator );
0249     connect ( pm, &ProgressManager::showProgressDialog,
0250               this, &ProgressDialog::slotShow );
0251 }
0252 
0253 void ProgressDialog::closeEvent( QCloseEvent *e )
0254 {
0255     e->accept();
0256     hide();
0257 }
0258 
0259 /*
0260  *  Destructor
0261  */
0262 ProgressDialog::~ProgressDialog()
0263 {
0264     // no need to delete child widgets.
0265 }
0266 
0267 void ProgressDialog::slotTransactionAdded( ProgressItem *item )
0268 {
0269     if ( item->parent() ) {
0270         const auto parentItemIt = mTransactionsToListviewItems.constFind(item->parent());
0271         if (parentItemIt != mTransactionsToListviewItems.constEnd()) {
0272             TransactionItem* parent = *parentItemIt;
0273             parent->addSubTransaction( item );
0274         }
0275     } else {
0276         const bool first = mTransactionsToListviewItems.empty();
0277         TransactionItem *ti = mScrollView->addTransactionItem( item, first );
0278         if ( ti ) {
0279             mTransactionsToListviewItems.insert( item, ti );
0280         }
0281         if ( first && mWasLastShown ) {
0282             QTimer::singleShot( 1000, this, &ProgressDialog::slotShow );
0283         }
0284 
0285     }
0286 }
0287 
0288 void ProgressDialog::slotTransactionCompleted( ProgressItem *item )
0289 {
0290     const auto itemIt = mTransactionsToListviewItems.find(item);
0291     if (itemIt != mTransactionsToListviewItems.end()) {
0292         TransactionItem* ti = *itemIt;
0293         mTransactionsToListviewItems.erase(itemIt);
0294         ti->setItemComplete();
0295         QTimer::singleShot( 3000, mScrollView, [=] { mScrollView->slotItemCompleted(ti); } );
0296     }
0297     // This was the last item, hide.
0298     if ( mTransactionsToListviewItems.empty() ) {
0299         QTimer::singleShot( 3000, this, &ProgressDialog::slotHide );
0300     }
0301 }
0302 
0303 void ProgressDialog::slotTransactionCanceled( ProgressItem * )
0304 {
0305 }
0306 
0307 void ProgressDialog::slotTransactionProgress( ProgressItem *item,
0308                                               unsigned int progress )
0309 {
0310     const auto itemIt = mTransactionsToListviewItems.constFind(item);
0311     if (itemIt != mTransactionsToListviewItems.constEnd()) {
0312         TransactionItem* ti = *itemIt;
0313         ti->setProgress( progress );
0314     }
0315 }
0316 
0317 void ProgressDialog::slotTransactionStatus( ProgressItem *item,
0318                                             const QString &status )
0319 {
0320     const auto itemIt = mTransactionsToListviewItems.constFind(item);
0321     if (itemIt != mTransactionsToListviewItems.constEnd()) {
0322         TransactionItem* ti = *itemIt;
0323         ti->setStatus( status );
0324     }
0325 }
0326 
0327 void ProgressDialog::slotTransactionLabel( ProgressItem *item,
0328                                            const QString &label )
0329 {
0330     const auto itemIt = mTransactionsToListviewItems.constFind(item);
0331     if (itemIt != mTransactionsToListviewItems.constEnd()) {
0332         TransactionItem* ti = *itemIt;
0333         ti->setLabel( label );
0334     }
0335 }
0336 
0337 void ProgressDialog::slotTransactionUsesBusyIndicator( KDevelop::ProgressItem *item, bool value )
0338 {
0339     const auto itemIt = mTransactionsToListviewItems.constFind(item);
0340     if (itemIt != mTransactionsToListviewItems.constEnd()) {
0341         TransactionItem* ti = *itemIt;
0342         if ( value ) {
0343             ti->setTotalSteps( 0 );
0344         } else {
0345             ti->setTotalSteps( 100 );
0346         }
0347     }
0348 }
0349 
0350 void ProgressDialog::slotShow()
0351 {
0352     setVisible( true );
0353 }
0354 
0355 void ProgressDialog::slotHide()
0356 {
0357     // check if a new item showed up since we started the timer. If not, hide
0358     if ( mTransactionsToListviewItems.isEmpty() ) {
0359         setVisible( false );
0360     }
0361     mWasLastShown = false;
0362 }
0363 
0364 void ProgressDialog::slotClose()
0365 {
0366     mWasLastShown = false;
0367     setVisible( false );
0368 }
0369 
0370 void ProgressDialog::setVisible( bool b )
0371 {
0372     OverlayWidget::setVisible( b );
0373     emit visibilityChanged( b );
0374 }
0375 
0376 void ProgressDialog::slotToggleVisibility()
0377 {
0378     /* Since we are only hiding with a timeout, there is a short period of
0379    * time where the last item is still visible, but clicking on it in
0380    * the statusbarwidget should not display the dialog, because there
0381    * are no items to be shown anymore. Guard against that.
0382    */
0383     mWasLastShown = isHidden();
0384     if ( !isHidden() || !mTransactionsToListviewItems.isEmpty() ) {
0385         setVisible( isHidden() );
0386     }
0387 }
0388 
0389 }
0390 
0391 #include "moc_progressdialog.cpp"