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