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"