File indexing completed on 2024-11-24 03:57:49

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 #include "../common/projectmodel.h"
0011 
0012 #include <QAction>
0013 #include <QApplication>
0014 #include <QComboBox>
0015 #include <QMenuBar>
0016 #include <QMenu>
0017 #include <QModelIndex>
0018 #include <QItemSelectionModel>
0019 #include <QTreeView>
0020 #include <QDebug>
0021 #include <QBrush>
0022 #include <QPainter>
0023 #include <QPrinter>
0024 #include <QPrintDialog>
0025 #include <QFileDialog>
0026 #include <QLabel>
0027 #include <QGridLayout>
0028 #include <QVBoxLayout>
0029 #include <QHBoxLayout>
0030 #include <QFileInfo>
0031 #include <QLineEdit>
0032 #include <QCheckBox>
0033 #include <QPushButton>
0034 #include <QDialogButtonBox>
0035 
0036 #include <KGanttGlobal>
0037 #include <KGanttView>
0038 #include <KGanttItemDelegate>
0039 #include <KGanttDateTimeGrid>
0040 #include <KGanttStyleOptionGanttItem>
0041 #include <KGanttConstraintModel>
0042 #include <KGanttGraphicsView>
0043 #include <KGanttDateTimeTimeLine>
0044 
0045 class ItemTypeComboBox : public QComboBox {
0046     Q_OBJECT
0047     Q_PROPERTY( KGantt::ItemType itemType READ itemType WRITE setItemType )
0048 public:
0049     explicit ItemTypeComboBox( QWidget* parent = nullptr );
0050 
0051     KGantt::ItemType itemType() const;
0052 public Q_SLOTS:
0053     void setItemType( KGantt::ItemType typ );
0054 };
0055 
0056 ItemTypeComboBox::ItemTypeComboBox( QWidget* parent )
0057     : QComboBox( parent )
0058 {
0059     addItem( tr( "Task" ), QVariant( KGantt::TypeTask ) );
0060     addItem( tr( "Event" ), QVariant( KGantt::TypeEvent ) );
0061     addItem( tr( "Summary" ), QVariant( KGantt::TypeSummary ) );
0062 }
0063 
0064 KGantt::ItemType ItemTypeComboBox::itemType() const
0065 {
0066     return static_cast<KGantt::ItemType>( itemData( currentIndex() ).toInt() );
0067 }
0068 
0069 void ItemTypeComboBox::setItemType( KGantt::ItemType typ )
0070 {
0071     setCurrentIndex( typ-1 );
0072 }
0073 
0074 class MyItemDelegate : public KGantt::ItemDelegate {
0075 public:
0076     explicit MyItemDelegate( QObject* parent = nullptr );
0077 
0078     /*reimp*/ QWidget* createEditor( QWidget* parent,
0079                                      const QStyleOptionViewItem& option,
0080                                      const QModelIndex& idx ) const override;
0081     /*reimp*/ void setEditorData( QWidget* editor, const QModelIndex& index ) const override;
0082     /*reimp*/ void setModelData( QWidget* editor, QAbstractItemModel* model,
0083                                   const QModelIndex & index ) const override;
0084 protected:
0085     /*reimp*/void drawDisplay( QPainter* painter, const QStyleOptionViewItem & option,
0086                                const QRect& rect, const QString& text ) const override;
0087 };
0088 
0089 MyItemDelegate::MyItemDelegate( QObject* parent )
0090     : KGantt::ItemDelegate( parent )
0091 {
0092 }
0093 
0094 QWidget* MyItemDelegate::createEditor( QWidget* parent,
0095                                        const QStyleOptionViewItem& option,
0096                                        const QModelIndex& idx ) const
0097 {
0098     qDebug() << "MyItemDelegate::createEditor("<<parent<<idx<<")";
0099     if ( idx.isValid() && idx.column() == 1 )
0100       return new ItemTypeComboBox(parent);
0101     return ItemDelegate::createEditor( parent, option, idx );
0102 }
0103 
0104 void MyItemDelegate::setEditorData ( QWidget* editor, const QModelIndex& index ) const
0105 {
0106   ItemTypeComboBox* c;
0107   if ( (c = qobject_cast<ItemTypeComboBox*>(editor)) && index.isValid() ) {
0108       c->setItemType(static_cast<KGantt::ItemType>(index.data(Qt::EditRole).toInt()));
0109   } else {
0110       ItemDelegate::setEditorData(editor,index);
0111   }
0112 }
0113 
0114 void MyItemDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model,
0115                                   const QModelIndex & index ) const
0116 {
0117   ItemTypeComboBox* c;
0118   if ( (c = qobject_cast<ItemTypeComboBox*>(editor)) && index.isValid() ) {
0119       model->setData(index,c->itemType());
0120   } else {
0121       ItemDelegate::setModelData(editor,model,index);
0122   }
0123 }
0124 
0125 void MyItemDelegate::drawDisplay( QPainter* painter, const QStyleOptionViewItem& option,
0126                                   const QRect& rect, const QString& text ) const
0127 {
0128   //qDebug() << "MyItemDelegate::drawDisplay(" <<painter<<rect<<text<<")";
0129   KGantt::ItemType typ = static_cast<KGantt::ItemType>(text.toInt());
0130   QString str;
0131   switch (typ) {
0132       case KGantt::TypeTask: str = tr("Task"); break;
0133       case KGantt::TypeEvent: str = tr("Event"); break;
0134       case KGantt::TypeSummary: str = tr("Summary"); break;
0135       default: str = tr("None"); break;
0136   }
0137   ItemDelegate::drawDisplay(painter,option,rect,str);
0138 }
0139 
0140 ///////////////////////////////////////////////////////////////////////////////
0141 // Provide custom background and foreground
0142 ///////////////////////////////////////////////////////////////////////////////
0143 
0144 class DateTimeGrid : public KGantt::DateTimeGrid
0145 {
0146 public:
0147     DateTimeGrid(QObject* parent = nullptr) {
0148         setParent(parent);
0149         setFreeDays( QSet<Qt::DayOfWeek>() );
0150         setFreeDaysBrush( QBrush( Qt::NoBrush ) );
0151     }
0152     ~DateTimeGrid() override { }
0153 
0154     //virtual void paintUserDefinedHeader(QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget = 0);
0155     void drawBackground(QPainter* painter, const QRectF& rect) override;
0156     void drawForeground(QPainter* painter, const QRectF& rect) override;
0157 };
0158 
0159 void DateTimeGrid::drawBackground(QPainter* painter, const QRectF& rect)
0160 {
0161     QLinearGradient grad;
0162     grad.setCoordinateMode( QGradient::ObjectBoundingMode );
0163     grad.setStart( 0.5, 0.5 );
0164     grad.setFinalStop( 0.5, 0.0 );
0165     grad.setSpread( QGradient::ReflectSpread );
0166 //    grad.setCenter( 0.5, 0.5 );
0167 //    grad.setFocalPoint( 0.5, 0.5 );
0168 //    grad.setRadius( 0.5 );
0169     QColor currentColor = Qt::blue;
0170     for ( qreal i = 0; i <= 1.0; i += 0.1 )
0171     {
0172         currentColor = currentColor.lighter( 100 + 20 * i );
0173         grad.setColorAt( i, currentColor );
0174     }
0175     QBrush brush( grad);
0176     //brush.setColor(Qt::lightGray);
0177 
0178     QRectF r = computeRect(QDateTime::currentDateTime(),
0179                            QDateTime::currentDateTime().addDays(2),
0180                            rect);
0181     painter->fillRect(r, brush);
0182     KGantt::DateTimeGrid::drawBackground(painter, rect);
0183 }
0184 
0185 void DateTimeGrid::drawForeground(QPainter* painter, const QRectF& rect)
0186 {
0187     painter->save();
0188 
0189     QRectF r = computeRect(QDateTime::currentDateTime(),
0190                            QDateTime::currentDateTime().addDays(2),
0191                            rect);
0192 
0193     static QString text("Holiday");
0194     QFont font = painter->font();
0195     font.setPixelSize(r.width()/5);
0196 
0197     QFontMetrics fm(font);
0198     int width = fm.boundingRect(text).width();
0199     int height = fm.boundingRect(text).height();
0200 
0201     painter->translate(r.center());
0202     painter->translate(-width/2, height/2);
0203     painter->setFont(font);
0204     painter->drawText(0, 0, text);
0205 
0206     painter->restore();
0207     KGantt::DateTimeGrid::drawForeground(painter, rect);
0208 }
0209 /*
0210 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget)
0211 {
0212     const QStyle* const style = widget ? widget->style() : QApplication::style();
0213 
0214     QDateTime dt = formatter->currentRangeBegin( mapToDateTime( offset + exposedRect.left() ) ).toUTC();
0215     qreal x = mapFromDateTime( dt );
0216 
0217     while ( x < exposedRect.right() + offset ) {
0218         const QDateTime next = formatter->nextRangeBegin( dt );
0219         const qreal nextx = mapFromDateTime( next );
0220 
0221         QStyleOptionHeader opt;
0222         if ( widget ) opt.init( widget );
0223         opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
0224         //opt.state = QStyle::State_Raised | QStyle::State_Enabled;
0225         opt.textAlignment = formatter->alignment();
0226         opt.text = formatter->text( dt );
0227 
0228         // use white text on black background
0229         opt.palette.setColor(QPalette::Window, QColor("black"));
0230         opt.palette.setColor(QPalette::ButtonText, QColor("white"));
0231 
0232         style->drawControl( QStyle::CE_Header, &opt, painter, widget );
0233 
0234         dt = next;
0235         x = nextx;
0236     }
0237 }
0238 */
0239 MainWindow::MainWindow( QWidget* parent )
0240     : QMainWindow( parent ),
0241       m_model( new ProjectModel( this ) ),
0242       m_view( new KGantt::View )
0243 {
0244     m_view->setModel( m_model );
0245     m_view->setSelectionModel( new QItemSelectionModel(m_model));
0246 
0247     // slotToolsNewItem();
0248     m_view->leftView()->setItemDelegateForColumn( 1, new MyItemDelegate( this ) );
0249     m_view->leftView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
0250     m_view->graphicsView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
0251 
0252     DateTimeGrid *grid = new DateTimeGrid(this);
0253     grid->timeLine()->setPen(QPen(Qt::red));
0254     grid->timeLine()->setOptions(KGantt::DateTimeTimeLine::UseCustomPen);
0255     grid->timeLine()->setInterval(5000);
0256     m_view->setGrid(grid);
0257 
0258     //QItemEditorCreatorBase *creator = new QItemEditorCreator<ItemTypeComboBox>("itemType");
0259     //QItemEditorFactory* factory = new QItemEditorFactory;
0260     //factory->registerEditor( QVariant( KGantt::TypeTask ).type(), creator );
0261     //m_view->itemDelegate()->setItemEditorFactory( factory );
0262 
0263     setCentralWidget( m_view );
0264 
0265     QMenuBar* mb = menuBar();
0266 
0267     QMenu* fileMenu = new QMenu( tr( "&File" ) );
0268 
0269 #ifndef QT_NO_PRINTER
0270     fileMenu->addAction( tr( "&Save as PDF..." ), this, SLOT(slotFileSavePdf()) );
0271     fileMenu->addAction( tr( "&Print..." ), this, SLOT(slotFilePrint()) );
0272 #endif
0273 
0274     fileMenu->addSeparator();
0275     fileMenu->addAction( tr( "&Quit" ), this, SLOT(slotFileQuit()) );
0276 
0277     mb->addMenu( fileMenu );
0278 
0279     QMenu* toolsMenu = new QMenu( tr( "&Tools" ) );
0280 
0281     toolsMenu->addAction( tr( "&New Item" ), this, SLOT(slotToolsNewItem()) );
0282     toolsMenu->addAction( tr( "&Add Item" ), this, SLOT(slotToolsAppendItem()) );
0283     toolsMenu->addAction( tr( "&Remove Item" ), this, SLOT(slotToolsRemoveItem()) );
0284     toolsMenu->addSeparator();
0285     QMenu *alignMenu = toolsMenu->addMenu( tr( "Ali&gn" ) );
0286     alignMenu->addAction( tr( "&Left" ), this, SLOT(slotAlignLeft()) );
0287     alignMenu->addAction( tr( "&Center" ), this, SLOT(slotAlignCenter()) );
0288     alignMenu->addAction( tr( "&Right" ), this, SLOT(slotAlignRight()) );
0289     alignMenu->addAction( tr( "&Hidden" ), this, SLOT(slotAlignHidden()) );
0290     toolsMenu->addSeparator();
0291     toolsMenu->addAction( tr( "&Collapse All" ), this, SLOT(slotCollapseAll()) );
0292     toolsMenu->addAction( tr( "&Expand All" ), this, SLOT(slotExpandAll()) );
0293 
0294     mb->addMenu( toolsMenu );
0295 
0296     /*
0297     slotToolsNewItem();
0298     slotToolsNewItem();
0299     slotToolsNewItem();
0300     for (int i = 0; i < 3; ++i) {
0301         m_model->setData(m_model->index(i,2,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime().addDays(i)), KGantt::StartTimeRole);
0302         m_model->setData(m_model->index(i,3,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime().addDays(i+1)), KGantt::EndTimeRole);
0303     }
0304     m_view->setConstraintModel(new KGantt::ConstraintModel(m_view));
0305     m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(0,0,QModelIndex()),m_model->index(1,0,QModelIndex())));
0306     m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(1,0,QModelIndex()),m_model->index(2,0,QModelIndex())));
0307     */
0308 }
0309 
0310 SavePdfDialog::SavePdfDialog(QWidget *parent)
0311     : QDialog(parent)
0312 {
0313     setModal(true);
0314     setWindowTitle(tr("Save as PDF"));
0315     QVBoxLayout *l = new QVBoxLayout(this);
0316     setLayout(l);
0317 
0318     QHBoxLayout *fileLayout = new QHBoxLayout(this);
0319     l->addLayout(fileLayout);
0320     QLabel *fileLabel = new QLabel(tr("File:"), this);
0321     fileLayout->addWidget(fileLabel);
0322     m_fileEdit = new QLineEdit(this);
0323     fileLabel->setBuddy(m_fileEdit);
0324     m_fileEdit->setText(QFileInfo(QDir::homePath(), "gantt.pdf").absoluteFilePath());
0325     fileLayout->addWidget(m_fileEdit);
0326     QPushButton *m_fileButton = new QPushButton("...", this);
0327     connect(m_fileButton, SIGNAL(clicked()), this, SLOT(fileButtonClicked()));
0328     fileLayout->addWidget(m_fileButton);
0329 
0330     m_rowLabels = new QCheckBox(tr("Row Header"), this);
0331     m_rowLabels->setChecked(true);
0332     l->addWidget(m_rowLabels);
0333 
0334     m_columnLabels = new QCheckBox(tr("Column Header"), this);
0335     m_columnLabels->setChecked(true);
0336     l->addWidget(m_columnLabels);
0337 
0338     QDialogButtonBox *btnBox = new QDialogButtonBox(this);
0339     btnBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
0340     connect(btnBox, SIGNAL(accepted()), this, SLOT(accept()));
0341     connect(btnBox, SIGNAL(rejected()), this, SLOT(reject()));
0342     l->addWidget(btnBox);
0343 
0344     resize(QSize(400, 100).expandedTo(minimumSizeHint()));
0345 }
0346 
0347 void SavePdfDialog::fileButtonClicked()
0348 {
0349     const QString file = QFileDialog::getSaveFileName(this, tr("Choose PDF File..."), QString(), tr("PDF files (*.pdf)"));
0350     if (!file.isEmpty())
0351         m_fileEdit->setText(file);
0352 }
0353 
0354 void MainWindow::slotFileSavePdf()
0355 {
0356 #ifndef QT_NO_PRINTER
0357     SavePdfDialog dialog(this);
0358     if (dialog.exec() != QDialog::Accepted)
0359         return;
0360 
0361     const QString file = dialog.m_fileEdit->text();
0362     if (file.isEmpty())
0363         return;
0364 
0365     const bool drawRowLabels = dialog.m_rowLabels->isChecked();
0366     const bool drawColumnLabels = dialog.m_columnLabels->isChecked();
0367 
0368     QPrinter printer(QPrinter::HighResolution);
0369     printer.setPageOrientation(QPageLayout::Landscape);
0370     printer.setColorMode(QPrinter::Color);
0371     printer.setPageMargins(QMarginsF(0.2, 0.2, 0.2, 0.2), QPageLayout::Point);
0372     printer.setOutputFormat(QPrinter::PdfFormat);
0373     printer.setOutputFileName(file);
0374     m_view->print(&printer, drawRowLabels, drawColumnLabels);
0375 #endif
0376 }
0377 
0378 void MainWindow::slotFilePrint()
0379 {
0380 #ifndef QT_NO_PRINTER
0381     QPrinter printer(QPrinter::HighResolution);
0382     printer.setPageOrientation(QPageLayout::Landscape);
0383     printer.setColorMode(QPrinter::Color);
0384     QPrintDialog dialog(&printer, this);
0385     if (dialog.exec() != QDialog::Accepted)
0386         return;
0387     m_view->print(&printer);
0388 #endif
0389 }
0390 
0391 void MainWindow::slotFileQuit()
0392 {
0393     // TODO
0394     QApplication::instance()->quit();
0395 }
0396 
0397 void MainWindow::slotToolsNewItem()
0398 {
0399     QModelIndex idx = m_view->selectionModel()->currentIndex();
0400     if ( idx.isValid() ) {
0401         qDebug() << "MainWindow::slotToolsNewItem" << idx;
0402         m_model->insertRows( 0, 1, m_model->index( idx.row(),0,idx.parent() ) );
0403     } else {
0404         m_model->insertRows( 0, 1, m_view->rootIndex() );
0405     }
0406 }
0407 
0408 void MainWindow::slotToolsAppendItem()
0409 {
0410     QModelIndex idx = m_view->selectionModel()->currentIndex();
0411     if ( idx.isValid() ) {
0412         qDebug() << "MainWindow::slotToolsAppendItem" << idx;
0413         m_model->insertRows( m_model->rowCount( idx ), 1, m_model->index( idx.row(),0,idx.parent() ) );
0414     } else {
0415         m_model->insertRows( m_model->rowCount( m_view->rootIndex() ), 1, m_view->rootIndex() );
0416     }
0417 }
0418 
0419 void MainWindow::slotToolsRemoveItem()
0420 {
0421     QModelIndex idx = m_view->selectionModel()->currentIndex();
0422     if ( idx.isValid() ) {
0423         qDebug() << "MainWindow::slotToolsRemoveItem" << idx;
0424         m_model->removeRows( idx.row(), 1, idx.parent() );
0425     }
0426 }
0427 
0428 void MainWindow::slotCollapseAll()
0429 {
0430     // don't use the treeview's collapseAll/expandAll methods but use the one provided by the
0431     // view cause that one will take care to update everyt6hing as needed.
0432     //QTreeView* view = qobject_cast<QTreeView*>( m_view->leftView() );
0433     //view->collapseAll();
0434 
0435     QModelIndex idx = m_view->selectionModel()->currentIndex();
0436     if ( idx.isValid() )
0437         m_view->collapseAll();
0438 }
0439 
0440 void MainWindow::slotExpandAll()
0441 {
0442     // don't use the treeview's collapseAll/expandAll methods but use the one provided by the
0443     // view cause that one will take care to update everyt6hing as needed.
0444     //QTreeView* view = qobject_cast<QTreeView*>( m_view->leftView() );
0445     //view->expandAll();
0446 
0447     QModelIndex idx = m_view->selectionModel()->currentIndex();
0448     if ( idx.isValid() )
0449         m_view->expandAll();
0450 }
0451 
0452 void MainWindow::slotAlignLeft()
0453 {
0454     QModelIndex idx = m_view->selectionModel()->currentIndex();
0455     if ( idx.isValid() ) {
0456         m_model->setData( idx, KGantt::StyleOptionGanttItem::Left, KGantt::TextPositionRole );
0457     }
0458 }
0459 
0460 void MainWindow::slotAlignCenter()
0461 {
0462     QModelIndex idx = m_view->selectionModel()->currentIndex();
0463     if ( idx.isValid() ) {
0464         m_model->setData( idx, KGantt::StyleOptionGanttItem::Center, KGantt::TextPositionRole );
0465     }
0466 }
0467 
0468 void MainWindow::slotAlignRight()
0469 {
0470     QModelIndex idx = m_view->selectionModel()->currentIndex();
0471     if ( idx.isValid() ) {
0472         m_model->setData( idx, KGantt::StyleOptionGanttItem::Right, KGantt::TextPositionRole );
0473     }
0474 }
0475 
0476 void MainWindow::slotAlignHidden()
0477 {
0478     QModelIndex idx = m_view->selectionModel()->currentIndex();
0479     if ( idx.isValid() ) {
0480         m_model->setData( idx, KGantt::StyleOptionGanttItem::Hidden, KGantt::TextPositionRole );
0481     }
0482 }
0483 
0484 #include "mainwindow.moc"