1 /**
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
3  *
4  * This file is part of the KGantt library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "mainwindow.h"
21 #include "../common/projectmodel.h"
22 
23 #include <QAction>
24 #include <QApplication>
25 #include <QComboBox>
26 #include <QMenuBar>
27 #include <QMenu>
28 #include <QModelIndex>
29 #include <QItemSelectionModel>
30 #include <QTreeView>
31 #include <QDebug>
32 #include <QBrush>
33 #include <QPainter>
34 #include <QPrinter>
35 #include <QPrintDialog>
36 #include <QPrintPreviewDialog>
37 #include <QFileDialog>
38 #include <QLabel>
39 #include <QGridLayout>
40 #include <QVBoxLayout>
41 #include <QHBoxLayout>
42 #include <QFileInfo>
43 #include <QLineEdit>
44 #include <QCheckBox>
45 #include <QPushButton>
46 #include <QDialogButtonBox>
47 
48 #include <KGanttGlobal>
49 #include <KGanttView>
50 #include <KGanttItemDelegate>
51 #include <KGanttDateTimeGrid>
52 #include <KGanttStyleOptionGanttItem>
53 #include <KGanttConstraintModel>
54 #include <KGanttGraphicsView>
55 #include <KGanttDateTimeTimeLine>
56 
57 // Define a printer friendly palette
58 #define VeryLightGray   "#f8f8f8"
59 #define LightLightGray  "#f0f0f0"
60 #define DarkDarkGray    "#b3b3b3"
61 #define VeryDarkGray    "#838383"
62 class PrintPalette {
63 public:
PrintPalette()64     PrintPalette() {
65         orig = QApplication::palette();
66         QPalette palette = orig;
67         // define a palette that works when printing on white paper
68         palette.setColor(QPalette::Window, Qt::white);
69         palette.setColor(QPalette::WindowText, Qt::black);
70         palette.setColor(QPalette::Base, Qt::white);
71         palette.setColor(QPalette::AlternateBase, VeryLightGray);
72         palette.setColor(QPalette::ToolTipBase, Qt::white);
73         palette.setColor(QPalette::ToolTipText, Qt::black);
74         palette.setColor(QPalette::Text, Qt::black);
75         palette.setColor(QPalette::Button, Qt::lightGray);
76         palette.setColor(QPalette::ButtonText, Qt::black);
77         palette.setColor(QPalette::BrightText, Qt::white);
78         palette.setColor(QPalette::Link, Qt::blue);
79         palette.setColor(QPalette::Highlight, Qt::blue);
80         palette.setColor(QPalette::HighlightedText, Qt::white);
81         palette.setColor(QPalette::Light, QColor(VeryLightGray));
82         palette.setColor(QPalette::Midlight, QColor(LightLightGray));
83         palette.setColor(QPalette::Dark, QColor(DarkDarkGray));
84         palette.setColor(QPalette::Mid, QColor(VeryDarkGray));
85         palette.setColor(QPalette::Shadow, Qt::black);
86         QApplication::setPalette(palette);
87     }
~PrintPalette()88     ~PrintPalette() {
89         QApplication::setPalette(orig);
90     }
91     QPalette orig;
92 };
93 
94 
95 class ItemTypeComboBox : public QComboBox {
96     Q_OBJECT
97     Q_PROPERTY( KGantt::ItemType itemType READ itemType WRITE setItemType )
98 public:
99     explicit ItemTypeComboBox( QWidget* parent = nullptr );
100 
101     KGantt::ItemType itemType() const;
102 public Q_SLOTS:
103     void setItemType( KGantt::ItemType typ );
104 };
105 
ItemTypeComboBox(QWidget * parent)106 ItemTypeComboBox::ItemTypeComboBox( QWidget* parent )
107     : QComboBox( parent )
108 {
109     addItem( tr( "Task" ), QVariant( KGantt::TypeTask ) );
110     addItem( tr( "Event" ), QVariant( KGantt::TypeEvent ) );
111     addItem( tr( "Summary" ), QVariant( KGantt::TypeSummary ) );
112 }
113 
itemType() const114 KGantt::ItemType ItemTypeComboBox::itemType() const
115 {
116     return static_cast<KGantt::ItemType>( itemData( currentIndex() ).toInt() );
117 }
118 
setItemType(KGantt::ItemType typ)119 void ItemTypeComboBox::setItemType( KGantt::ItemType typ )
120 {
121     setCurrentIndex( typ-1 );
122 }
123 
124 class MyItemDelegate : public KGantt::ItemDelegate {
125 public:
126     explicit MyItemDelegate( QObject* parent = nullptr );
127 
128     /*reimp*/ QWidget* createEditor( QWidget* parent,
129                                      const QStyleOptionViewItem& option,
130                                      const QModelIndex& idx ) const override;
131     /*reimp*/ void setEditorData( QWidget* editor, const QModelIndex& index ) const override;
132     /*reimp*/ void setModelData( QWidget* editor, QAbstractItemModel* model,
133                                   const QModelIndex & index ) const override;
134 };
135 
MyItemDelegate(QObject * parent)136 MyItemDelegate::MyItemDelegate( QObject* parent )
137     : KGantt::ItemDelegate( parent )
138 {
139 }
140 
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & idx) const141 QWidget* MyItemDelegate::createEditor( QWidget* parent,
142                                        const QStyleOptionViewItem& option,
143                                        const QModelIndex& idx ) const
144 {
145     qDebug() << "MyItemDelegate::createEditor("<<parent<<idx<<")";
146     if ( idx.isValid() && idx.column() == 1 )
147       return new ItemTypeComboBox(parent);
148     return ItemDelegate::createEditor( parent, option, idx );
149 }
150 
setEditorData(QWidget * editor,const QModelIndex & index) const151 void MyItemDelegate::setEditorData ( QWidget* editor, const QModelIndex& index ) const
152 {
153   ItemTypeComboBox* c;
154   if ( (c = qobject_cast<ItemTypeComboBox*>(editor)) && index.isValid() ) {
155       c->setItemType(static_cast<KGantt::ItemType>(index.data(Qt::EditRole).toInt()));
156   } else {
157       ItemDelegate::setEditorData(editor,index);
158   }
159 }
160 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const161 void MyItemDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model,
162                                   const QModelIndex & index ) const
163 {
164   ItemTypeComboBox* c;
165   if ( (c = qobject_cast<ItemTypeComboBox*>(editor)) && index.isValid() ) {
166       model->setData(index,c->itemType());
167   } else {
168       ItemDelegate::setModelData(editor,model,index);
169   }
170 }
171 
MainWindow(QWidget * parent)172 MainWindow::MainWindow( QWidget* parent )
173     : QMainWindow( parent ),
174       m_model( new ProjectModel( this ) ),
175       m_view( new KGantt::View )
176 {
177     m_view->setModel( m_model );
178 
179     m_view->leftView()->setItemDelegateForColumn( 1, new MyItemDelegate( this ) );
180 
181     KGantt::DateTimeGrid *grid = new KGantt::DateTimeGrid();
182     grid->timeLine()->setPen(QPen(Qt::red));
183     grid->timeLine()->setOptions(KGantt::DateTimeTimeLine::UseCustomPen);
184     grid->timeLine()->setInterval(5000);
185     grid->setRowSeparators(true);
186     m_view->setGrid(grid);
187 
188 
189     setCentralWidget( m_view );
190 
191     QMenuBar* mb = menuBar();
192 
193     QMenu* fileMenu = new QMenu( tr( "&File" ) );
194 
195 #ifndef QT_NO_PRINTER
196     fileMenu->addAction( tr( "&Save as PDF..." ), this, SLOT(slotFileSavePdf()) );
197     fileMenu->addAction( tr( "&Print Preview..." ), this, SLOT(slotFilePrintPreview()) );
198     fileMenu->addAction( tr( "&Print..." ), this, SLOT(slotFilePrint()) );
199 #endif
200 
201     fileMenu->addSeparator();
202     fileMenu->addAction( tr( "&Quit" ), this, SLOT(slotFileQuit()) );
203 
204     mb->addMenu( fileMenu );
205 
206     QMenu* toolsMenu = new QMenu( tr( "&Tools" ) );
207 
208     toolsMenu->addAction( tr( "&New Item" ), this, SLOT(slotToolsNewItem()) );
209     toolsMenu->addAction( tr( "&Add Item" ), this, SLOT(slotToolsAppendItem()) );
210     toolsMenu->addSeparator();
211     QMenu *alignMenu = toolsMenu->addMenu( tr( "Ali&gn" ) );
212     alignMenu->addAction( tr( "&Left" ), this, SLOT(slotAlignLeft()) );
213     alignMenu->addAction( tr( "&Center" ), this, SLOT(slotAlignCenter()) );
214     alignMenu->addAction( tr( "&Right" ), this, SLOT(slotAlignRight()) );
215     alignMenu->addAction( tr( "&Hidden" ), this, SLOT(slotAlignHidden()) );
216     toolsMenu->addSeparator();
217     toolsMenu->addAction( tr( "&Collapse All" ), this, SLOT(slotCollapseAll()) );
218     toolsMenu->addAction( tr( "&Expand All" ), this, SLOT(slotExpandAll()) );
219 
220     mb->addMenu( toolsMenu );
221 
222     // define some items with different properties
223     slotToolsAppendItem();
224     slotToolsAppendItem();
225     slotToolsAppendItem();
226     for (int i = 0; i < 3; ++i) {
227         m_model->setData(m_model->index(i,2,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime().addDays(i)), KGantt::StartTimeRole);
228         m_model->setData(m_model->index(i,3,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime().addDays(i+1)), KGantt::EndTimeRole);
229     }
230     slotToolsAppendItem();
231     m_model->setData(m_model->index(3,2,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime()), KGantt::StartTimeRole);
232     m_model->setData(m_model->index(3,3,QModelIndex()), QVariant::fromValue(QDateTime::currentDateTime().addDays(1)), KGantt::EndTimeRole);
233     m_model->setData(m_model->index(3,4,QModelIndex()), QVariant::fromValue(50), KGantt::TaskCompletionRole);
234 
235     m_view->setConstraintModel(new KGantt::ConstraintModel(m_view));
236     m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(0,0,QModelIndex()),m_model->index(1,0,QModelIndex())));
237     m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(1,0,QModelIndex()),m_model->index(2,0,QModelIndex())));
238     // invalid contraint
239     m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(2,0,QModelIndex()),m_model->index(3,0,QModelIndex())));
240 
241     // no info
242     slotToolsAppendItem();
243     m_model->setData(m_model->index(4,2,QModelIndex()), QVariant::fromValue(QDateTime()), KGantt::StartTimeRole);
244     m_model->setData(m_model->index(4,3,QModelIndex()), QVariant::fromValue(QDateTime()), KGantt::EndTimeRole);
245 }
246 
SavePdfDialog(QWidget * parent)247 SavePdfDialog::SavePdfDialog(QWidget *parent)
248     : QDialog(parent)
249 {
250     setModal(true);
251     setWindowTitle(tr("Save as PDF"));
252     QVBoxLayout *l = new QVBoxLayout(this);
253     setLayout(l);
254 
255     QHBoxLayout *fileLayout = new QHBoxLayout(this);
256     l->addLayout(fileLayout);
257     QLabel *fileLabel = new QLabel(tr("File:"), this);
258     fileLayout->addWidget(fileLabel);
259     m_fileEdit = new QLineEdit(this);
260     fileLabel->setBuddy(m_fileEdit);
261     m_fileEdit->setText(QFileInfo(QDir::homePath(), "gantt.pdf").absoluteFilePath());
262     fileLayout->addWidget(m_fileEdit);
263     QPushButton *m_fileButton = new QPushButton("...", this);
264     connect(m_fileButton, SIGNAL(clicked()), this, SLOT(fileButtonClicked()));
265     fileLayout->addWidget(m_fileButton);
266 
267     m_rowLabels = new QCheckBox(tr("Row Header"), this);
268     m_rowLabels->setChecked(true);
269     l->addWidget(m_rowLabels);
270 
271     m_columnLabels = new QCheckBox(tr("Column Header"), this);
272     m_columnLabels->setChecked(true);
273     l->addWidget(m_columnLabels);
274 
275     QDialogButtonBox *btnBox = new QDialogButtonBox(this);
276     btnBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
277     connect(btnBox, SIGNAL(accepted()), this, SLOT(accept()));
278     connect(btnBox, SIGNAL(rejected()), this, SLOT(reject()));
279     l->addWidget(btnBox);
280 
281     resize(QSize(400, 100).expandedTo(minimumSizeHint()));
282 }
283 
fileButtonClicked()284 void SavePdfDialog::fileButtonClicked()
285 {
286     const QString file = QFileDialog::getSaveFileName(this, tr("Choose PDF File..."), QString(), tr("PDF files (*.pdf)"));
287     if (!file.isEmpty())
288         m_fileEdit->setText(file);
289 }
290 
slotFileSavePdf()291 void MainWindow::slotFileSavePdf()
292 {
293 #ifndef QT_NO_PRINTER
294     SavePdfDialog dialog(this);
295     if (dialog.exec() != QDialog::Accepted)
296         return;
297 
298     const QString file = dialog.m_fileEdit->text();
299     if (file.isEmpty())
300         return;
301 
302     const bool drawRowLabels = dialog.m_rowLabels->isChecked();
303     const bool drawColumnLabels = dialog.m_columnLabels->isChecked();
304 
305     QPrinter printer(QPrinter::HighResolution);
306     printer.setOrientation(QPrinter::Landscape);
307     printer.setColorMode(QPrinter::Color);
308     printer.setPageMargins(0.2, 0.2, 0.2, 0.2, QPrinter::Point);
309     printer.setOutputFormat(QPrinter::PdfFormat);
310     printer.setOutputFileName(file);
311     PrintPalette p;
312     m_view->print(&printer, drawRowLabels, drawColumnLabels);
313 #endif
314 }
315 
slotFilePrint()316 void MainWindow::slotFilePrint()
317 {
318 #ifndef QT_NO_PRINTER
319     QPrinter printer(QPrinter::HighResolution);
320     printer.setOrientation(QPrinter::Landscape);
321     printer.setColorMode(QPrinter::Color);
322     QPrintDialog dialog(&printer, this);
323     if (dialog.exec() != QDialog::Accepted) {
324         return;
325     }
326     PrintPalette p;
327     m_view->print(&printer);
328 #endif
329 }
330 
slotFilePrintPreview()331 void MainWindow::slotFilePrintPreview()
332 {
333     QPrinter printer(QPrinter::HighResolution);
334     printer.setOrientation(QPrinter::Landscape);
335     printer.setColorMode(QPrinter::Color);
336     QPrintPreviewDialog preview(&printer);
337     connect(&preview, SIGNAL(paintRequested(QPrinter*)), this, SLOT(slotPrintPreviewPaintRequest(QPrinter*)));
338     preview.exec();
339 }
340 
slotPrintPreviewPaintRequest(QPrinter * printer)341 void MainWindow::slotPrintPreviewPaintRequest(QPrinter *printer)
342 {
343     PrintPalette p;
344     m_view->print(printer);
345 }
346 
slotFileQuit()347 void MainWindow::slotFileQuit()
348 {
349     // TODO
350     QApplication::instance()->quit();
351 }
352 
slotToolsNewItem()353 void MainWindow::slotToolsNewItem()
354 {
355     QModelIndex idx = m_view->selectionModel()->currentIndex();
356     if ( idx.isValid() ) {
357         qDebug() << "MainWindow::slotToolsNewItem" << idx;
358         m_model->insertRows( 0, 1, m_model->index( idx.row(),0,idx.parent() ) );
359     } else {
360         m_model->insertRows( 0, 1, m_view->rootIndex() );
361     }
362 }
363 
slotToolsAppendItem()364 void MainWindow::slotToolsAppendItem()
365 {
366     QModelIndex idx = m_view->selectionModel()->currentIndex();
367     if ( idx.isValid() ) {
368         qDebug() << "MainWindow::slotToolsAppendItem" << idx;
369         m_model->insertRows( m_model->rowCount( idx ), 1, m_model->index( idx.row(),0,idx.parent() ) );
370     } else {
371         m_model->insertRows( m_model->rowCount( m_view->rootIndex() ), 1, m_view->rootIndex() );
372     }
373 }
374 
slotCollapseAll()375 void MainWindow::slotCollapseAll()
376 {
377     // don't use the treeview's collapseAll/expandAll methods but use the one provided by the
378     // view cause that one will take care to update everyt6hing as needed.
379     //QTreeView* view = qobject_cast<QTreeView*>( m_view->leftView() );
380     //view->collapseAll();
381 
382     QModelIndex idx = m_view->selectionModel()->currentIndex();
383     if ( idx.isValid() )
384         m_view->collapseAll();
385 }
386 
slotExpandAll()387 void MainWindow::slotExpandAll()
388 {
389     // don't use the treeview's collapseAll/expandAll methods but use the one provided by the
390     // view cause that one will take care to update everyt6hing as needed.
391     //QTreeView* view = qobject_cast<QTreeView*>( m_view->leftView() );
392     //view->expandAll();
393 
394     QModelIndex idx = m_view->selectionModel()->currentIndex();
395     if ( idx.isValid() )
396         m_view->expandAll();
397 }
398 
slotAlignLeft()399 void MainWindow::slotAlignLeft()
400 {
401     QModelIndex idx = m_view->selectionModel()->currentIndex();
402     if ( idx.isValid() ) {
403         m_model->setData( idx, KGantt::StyleOptionGanttItem::Left, KGantt::TextPositionRole );
404     }
405 }
406 
slotAlignCenter()407 void MainWindow::slotAlignCenter()
408 {
409     QModelIndex idx = m_view->selectionModel()->currentIndex();
410     if ( idx.isValid() ) {
411         m_model->setData( idx, KGantt::StyleOptionGanttItem::Center, KGantt::TextPositionRole );
412     }
413 }
414 
slotAlignRight()415 void MainWindow::slotAlignRight()
416 {
417     QModelIndex idx = m_view->selectionModel()->currentIndex();
418     if ( idx.isValid() ) {
419         m_model->setData( idx, KGantt::StyleOptionGanttItem::Right, KGantt::TextPositionRole );
420     }
421 }
422 
slotAlignHidden()423 void MainWindow::slotAlignHidden()
424 {
425     QModelIndex idx = m_view->selectionModel()->currentIndex();
426     if ( idx.isValid() ) {
427         m_model->setData( idx, KGantt::StyleOptionGanttItem::Hidden, KGantt::TextPositionRole );
428     }
429 }
430 
431 #include "mainwindow.moc"
432