File indexing completed on 2024-11-24 03:57:48
0001 /** 0002 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 0003 * 0004 * This file is part of the KGantt library. 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "mainwindow.h" 0010 0011 #include "ui_mainwindow.h" 0012 #include "entrydelegate.h" 0013 #include "entrydialog.h" 0014 0015 #include <algorithm> 0016 0017 #include <KGanttConstraintModel> 0018 #include <KGanttDateTimeGrid> 0019 #include <KGanttGraphicsView> 0020 #include <KGanttLegend> 0021 #include <QAbstractItemView> 0022 #include <QDebug> 0023 #include <QHeaderView> 0024 #include <QStandardItemModel> 0025 #include <QTreeView> 0026 #include <QCloseEvent> 0027 #include <QPointer> 0028 #include <QScrollBar> 0029 0030 class MyStandardItem : public QStandardItem { 0031 public: 0032 MyStandardItem( const QVariant& v ) : QStandardItem() 0033 { 0034 setData( v, Qt::DisplayRole ); 0035 } 0036 MyStandardItem( const QString& v ) : QStandardItem() 0037 { 0038 setData( v, Qt::DisplayRole ); 0039 } 0040 }; 0041 0042 MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags ) 0043 : QMainWindow( parent, flags ), 0044 smallLegend( nullptr ), 0045 detailedLegend( nullptr ), 0046 ui( new Ui::MainWindow ) 0047 { 0048 ui->setupUi( this ); 0049 0050 initModel(); 0051 initActions(); 0052 initItemDelegate(); 0053 initGrid(); 0054 0055 QTreeView* leftView = qobject_cast<QTreeView*>( ui->ganttView->leftView() ); 0056 Q_ASSERT( leftView ); 0057 leftView->setColumnHidden( 1, true ); 0058 leftView->setColumnHidden( 2, true ); 0059 leftView->setColumnHidden( 3, true ); 0060 leftView->setColumnHidden( 4, true ); 0061 leftView->setColumnHidden( 5, true ); 0062 leftView->header()->setStretchLastSection( true ); 0063 0064 connect( ui->ganttView->leftView(), SIGNAL(customContextMenuRequested(QPoint)), 0065 this, SLOT(showContextMenu(QPoint)) ); 0066 connect( ui->ganttView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 0067 this, SLOT(enableActions(QItemSelection)) ); 0068 } 0069 0070 MainWindow::~MainWindow() 0071 { 0072 } 0073 0074 void MainWindow::closeEvent(QCloseEvent *event) 0075 { 0076 delete smallLegend; 0077 delete detailedLegend; 0078 event->accept(); 0079 } 0080 0081 void MainWindow::initModel() 0082 { 0083 model = new QStandardItemModel( 0, 6, this ); 0084 model->setHeaderData( 0, Qt::Horizontal, tr( "Tree View of Entries" ) ); 0085 ui->ganttView->setModel( model ); 0086 0087 QStandardItemModel* lmodel = new QStandardItemModel; 0088 lmodel->appendRow( QList<QStandardItem*>() 0089 << new MyStandardItem( QVariant() ) 0090 << new MyStandardItem( KGantt::TypeEvent ) 0091 << new MyStandardItem( QVariant() ) 0092 << new MyStandardItem( QVariant() ) 0093 << new MyStandardItem( QVariant() ) 0094 << new MyStandardItem( QString::fromLatin1("Event") ) ); 0095 lmodel->appendRow( QList<QStandardItem*>() 0096 << new MyStandardItem( QVariant() ) 0097 << new MyStandardItem( KGantt::TypeTask ) 0098 << new MyStandardItem( QVariant() ) 0099 << new MyStandardItem( QVariant() ) 0100 << new MyStandardItem( QVariant() ) 0101 << new MyStandardItem( QString::fromLatin1("Task") ) ); 0102 lmodel->appendRow( QList<QStandardItem*>() 0103 << new MyStandardItem( QVariant() ) 0104 << new MyStandardItem( KGantt::TypeSummary ) 0105 << new MyStandardItem( QVariant() ) 0106 << new MyStandardItem( QVariant() ) 0107 << new MyStandardItem( QVariant() ) 0108 << new MyStandardItem( QString::fromLatin1("Summary") ) ); 0109 0110 smallLegend = new KGantt::Legend(); 0111 smallLegend->setWindowTitle( tr( "Legend" ) ); 0112 smallLegend->show(); 0113 smallLegend->setModel( lmodel ); 0114 0115 detailedLegend = new KGantt::Legend(); 0116 detailedLegend->setWindowTitle( tr( "List" ) ); 0117 detailedLegend->show(); 0118 detailedLegend->setModel( model ); 0119 0120 constraintModel = new KGantt::ConstraintModel( this ); 0121 ui->ganttView->setConstraintModel( constraintModel ); 0122 } 0123 0124 void MainWindow::initActions() 0125 { 0126 newEntryAction = new QAction( tr( "New entry" ), this ); 0127 newEntryAction->setShortcut( QKeySequence::New ); 0128 connect( newEntryAction, SIGNAL(triggered()), this, SLOT(addNewEntry()) ); 0129 0130 removeEntryAction = new QAction( tr( "Remove entry" ), this ); 0131 removeEntryAction->setShortcut( QKeySequence::Delete ); 0132 connect( removeEntryAction, SIGNAL(triggered()), this, SLOT(removeEntry()) ); 0133 0134 zoomInAction = new QAction( tr( "Zoom In" ), this ); 0135 zoomInAction->setShortcut( QKeySequence::ZoomIn ); 0136 connect( zoomInAction, SIGNAL(triggered()), this, SLOT(zoomIn()) ); 0137 0138 zoomOutAction = new QAction( tr( "Zoom Out" ), this ); 0139 zoomOutAction->setShortcut( QKeySequence::ZoomOut ); 0140 connect( zoomOutAction, SIGNAL(triggered()), this, SLOT(zoomOut()) ); 0141 0142 zoomFitAction = new QAction( tr( "Zoom to Fit" ), this ); 0143 connect( zoomFitAction, SIGNAL(triggered()), this, SLOT(zoomFit()) ); 0144 0145 ui->ganttView->leftView()->setContextMenuPolicy( Qt::CustomContextMenu ); 0146 ui->ganttView->leftView()->addAction( newEntryAction ); 0147 ui->ganttView->leftView()->addAction( removeEntryAction ); 0148 0149 QMenu* entryMenu = menuBar()->addMenu( tr( "Entry" ) ); 0150 entryMenu->addAction( newEntryAction ); 0151 entryMenu->addAction( removeEntryAction ); 0152 0153 QMenu* zoomMenu = menuBar()->addMenu( tr( "Zoom" ) ); 0154 zoomMenu->addAction( zoomInAction ); 0155 zoomMenu->addAction( zoomOutAction ); 0156 zoomMenu->addAction( zoomFitAction ); 0157 0158 QMenu* scaleMenu = menuBar()->addMenu( tr( "Scale" ) ); 0159 0160 scaleMenu->addAction( tr( "Auto" ), this, SLOT(scaleAuto()) ); 0161 scaleMenu->addAction( tr( "Hour" ), this, SLOT(scaleHour()) ); 0162 scaleMenu->addAction( tr( "Day" ), this, SLOT(scaleDay()) ); 0163 scaleMenu->addAction( tr( "Week" ), this, SLOT(scaleWeek()) ); 0164 scaleMenu->addAction( tr( "Month" ), this, SLOT(scaleMonth()) ); 0165 0166 enableActions( QItemSelection() ); 0167 } 0168 0169 void MainWindow::initItemDelegate() 0170 { 0171 EntryDelegate* delegate = new EntryDelegate( constraintModel, this ); 0172 ui->ganttView->leftView()->setItemDelegate( delegate ); 0173 } 0174 0175 void MainWindow::initGrid() 0176 { 0177 grid = new KGantt::DateTimeGrid(); 0178 grid->setDayWidth( 70 ); 0179 ui->ganttView->setGrid( grid ); 0180 } 0181 0182 void MainWindow::showContextMenu( const QPoint& pos ) 0183 { 0184 if ( !ui->ganttView->leftView()->indexAt( pos ).isValid() ) 0185 ui->ganttView->selectionModel()->clearSelection(); 0186 0187 QMenu menu( ui->ganttView->leftView() ); 0188 menu.addAction( newEntryAction ); 0189 menu.addAction( removeEntryAction ); 0190 menu.exec( ui->ganttView->leftView()->viewport()->mapToGlobal( pos ) ); 0191 } 0192 0193 void MainWindow::enableActions(const QItemSelection & selected) 0194 { 0195 if ( selected.indexes().isEmpty() ) { 0196 newEntryAction->setEnabled( true ); 0197 removeEntryAction->setEnabled( false ); 0198 return; 0199 } 0200 0201 QModelIndex selectedIndex = selected.indexes()[0]; 0202 0203 if ( model->data( model->index( selectedIndex.row(), 1 ) ) == KGantt::TypeEvent || 0204 model->data( model->index( selectedIndex.row(), 1 ) ) == KGantt::TypeTask ) { 0205 newEntryAction->setEnabled( false ); 0206 removeEntryAction->setEnabled( true ); 0207 return; 0208 } 0209 0210 newEntryAction->setEnabled( true ); 0211 removeEntryAction->setEnabled( true ); 0212 } 0213 0214 void MainWindow::addNewEntry() 0215 { 0216 QPointer<EntryDialog> dialog = new EntryDialog( model ); 0217 dialog->setWindowTitle( tr( "New Entry") ); 0218 if ( dialog->exec() == QDialog::Rejected || !dialog ) { 0219 delete dialog; 0220 return; 0221 } 0222 0223 QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); 0224 const QModelIndex parent = selectedIndexes.value( 0 ); 0225 0226 if ( !model->insertRow( model->rowCount( parent ), parent ) ) 0227 return; 0228 0229 int row = model->rowCount( parent ) - 1; 0230 if ( row == 0 && parent.isValid() ) 0231 model->insertColumns( model->columnCount( parent ), 5, parent ); 0232 0233 model->setData( model->index( row, 0, parent ), dialog->name() ); 0234 model->setData( model->index( row, 1, parent ), dialog->type() ); 0235 if ( dialog->type() != KGantt::TypeSummary ) { 0236 model->setData( model->index( row, 2, parent ), dialog->startDate(), KGantt::StartTimeRole ); 0237 model->setData( model->index( row, 3, parent ), dialog->endDate(), KGantt::EndTimeRole ); 0238 } 0239 model->setData( model->index( row, 4, parent ), dialog->completion() ); 0240 const QString legend( dialog->legend() ); 0241 if ( ! legend.isEmpty() ) 0242 model->setData( model->index( row, 5, parent ), legend ); 0243 0244 addConstraint( dialog->depends(), model->index( row, 0, parent ) ); 0245 setReadOnly( model->index( row, 0, parent ), dialog->readOnly() ); 0246 0247 delete dialog; 0248 } 0249 0250 void MainWindow::setReadOnly(const QModelIndex & index, bool readOnly) 0251 { 0252 int row = index.row(); 0253 const QModelIndex parent = index.parent(); 0254 QStandardItem* item; 0255 0256 item = model->itemFromIndex( model->index( row, 0, parent ) ); 0257 item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); 0258 0259 item = model->itemFromIndex( model->index( row, 1, parent ) ); 0260 item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); 0261 0262 item = model->itemFromIndex( model->index( row, 2, parent ) ); 0263 item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); 0264 0265 item = model->itemFromIndex( model->index( row, 3, parent ) ); 0266 item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); 0267 0268 item = model->itemFromIndex( model->index( row, 4, parent ) ); 0269 item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); 0270 } 0271 0272 void MainWindow::addConstraint(const QModelIndex & index1, const QModelIndex & index2) 0273 { 0274 if ( !index1.isValid() || !index2.isValid() ) 0275 return; 0276 0277 KGantt::Constraint c( index1, index2 ); 0278 ui->ganttView->constraintModel()->addConstraint( c ); 0279 } 0280 0281 void MainWindow::removeEntry() 0282 { 0283 QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); 0284 QModelIndex index = selectedIndexes.value( 0 ); 0285 0286 if ( !index.isValid() ) 0287 return; 0288 0289 model->removeRow( index.row(), index.parent() ); 0290 } 0291 0292 void MainWindow::zoomIn() 0293 { 0294 qreal dayWidth = grid->dayWidth() + 10; 0295 if ( dayWidth > 400 ) 0296 grid->setScale( KGantt::DateTimeGrid::ScaleHour ); 0297 0298 grid->setDayWidth( dayWidth ); 0299 } 0300 0301 void MainWindow::zoomOut() 0302 { 0303 qreal dayWidth = grid->dayWidth() - 10; 0304 if ( dayWidth < 10 ) 0305 dayWidth = 10; 0306 0307 if ( dayWidth <= 400 ) 0308 grid->setScale( KGantt::DateTimeGrid::ScaleDay ); 0309 0310 grid->setDayWidth( dayWidth ); 0311 } 0312 0313 void MainWindow::zoomFit() 0314 { 0315 const QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); 0316 0317 if ( selectedIndexes.isEmpty() ) { 0318 return; 0319 } 0320 0321 KGantt::Span span; 0322 for ( const QModelIndex &idx : selectedIndexes ) { 0323 const KGantt::Span s = grid->mapToChart( grid->model()->index( idx.row(), 0 ) ); 0324 if ( span.isValid() ) { 0325 span = span.expandedTo( s ); 0326 } else { 0327 span = s; 0328 } 0329 } 0330 0331 span.setLength( span.length()+20 ); 0332 span.setStart( span.start()-10 ); 0333 0334 qDebug() << selectedIndexes << span; 0335 0336 const qreal view_width = ui->ganttView->graphicsView()->viewport()->width(); 0337 const QDateTime start = grid->mapFromChart( span.start() ).value<QDateTime>(); 0338 const QDateTime end = grid->mapFromChart( span.end() ).value<QDateTime>(); 0339 0340 qreal delta = start.date().daysTo(end.date()); 0341 delta += start.time().msecsTo(end.time())/( 1000.*24.*60.*60. ); 0342 0343 qDebug() << view_width << "/" << delta; 0344 grid->setDayWidth( view_width/( std::max( 1., delta ) ) ); 0345 qDebug() << "daywidth set to" << grid->dayWidth(); 0346 qDebug() << "start scroll to" << grid->mapToChart( start ); 0347 ui->ganttView->graphicsView()->horizontalScrollBar()->setValue( grid->mapToChart( start ) ); 0348 } 0349 0350 void MainWindow::scaleAuto() 0351 { 0352 KGantt::DateTimeGrid* grid = static_cast<KGantt::DateTimeGrid*>(ui->ganttView->grid()); 0353 grid->setScale( KGantt::DateTimeGrid::ScaleAuto ); 0354 } 0355 0356 void MainWindow::scaleHour() 0357 { 0358 KGantt::DateTimeGrid* grid = static_cast<KGantt::DateTimeGrid*>(ui->ganttView->grid()); 0359 grid->setScale( KGantt::DateTimeGrid::ScaleHour ); 0360 } 0361 0362 void MainWindow::scaleDay() 0363 { 0364 KGantt::DateTimeGrid* grid = static_cast<KGantt::DateTimeGrid*>(ui->ganttView->grid()); 0365 grid->setScale( KGantt::DateTimeGrid::ScaleDay ); 0366 } 0367 0368 void MainWindow::scaleWeek() 0369 { 0370 KGantt::DateTimeGrid* grid = static_cast<KGantt::DateTimeGrid*>(ui->ganttView->grid()); 0371 grid->setScale( KGantt::DateTimeGrid::ScaleWeek ); 0372 } 0373 0374 void MainWindow::scaleMonth() 0375 { 0376 KGantt::DateTimeGrid* grid = static_cast<KGantt::DateTimeGrid*>(ui->ganttView->grid()); 0377 grid->setScale( KGantt::DateTimeGrid::ScaleMonth ); 0378 } 0379