diff --git a/examples/Gantt/project/mainwindow.cpp b/examples/Gantt/project/mainwindow.cpp index 961a4a5..3038082 100644 --- a/examples/Gantt/project/mainwindow.cpp +++ b/examples/Gantt/project/mainwindow.cpp @@ -1,497 +1,478 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "projectmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include class ItemTypeComboBox : public QComboBox { Q_OBJECT Q_PROPERTY( KGantt::ItemType itemType READ itemType WRITE setItemType ) public: explicit ItemTypeComboBox( QWidget* parent = nullptr ); KGantt::ItemType itemType() const; public slots: void setItemType( KGantt::ItemType typ ); }; ItemTypeComboBox::ItemTypeComboBox( QWidget* parent ) : QComboBox( parent ) { addItem( tr( "Task" ), QVariant( KGantt::TypeTask ) ); addItem( tr( "Event" ), QVariant( KGantt::TypeEvent ) ); addItem( tr( "Summary" ), QVariant( KGantt::TypeSummary ) ); } KGantt::ItemType ItemTypeComboBox::itemType() const { return static_cast( itemData( currentIndex() ).toInt() ); } void ItemTypeComboBox::setItemType( KGantt::ItemType typ ) { setCurrentIndex( typ-1 ); } class MyItemDelegate : public KGantt::ItemDelegate { public: explicit MyItemDelegate( QObject* parent = nullptr ); /*reimp*/ QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ void setEditorData( QWidget* editor, const QModelIndex& index ) const Q_DECL_OVERRIDE; /*reimp*/ void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex & index ) const Q_DECL_OVERRIDE; protected: /*reimp*/void drawDisplay( QPainter* painter, const QStyleOptionViewItem & option, const QRect& rect, const QString& text ) const Q_DECL_OVERRIDE; }; MyItemDelegate::MyItemDelegate( QObject* parent ) : KGantt::ItemDelegate( parent ) { } QWidget* MyItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& idx ) const { qDebug() << "MyItemDelegate::createEditor("<(editor)) && index.isValid() ) { c->setItemType(static_cast(index.data(Qt::EditRole).toInt())); } else { ItemDelegate::setEditorData(editor,index); } } void MyItemDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model, const QModelIndex & index ) const { ItemTypeComboBox* c; if ( (c = qobject_cast(editor)) && index.isValid() ) { model->setData(index,c->itemType()); } else { ItemDelegate::setModelData(editor,model,index); } } void MyItemDelegate::drawDisplay( QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text ) const { //qDebug() << "MyItemDelegate::drawDisplay(" <(text.toInt()); QString str; switch (typ) { case KGantt::TypeTask: str = tr("Task"); break; case KGantt::TypeEvent: str = tr("Event"); break; case KGantt::TypeSummary: str = tr("Summary"); break; default: str = tr("None"); break; } ItemDelegate::drawDisplay(painter,option,rect,str); } /////////////////////////////////////////////////////////////////////////////// // Provide custom background and foreground /////////////////////////////////////////////////////////////////////////////// class DateTimeGrid : public KGantt::DateTimeGrid { public: DateTimeGrid(QObject* parent = nullptr) { setParent(parent); setFreeDays( QSet() ); setFreeDaysBrush( QBrush( Qt::NoBrush ) ); } ~DateTimeGrid() { } //virtual void paintUserDefinedHeader(QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget = 0); void drawBackground(QPainter* painter, const QRectF& rect) Q_DECL_OVERRIDE; void drawForeground(QPainter* painter, const QRectF& rect) Q_DECL_OVERRIDE; }; void DateTimeGrid::drawBackground(QPainter* painter, const QRectF& rect) { QLinearGradient grad; grad.setCoordinateMode( QGradient::ObjectBoundingMode ); grad.setStart( 0.5, 0.5 ); grad.setFinalStop( 0.5, 0.0 ); grad.setSpread( QGradient::ReflectSpread ); // grad.setCenter( 0.5, 0.5 ); // grad.setFocalPoint( 0.5, 0.5 ); // grad.setRadius( 0.5 ); QColor currentColor = Qt::blue; for ( qreal i = 0; i <= 1.0; i += 0.1 ) { currentColor = currentColor.lighter( 100 + 20 * i ); grad.setColorAt( i, currentColor ); } QBrush brush( grad); //brush.setColor(Qt::lightGray); QRectF r = computeRect(QDateTime::currentDateTime(), QDateTime::currentDateTime().addDays(2), rect); painter->fillRect(r, brush); - KGantt::DateTimeGrid::drawBackground(painter, rect); } void DateTimeGrid::drawForeground(QPainter* painter, const QRectF& rect) { painter->save(); QRectF r = computeRect(QDateTime::currentDateTime(), QDateTime::currentDateTime().addDays(2), rect); static QString text("Holiday"); QFont font = painter->font(); font.setPixelSize(r.width()/5); QFontMetrics fm(font); int width = fm.width(text); int height = fm.boundingRect(text).height(); painter->translate(r.center()); painter->translate(-width/2, height/2); painter->setFont(font); painter->drawText(0, 0, text); painter->restore(); - - KGantt::DateTimeGrid::drawForeground(painter, rect); } /* void DateTimeGrid::paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget) { const QStyle* const style = widget ? widget->style() : QApplication::style(); QDateTime dt = formatter->currentRangeBegin( mapToDateTime( offset + exposedRect.left() ) ).toUTC(); qreal x = mapFromDateTime( dt ); while ( x < exposedRect.right() + offset ) { const QDateTime next = formatter->nextRangeBegin( dt ); const qreal nextx = mapFromDateTime( next ); QStyleOptionHeader opt; if ( widget ) opt.init( widget ); opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect(); //opt.state = QStyle::State_Raised | QStyle::State_Enabled; opt.textAlignment = formatter->alignment(); opt.text = formatter->text( dt ); // use white text on black background opt.palette.setColor(QPalette::Window, QColor("black")); opt.palette.setColor(QPalette::ButtonText, QColor("white")); style->drawControl( QStyle::CE_Header, &opt, painter, widget ); dt = next; x = nextx; } } */ MainWindow::MainWindow( QWidget* parent ) : QMainWindow( parent ), m_model( new ProjectModel( this ) ), m_view( new KGantt::View ) { m_view->setModel( m_model ); m_view->setSelectionModel( new QItemSelectionModel(m_model)); // slotToolsNewItem(); m_view->leftView()->setItemDelegateForColumn( 1, new MyItemDelegate( this ) ); m_view->leftView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_view->graphicsView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_view->setGrid(new DateTimeGrid(this)); - // Display timeline - KGantt::DateTimeGrid *grid = static_cast(m_view->grid()); - grid->setTimelinePen(QPen(Qt::red, 2)); -// grid->setTimelineTime(QDateTime::currentDateTime().addDays(-1)); - grid->setTimelineOptions(KGantt::DateTimeGrid::Background | KGantt::DateTimeGrid::UseCustomPen); - // Update timeline every 5 seconds - QTimer *timelineTimer = new QTimer(this); - timelineTimer->setInterval(5000); - timelineTimer->start(); - connect(timelineTimer, SIGNAL(timeout()), grid, SLOT(setTimelineTime())); - + //QItemEditorCreatorBase *creator = new QItemEditorCreator("itemType"); //QItemEditorFactory* factory = new QItemEditorFactory; //factory->registerEditor( QVariant( KGantt::TypeTask ).type(), creator ); //m_view->itemDelegate()->setItemEditorFactory( factory ); setCentralWidget( m_view ); QMenuBar* mb = menuBar(); QMenu* fileMenu = new QMenu( tr( "&File" ) ); #ifndef QT_NO_PRINTER fileMenu->addAction( tr( "&Save as PDF..." ), this, SLOT(slotFileSavePdf()) ); fileMenu->addAction( tr( "&Print..." ), this, SLOT(slotFilePrint()) ); #endif fileMenu->addSeparator(); fileMenu->addAction( tr( "&Quit" ), this, SLOT(slotFileQuit()) ); mb->addMenu( fileMenu ); QMenu* toolsMenu = new QMenu( tr( "&Tools" ) ); toolsMenu->addAction( tr( "&New Item" ), this, SLOT(slotToolsNewItem()) ); toolsMenu->addAction( tr( "&Add Item" ), this, SLOT(slotToolsAppendItem()) ); toolsMenu->addSeparator(); QMenu *alignMenu = toolsMenu->addMenu( tr( "Ali&gn" ) ); alignMenu->addAction( tr( "&Left" ), this, SLOT(slotAlignLeft()) ); alignMenu->addAction( tr( "&Center" ), this, SLOT(slotAlignCenter()) ); alignMenu->addAction( tr( "&Right" ), this, SLOT(slotAlignRight()) ); alignMenu->addAction( tr( "&Hidden" ), this, SLOT(slotAlignHidden()) ); toolsMenu->addSeparator(); toolsMenu->addAction( tr( "&Collapse All" ), this, SLOT(slotCollapseAll()) ); toolsMenu->addAction( tr( "&Expand All" ), this, SLOT(slotExpandAll()) ); mb->addMenu( toolsMenu ); /* slotToolsNewItem(); slotToolsNewItem(); slotToolsNewItem(); for (int i = 0; i < 3; ++i) { m_model->setData(m_model->index(i,2,QModelIndex()), qVariantFromValue(QDateTime::currentDateTime().addDays(i)), KGantt::StartTimeRole); m_model->setData(m_model->index(i,3,QModelIndex()), qVariantFromValue(QDateTime::currentDateTime().addDays(i+1)), KGantt::EndTimeRole); } m_view->setConstraintModel(new KGantt::ConstraintModel(m_view)); m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(0,0,QModelIndex()),m_model->index(1,0,QModelIndex()))); m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(1,0,QModelIndex()),m_model->index(2,0,QModelIndex()))); */ } SavePdfDialog::SavePdfDialog(QWidget *parent) : QDialog(parent) { setModal(true); setWindowTitle(tr("Save as PDF")); QVBoxLayout *l = new QVBoxLayout(this); setLayout(l); QHBoxLayout *fileLayout = new QHBoxLayout(this); l->addLayout(fileLayout); QLabel *fileLabel = new QLabel(tr("File:"), this); fileLayout->addWidget(fileLabel); m_fileEdit = new QLineEdit(this); fileLabel->setBuddy(m_fileEdit); m_fileEdit->setText(QFileInfo(QDir::homePath(), "gantt.pdf").absoluteFilePath()); fileLayout->addWidget(m_fileEdit); QPushButton *m_fileButton = new QPushButton("...", this); connect(m_fileButton, SIGNAL(clicked()), this, SLOT(fileButtonClicked())); fileLayout->addWidget(m_fileButton); m_rowLabels = new QCheckBox(tr("Row Header"), this); m_rowLabels->setChecked(true); l->addWidget(m_rowLabels); m_columnLabels = new QCheckBox(tr("Column Header"), this); m_columnLabels->setChecked(true); l->addWidget(m_columnLabels); QDialogButtonBox *btnBox = new QDialogButtonBox(this); btnBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); connect(btnBox, SIGNAL(accepted()), this, SLOT(accept())); connect(btnBox, SIGNAL(rejected()), this, SLOT(reject())); l->addWidget(btnBox); resize(QSize(400, 100).expandedTo(minimumSizeHint())); } void SavePdfDialog::fileButtonClicked() { const QString file = QFileDialog::getSaveFileName(this, tr("Choose PDF File..."), QString(), tr("PDF files (*.pdf)")); if (!file.isEmpty()) m_fileEdit->setText(file); } void MainWindow::slotFileSavePdf() { #ifndef QT_NO_PRINTER SavePdfDialog dialog(this); if (dialog.exec() != QDialog::Accepted) return; const QString file = dialog.m_fileEdit->text(); if (file.isEmpty()) return; const bool drawRowLabels = dialog.m_rowLabels->isChecked(); const bool drawColumnLabels = dialog.m_columnLabels->isChecked(); QPrinter printer(QPrinter::HighResolution); printer.setOrientation(QPrinter::Landscape); printer.setColorMode(QPrinter::Color); printer.setPageMargins(0.2, 0.2, 0.2, 0.2, QPrinter::Point); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(file); m_view->print(&printer, drawRowLabels, drawColumnLabels); #endif } void MainWindow::slotFilePrint() { #ifndef QT_NO_PRINTER QPrinter printer(QPrinter::HighResolution); printer.setOrientation(QPrinter::Landscape); printer.setColorMode(QPrinter::Color); QPrintDialog dialog(&printer, this); if (dialog.exec() != QDialog::Accepted) return; m_view->print(&printer); #endif } void MainWindow::slotFileQuit() { // TODO QApplication::instance()->quit(); } void MainWindow::slotToolsNewItem() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { qDebug() << "MainWindow::slotToolsNewItem" << idx; m_model->insertRows( 0, 1, m_model->index( idx.row(),0,idx.parent() ) ); } else { m_model->insertRows( 0, 1, m_view->rootIndex() ); } } void MainWindow::slotToolsAppendItem() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { qDebug() << "MainWindow::slotToolsAppendItem" << idx; m_model->insertRows( m_model->rowCount( idx ), 1, m_model->index( idx.row(),0,idx.parent() ) ); } else { m_model->insertRows( m_model->rowCount( m_view->rootIndex() ), 1, m_view->rootIndex() ); } } void MainWindow::slotCollapseAll() { // don't use the treeview's collapseAll/expandAll methods but use the one provided by the // view cause that one will take care to update everyt6hing as needed. //QTreeView* view = qobject_cast( m_view->leftView() ); //view->collapseAll(); QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) m_view->collapseAll(); } void MainWindow::slotExpandAll() { // don't use the treeview's collapseAll/expandAll methods but use the one provided by the // view cause that one will take care to update everyt6hing as needed. //QTreeView* view = qobject_cast( m_view->leftView() ); //view->expandAll(); QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) m_view->expandAll(); } void MainWindow::slotAlignLeft() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Left, KGantt::TextPositionRole ); } } void MainWindow::slotAlignCenter() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Center, KGantt::TextPositionRole ); } } void MainWindow::slotAlignRight() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Right, KGantt::TextPositionRole ); } } void MainWindow::slotAlignHidden() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Hidden, KGantt::TextPositionRole ); } } -void MainWindow::updateTimeline() -{ - qobject_cast(m_view->grid())->setTimelineTime(QDateTime::currentDateTime()); -} - #include "mainwindow.moc" diff --git a/examples/Gantt/project/mainwindow.h b/examples/Gantt/project/mainwindow.h index 1ed5fb5..e74a909 100644 --- a/examples/Gantt/project/mainwindow.h +++ b/examples/Gantt/project/mainwindow.h @@ -1,72 +1,71 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include class QLineEdit; class QCheckBox; namespace KGantt { class View; } class ProjectModel; class SavePdfDialog : public QDialog { Q_OBJECT public: QLineEdit *m_fileEdit; QCheckBox *m_rowLabels; QCheckBox *m_columnLabels; SavePdfDialog(QWidget *parent = nullptr); private slots: void fileButtonClicked(); }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow( QWidget* parent = nullptr ); private slots: void slotFileSavePdf(); void slotFilePrint(); void slotFileQuit(); void slotToolsNewItem(); void slotToolsAppendItem(); void slotCollapseAll(); void slotExpandAll(); void slotAlignLeft(); void slotAlignCenter(); void slotAlignRight(); void slotAlignHidden(); - - void updateTimeline(); + private: ProjectModel* m_model; KGantt::View* m_view; }; #endif /* MAINWINDOW_H */ diff --git a/src/KGantt/kganttabstractgrid.cpp b/src/KGantt/kganttabstractgrid.cpp index baba3a8..c00e91d 100644 --- a/src/KGantt/kganttabstractgrid.cpp +++ b/src/KGantt/kganttabstractgrid.cpp @@ -1,170 +1,168 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttabstractgrid.h" #include "kganttabstractgrid_p.h" #include using namespace KGantt; /*!\class KGantt::AbstractGrid kganttabstractgrid.h KGanttAbstractGrid * \ingroup KGantt * \brief Abstract baseclass for grids. A grid is used to convert between * QModelIndex'es and gantt chart values (qreals) and to paint the * background and header of the view. * * \see KGantt::DateTimeGrid */ /*! Constructor. Creates an AbstractGrid with parent \a parent. * The QObject parent is not used for anything internally. */ AbstractGrid::AbstractGrid( QObject* parent ) : QObject( parent ), _d( new Private ) { } /*! Destructor. Does nothing */ AbstractGrid::~AbstractGrid() { delete _d; } #define d d_func() /*! Sets the QAbstractItemModel used by this grid implementation. * This is called by the view, you should never need to call this * from client code. */ void AbstractGrid::setModel( QAbstractItemModel* model ) { d->model = model; } /*!\returns The QAbstractItemModel used by this grid */ QAbstractItemModel* AbstractGrid::model() const { return d->model; } /*! Sets the root index used by this grid implementation. * This is called by the view, you should never need to call this * from client code. */ void AbstractGrid::setRootIndex( const QModelIndex& idx ) { d->root = idx; } /*!\returns the current root index for this grid */ QModelIndex AbstractGrid::rootIndex() const { return d->root; } /*!\returns true if the startpoint is before the endpoint * of the constraint \a c. */ bool AbstractGrid::isSatisfiedConstraint( const Constraint& c ) const { // First check if the data is valid, // TODO: review if true is the right choice if ( !c.startIndex().isValid() || !c.endIndex().isValid() ) return true; Span ss = mapToChart( c.startIndex() ); Span es = mapToChart( c.endIndex() ); return ( ss.end() <= es.start() ); } /*! * Implement this to map from \a value to the corresponding location in the view. * Return a negative value if \a value cannot be mapped. * The default implementation returns -1.0. */ qreal AbstractGrid::mapToChart( const QVariant& value ) const { Q_UNUSED( value ); return -1.0; } /*! * Implement this to map from \a x to the corresponding location in the view. * Return an invalid value if \a x cannot be mapped. */ QVariant AbstractGrid::mapFromChart( qreal x ) const { Q_UNUSED( x ); return QVariant(); } /*!\fn virtual Span AbstractGrid::mapToChart( const QModelIndex& idx ) const * Implement this to map from the data in the model to the location of * the corresponding item in the view. */ /*!\fn virtual bool AbstractGrid::mapFromChart( const Span& span, const QModelIndex& idx, const QList& constraints ) const * Implement this to update the model data based on the location of the item. Check * against the \a constraints list to make sure no hard constraints are violated by * writing back to the model. * \returns true if the update succeeded. */ /*!\fn virtual void AbstractGrid::paintGrid( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, AbstractRowController* rowController=0, QWidget* widget=0 ) * * Implement this to paint the background of the view -- typically * with some grid lines. * \param painter -- the QPainter to paint with. * \param sceneRect -- the total bounding rectangle of the scene. * \param exposedRect -- the rectangle that needs to be painted. * \param rowController -- the row controller used by the view -- may be 0. * \param widget -- the widget used by the view -- may be 0. */ /*!\fn virtual void AbstractGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget=0 ) * * Implement this to paint the header part of the view. * \param painter -- the QPainter to paint with. * \param headerRect -- the total rectangle occupied by the header. * \param exposedRect -- the rectangle that needs to be painted. * \param offset -- the horizontal scroll offset of the view. * \param widget -- the widget used by the view -- may be 0. */ /*! \todo document this function */ void AbstractGrid::drawBackground(QPainter* paint, const QRectF& rect) { - qInfo()<. */ #include "kganttdatetimegrid.h" #include "kganttdatetimegrid_p.h" #include "kganttabstractrowcontroller.h" #include #include #include #include #include #include #include #include #include #include using namespace KGantt; QDebug operator<<( QDebug dbg, KGantt::DateTimeScaleFormatter::Range range ) { switch ( range ) { case KGantt::DateTimeScaleFormatter::Second: dbg << "KGantt::DateTimeScaleFormatter::Second"; break; case KGantt::DateTimeScaleFormatter::Minute: dbg << "KGantt::DateTimeScaleFormatter::Minute"; break; case KGantt::DateTimeScaleFormatter::Hour: dbg << "KGantt::DateTimeScaleFormatter::Hour"; break; case KGantt::DateTimeScaleFormatter::Day: dbg << "KGantt::DateTimeScaleFormatter::Day"; break; case KGantt::DateTimeScaleFormatter::Week: dbg << "KGantt::DateTimeScaleFormatter::Week"; break; case KGantt::DateTimeScaleFormatter::Month: dbg << "KGantt::DateTimeScaleFormatter::Month"; break; case KGantt::DateTimeScaleFormatter::Year: dbg << "KGantt::DateTimeScaleFormatter::Year"; break; } return dbg; } /*!\class KGantt::DateTimeGrid * \ingroup KGantt * * This implementation of AbstractGrid works with QDateTime * and shows days and week numbers in the header */ qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const { assert( startDateTime.isValid() ); qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.; result += startDateTime.time().msecsTo(dt.time())/1000.; result *= dayWidth/( 24.*60.*60. ); return result; } QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const { assert( startDateTime.isValid() ); int days = static_cast( x/dayWidth ); qreal secs = x*( 24.*60.*60. )/dayWidth; QDateTime dt = startDateTime; QDateTime result = dt.addDays( days ) .addSecs( static_cast(secs-(days*24.*60.*60.) ) ) .addMSecs( qRound( ( secs-static_cast( secs ) )*1000. ) ); return result; } -void DateTimeGrid::Private::drawTimeline(QPainter* painter, const QRectF& rect) -{ - QDateTime dt = timelineTime.isValid() ? timelineTime : QDateTime::currentDateTime(); - qreal x = dateTimeToChartX(dt); - if (rect.contains(x, rect.top())) { - painter->save(); - QPen pen; - if (timelineOptions & UseCustomPen) { - pen = timelinePen; - } else { - pen = QPen(QApplication::palette().color(QPalette::Highlight), 0); - } - painter->setPen(pen); - painter->drawLine(x, rect.top(), x, rect.bottom()); - painter->restore(); - } -} - #define d d_func() /*!\class KGantt::DateTimeScaleFormatter * \ingroup KGantt * * This class formats dates and times used in DateTimeGrid follawing a given format. * * The format follows the format of QDateTime::toString(), with one addition: * "w" is replaced with the week number of the date as number without a leading zero (1-53) * "ww" is replaced with the week number of the date as number with a leading zero (01-53) * * For example: * * \code * // formatter to print the complete date over the current week * // This leads to the first day of the week being printed * DateTimeScaleFormatter formatter = DateTimeScaleFormatter( DateTimeScaleFormatter::Week, "yyyy-MM-dd" ); * \endcode * * Optionally, you can set an user defined text alignment flag. The default value is Qt::AlignCenter. * \sa DateTimeScaleFormatter::DateTimeScaleFormatter * * This class even controls the range of the grid sections. * \sa KGanttDateTimeScaleFormatter::Range */ /*! Creates a DateTimeScaleFormatter using \a range and \a format. * The text on the header is aligned following \a alignment. */ DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, const QString& templ, Qt::Alignment alignment ) : _d( new Private( range, format, templ, alignment ) ) { } DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment ) : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) ) { } DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other ) : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) ) { } DateTimeScaleFormatter::~DateTimeScaleFormatter() { delete _d; } DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other ) { if ( this == &other ) return *this; delete _d; _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() ); return *this; } /*! \returns The format being used for formatting dates and times. */ QString DateTimeScaleFormatter::format() const { return d->format; } /*! \returns The \a datetime as string respecting the format. */ QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const { QString result = d->format; // additional feature: Weeknumber const QString shortWeekNumber = QString::number( datetime.date().weekNumber()) + QLatin1String("/") + QString::number( datetime.date().year()); const QString longWeekNumber = ( shortWeekNumber.length() == 1 ? QString::fromLatin1( "0" ) : QString() ) + shortWeekNumber; result.replace( QString::fromLatin1( "ww" ), longWeekNumber ); result.replace( QString::fromLatin1( "w" ), shortWeekNumber ); result = datetime.toLocalTime().toString( result ); return result; } QString DateTimeScaleFormatter::text( const QDateTime& datetime ) const { return d->templ.arg( format( datetime ) ); } /*! \returns The range of each item on a DateTimeGrid header. * \sa DateTimeScaleFormatter::Range */ DateTimeScaleFormatter::Range DateTimeScaleFormatter::range() const { return d->range; } Qt::Alignment DateTimeScaleFormatter::alignment() const { return d->alignment; } /*! \returns the QDateTime being the begin of the range after the one containing \a datetime * \sa currentRangeBegin */ QDateTime DateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const { QDateTime result = datetime; switch ( d->range ) { case Second: result = result.addSecs( 60 ); break; case Minute: // set it to the begin of the next minute result.setTime( QTime( result.time().hour(), result.time().minute() ) ); result = result.addSecs( 60 ); break; case Hour: // set it to the begin of the next hour result.setTime( QTime( result.time().hour(), 0 ) ); result = result.addSecs( 60 * 60 ); break; case Day: // set it to midnight the next day result.setTime( QTime( 0, 0 ) ); result = result.addDays( 1 ); break; case Week: // set it to midnight result.setTime( QTime( 0, 0 ) ); // iterate day-wise, until weekNumber changes { const int weekNumber = result.date().weekNumber(); while ( weekNumber == result.date().weekNumber() ) result = result.addDays( 1 ); } break; case Month: // set it to midnight result.setTime( QTime( 0, 0 ) ); // set it to the first of the next month result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) ); break; case Year: // set it to midnight result.setTime( QTime( 0, 0 ) ); // set it to the first of the next year result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) ); break; } //result = result.toLocalTime(); assert( result != datetime ); //qDebug() << "DateTimeScaleFormatter::nextRangeBegin("<range<range ) { case Second: break; // nothing case Minute: // set it to the begin of the current minute result.setTime( QTime( result.time().hour(), result.time().minute() ) ); break; case Hour: // set it to the begin of the current hour result.setTime( QTime( result.time().hour(), 0 ) ); break; case Day: // set it to midnight the current day result.setTime( QTime( 0, 0 ) ); break; case Week: // set it to midnight result.setTime( QTime( 0, 0 ) ); // iterate day-wise, as long weekNumber is the same { const int weekNumber = result.date().weekNumber(); while ( weekNumber == result.date().addDays( -1 ).weekNumber() ) result = result.addDays( -1 ); } break; case Month: // set it to midnight result.setTime( QTime( 0, 0 ) ); // set it to the first of the current month result.setDate( QDate( result.date().year(), result.date().month(), 1 ) ); break; case Year: // set it to midnight result.setTime( QTime( 0, 0 ) ); // set it to the first of the current year result.setDate( QDate( result.date().year(), 1, 1 ) ); break; } return result; } DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private ) { } DateTimeGrid::~DateTimeGrid() { } /*! \returns The QDateTime used as start date for the grid. * * The default is three days before the current date. */ QDateTime DateTimeGrid::startDateTime() const { return d->startDateTime; } /*! \param dt The start date of the grid. It is used as the beginning of the * horizontal scrollbar in the view. * * Emits gridChanged() after the start date has changed. */ void DateTimeGrid::setStartDateTime( const QDateTime& dt ) { d->startDateTime = dt; emit gridChanged(); } /*! \returns The width in pixels for each day in the grid. * * The default is 100 pixels. */ qreal DateTimeGrid::dayWidth() const { return d->dayWidth; } /*! Maps a given point in time \a dt to an X value in the scene. */ qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const { return d->dateTimeToChartX( dt ); } /*! Maps a given X value \a x in scene coordinates to a point in time. */ QDateTime DateTimeGrid::mapToDateTime( qreal x ) const { return d->chartXtoDateTime( x ); } /*! \param w The width in pixels for each day in the grid. * * The signal gridChanged() is emitted after the day width is changed. */ void DateTimeGrid::setDayWidth( qreal w ) { assert( w>0 ); d->dayWidth = w; emit gridChanged(); } /*! \param s The scale to be used to paint the grid. * * The signal gridChanged() is emitted after the scale has changed. * \sa Scale * * Following example demonstrates how to change the format of the header to use * a date-scaling with the header-label displayed with the ISO date-notation. * \code * DateTimeScaleFormatter* formatter = new DateTimeScaleFormatter(DateTimeScaleFormatter::Day, QString::fromLatin1("yyyy-MMMM-dddd")); * grid->setUserDefinedUpperScale( formatter ); * grid->setUserDefinedLowerScale( formatter ); * grid->setScale( DateTimeGrid::ScaleUserDefined ); * \endcode */ void DateTimeGrid::setScale( Scale s ) { d->scale = s; emit gridChanged(); } /*! \returns The scale used to paint the grid. * * The default is ScaleAuto, which means the day scale will be used * as long as the day width is less or equal to 500. * \sa Scale */ DateTimeGrid::Scale DateTimeGrid::scale() const { return d->scale; } /*! Sets the scale formatter for the lower part of the header to the * user defined formatter to \a lower. The DateTimeGrid object takes * ownership of the formatter, which has to be allocated with new. * * You have to set the scale to ScaleUserDefined for this setting to take effect. * \sa DateTimeScaleFormatter */ void DateTimeGrid::setUserDefinedLowerScale( DateTimeScaleFormatter* lower ) { delete d->lower; d->lower = lower; emit gridChanged(); } /*! Sets the scale formatter for the upper part of the header to the * user defined formatter to \a upper. The DateTimeGrid object takes * ownership of the formatter, which has to be allocated with new. * * You have to set the scale to ScaleUserDefined for this setting to take effect. * \sa DateTimeScaleFormatter */ void DateTimeGrid::setUserDefinedUpperScale( DateTimeScaleFormatter* upper ) { delete d->upper; d->upper = upper; emit gridChanged(); } /*! \return The DateTimeScaleFormatter being used to render the lower scale. */ DateTimeScaleFormatter* DateTimeGrid::userDefinedLowerScale() const { return d->lower; } /*! \return The DateTimeScaleFormatter being used to render the upper scale. */ DateTimeScaleFormatter* DateTimeGrid::userDefinedUpperScale() const { return d->upper; } /*! \param ws The start day of the week. * * A solid line is drawn on the grid to mark the beginning of a new week. * Emits gridChanged() after the start day has changed. */ void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws ) { d->weekStart = ws; emit gridChanged(); } /*! \returns The start day of the week */ Qt::DayOfWeek DateTimeGrid::weekStart() const { return d->weekStart; } /*! \param fd A set of days to mark as free in the grid. * * Free days are filled with the alternate base brush of the * palette used by the view. * The signal gridChanged() is emitted after the free days are changed. */ void DateTimeGrid::setFreeDays( const QSet& fd ) { d->freeDays = fd; emit gridChanged(); } /*! \returns The days marked as free in the grid. */ QSet DateTimeGrid::freeDays() const { return d->freeDays; } /*! Sets the brush to use to paint free days. */ void DateTimeGrid::setFreeDaysBrush(const QBrush brush) { d->freeDaysBrush = brush; } /*! \returns The brush used to paint free days. */ QBrush DateTimeGrid::freeDaysBrush() const { return d->freeDaysBrush; } /*! \returns true if row separators are used. */ bool DateTimeGrid::rowSeparators() const { return d->rowSeparators; } /*! \param enable Whether to use row separators or not. */ void DateTimeGrid::setRowSeparators( bool enable ) { d->rowSeparators = enable; } /*! Sets the brush used to display rows where no data is found. * Default is a red pattern. If set to QBrush() rows with no * information will not be marked. */ void DateTimeGrid::setNoInformationBrush( const QBrush& brush ) { d->noInformationBrush = brush; emit gridChanged(); } /*! \returns the brush used to mark rows with no information. */ QBrush DateTimeGrid::noInformationBrush() const { return d->noInformationBrush; } /*! * \param value The datetime to get the x value for. * \returns The x value corresponding to \a value or -1.0 if \a value is not a datetime variant. */ qreal DateTimeGrid::mapToChart( const QVariant& value ) const { if ( ! value.canConvert( QVariant::DateTime ) || ( value.type() == QVariant::String && value.toString().isEmpty() ) ) { return -1.0; } return d->dateTimeToChartX( value.toDateTime() ); } /*! * \param x The x value get the datetime for. * \returns The datetime corresponding to \a x or an invalid datetime if x cannot be mapped. */ QVariant DateTimeGrid::mapFromChart( qreal x ) const { return d->chartXtoDateTime( x ); } /*! \param idx The index to get the Span for. * \returns The start and end pixels, in a Span, of the specified index. */ Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const { assert( model() ); if ( !idx.isValid() ) return Span(); assert( idx.model()==model() ); const QVariant sv = model()->data( idx, StartTimeRole ); const QVariant ev = model()->data( idx, EndTimeRole ); if ( sv.canConvert( QVariant::DateTime ) && ev.canConvert( QVariant::DateTime ) && !(sv.type() == QVariant::String && sv.toString().isEmpty()) && !(ev.type() == QVariant::String && ev.toString().isEmpty()) ) { QDateTime st = sv.toDateTime(); QDateTime et = ev.toDateTime(); if ( et.isValid() && st.isValid() ) { qreal sx = d->dateTimeToChartX( st ); qreal ex = d->dateTimeToChartX( et )-sx; //qDebug() << "DateTimeGrid::mapToChart("< "<< Span( sx, ex ); return Span( sx, ex); } } // Special case for Events with only a start date if ( sv.canConvert( QVariant::DateTime ) && !(sv.type() == QVariant::String && sv.toString().isEmpty()) ) { QDateTime st = sv.toDateTime(); if ( st.isValid() ) { qreal sx = d->dateTimeToChartX( st ); return Span( sx, 0 ); } } return Span(); } #if 0 static void debug_print_idx( const QModelIndex& idx ) { if ( !idx.isValid() ) { qDebug() << "[Invalid]"; return; } QDateTime st = idx.data( StartTimeRole ).toDateTime(); QDateTime et = idx.data( EndTimeRole ).toDateTime(); qDebug() << idx << "["<& constraints ) const { assert( model() ); if ( !idx.isValid() ) return false; assert( idx.model()==model() ); QDateTime st = d->chartXtoDateTime(span.start()); QDateTime et = d->chartXtoDateTime(span.start()+span.length()); //qDebug() << "DateTimeGrid::mapFromChart("< "<< st << et; Q_FOREACH( const Constraint& c, constraints ) { if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue; if ( c.startIndex() == idx ) { QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime(); //qDebug() << tmpst << "<" << et <<"?"; if ( tmpstdata( c.startIndex(), EndTimeRole ).toDateTime(); //qDebug() << tmpet << ">" << st <<"?"; if ( tmpet>st ) return false; } } return model()->setData( idx, qVariantFromValue(st), StartTimeRole ) && model()->setData( idx, qVariantFromValue(et), EndTimeRole ); } Qt::PenStyle DateTimeGrid::Private::gridLinePenStyle( QDateTime dt, Private::HeaderType headerType ) const { switch ( headerType ) { case Private::HeaderHour: // Midnight if ( dt.time().hour() == 0 ) return Qt::SolidLine; return Qt::DashLine; case Private::HeaderDay: // First day of the week if ( dt.date().dayOfWeek() == weekStart ) return Qt::SolidLine; return Qt::DashLine; case Private::HeaderWeek: // First day of the month if ( dt.date().day() == 1 ) return Qt::SolidLine; // First day of the week if ( dt.date().dayOfWeek() == weekStart ) return Qt::DashLine; return Qt::NoPen; case Private::HeaderMonth: // First day of the year if ( dt.date().dayOfYear() == 1 ) return Qt::SolidLine; // First day of the month if ( dt.date().day() == 1 ) return Qt::DashLine; return Qt::NoPen; default: // Nothing to do here break; } // Default return Qt::NoPen; } QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const { // In any case, set time to 00:00:00:00 dt.setTime( QTime( 0, 0, 0, 0 ) ); switch ( headerType ) { case Private::HeaderWeek: // Set day to beginning of the week while ( dt.date().dayOfWeek() != weekStart ) dt = dt.addDays( -1 ); break; case Private::HeaderMonth: // Set day to beginning of the month dt = dt.addDays( 1 - dt.date().day() ); break; case Private::HeaderYear: // Set day to first day of the year dt = dt.addDays( 1 - dt.date().dayOfYear() ); break; default: // In any other case, we don't need to adjust the date time break; } return dt; } void DateTimeGrid::Private::paintVerticalLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, QWidget* widget, Private::HeaderType headerType ) { QDateTime dt = chartXtoDateTime( exposedRect.left() ); dt = adjustDateTimeForHeader( dt, headerType ); int offsetSeconds = 0; int offsetDays = 0; // Determine the time step per grid line if ( headerType == Private::HeaderHour ) offsetSeconds = 60*60; else offsetDays = 1; for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), x = dateTimeToChartX( dt ) ) { //TODO not the best solution as it might be one paint too much, but i don't know what //causes the test to fail yet, i think it might be a rounding error //if ( x >= exposedRect.left() ) { QPen pen = painter->pen(); pen.setBrush( QApplication::palette().dark() ); pen.setStyle( gridLinePenStyle( dt, headerType ) ); painter->setPen( pen ); if ( freeDays.contains( static_cast( dt.date().dayOfWeek() ) ) ) { if (freeDaysBrush.style() == Qt::NoBrush) painter->setBrush( widget?widget->palette().midlight() :QApplication::palette().midlight() ); else painter->setBrush(freeDaysBrush); painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() ); } painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) ); //} } } void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, const DateTimeScaleFormatter* formatter, QWidget* widget ) { Q_UNUSED( widget ); QDateTime dt = chartXtoDateTime( exposedRect.left() ); dt = formatter->currentRangeBegin( dt ); QPen pen = painter->pen(); pen.setBrush( QApplication::palette().dark() ); pen.setStyle( Qt::DashLine ); painter->setPen( pen ); for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right(); dt = formatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) ) { if ( freeDays.contains( static_cast( dt.date().dayOfWeek() ) ) ) { QBrush oldBrush = painter->brush(); if (freeDaysBrush.style() == Qt::NoBrush) painter->setBrush( widget?widget->palette().midlight() :QApplication::palette().midlight() ); else painter->setBrush(freeDaysBrush); painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() ); painter->setBrush( oldBrush ); } //TODO not the best solution as it might be one paint too much, but i don't know what //causes the test to fail yet, i think it might be a rounding error //if ( x >= exposedRect.left() ) { // FIXME: Also fill area between this and the next vertical line to indicate free days? (Johannes) painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) ); //} } } DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale ) { switch ( scale ) { case ScaleHour: return Private::HeaderHour; case ScaleDay: return Private::HeaderDay; case ScaleWeek: return Private::HeaderWeek; case ScaleMonth: return Private::HeaderMonth; default: // There are no specific header types for any other scale! assert( false ); break; } return Private::HeaderDay; } void DateTimeGrid::paintGrid( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, AbstractRowController* rowController, QWidget* widget ) { // TODO: Support hours and weeks switch ( scale() ) { case ScaleHour: case ScaleDay: case ScaleWeek: case ScaleMonth: d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) ); break; case ScaleAuto: { const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) ); const qreal dayw = dayWidth(); if ( dayw > 24*60*60*tabw ) { d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->minute_lower, widget ); } else if ( dayw > 24*60*tabw ) { d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderHour ); } else if ( dayw > 24*tabw ) { d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderDay ); } else if ( dayw > tabw ) { d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->week_lower, widget ); } else if ( 4*dayw > tabw ) { d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->month_lower, widget ); } else { d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->year_lower, widget ); } break; } case ScaleUserDefined: d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, d->lower, widget ); break; } if ( rowController ) { // First draw the rows QPen pen = painter->pen(); pen.setBrush( QApplication::palette().dark() ); pen.setStyle( Qt::DashLine ); painter->setPen( pen ); QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) ); if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx ); qreal y = 0; while ( y < exposedRect.bottom() && idx.isValid() ) { const Span s = rowController->rowGeometry( idx ); y = s.start()+s.length(); if ( d->rowSeparators ) { painter->drawLine( QPointF( sceneRect.left(), y ), QPointF( sceneRect.right(), y ) ); } if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) { painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush ); } // Is alternating background better? //if ( idx.row()%2 ) painter->fillRect( QRectF( exposedRect.x(), s.start(), exposedRect.width(), s.length() ), QApplication::palette().alternateBase() ); idx = rowController->indexBelow( idx ); } } } int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const { QStyleOptionHeader opt; if ( widget ) opt.initFrom( widget ); opt.text = txt; QStyle* style; if ( widget ) style = widget->style(); else style = QApplication::style(); QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget); return s.height(); } void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper) { const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) ); const qreal dayw = dayWidth; if ( dayw > 24*60*60*tabw ) { *lower = &minute_lower; *upper = &minute_upper; } else if ( dayw > 24*60*tabw ) { *lower = &hour_lower; *upper = &hour_upper; } else if ( dayw > 24*tabw ) { *lower = &day_lower; *upper = &day_upper; } else if ( dayw > tabw ) { *lower = &week_lower; *upper = &week_upper; } else if ( 4*dayw > tabw ) { *lower = &month_lower; *upper = &month_upper; } else { *lower = &year_lower; *upper = &year_upper; } } void DateTimeGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget ) { painter->save(); QPainterPath clipPath; clipPath.addRect( headerRect ); painter->setClipPath( clipPath, Qt::IntersectClip ); switch ( scale() ) { case ScaleHour: paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget ); break; case ScaleDay: paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget ); break; case ScaleWeek: paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget ); break; case ScaleMonth: paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget ); break; case ScaleAuto: { DateTimeScaleFormatter *lower, *upper; d->getAutomaticFormatters( &lower, &upper ); const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) ); const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) ); const qreal upperRatio = upperHeight/( lowerHeight+upperHeight ); const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio ); const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 ); paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget ); paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget ); break; } case ScaleUserDefined: { const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) ); const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) ); const qreal upperRatio = upperHeight/( lowerHeight+upperHeight ); const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio ); const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 ); paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget ); paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget ); } break; } painter->restore(); } void DateTimeGrid::paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const DateTimeScaleFormatter* formatter, QWidget* widget ) { const QStyle* const style = widget ? widget->style() : QApplication::style(); QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() )); qreal x = d->dateTimeToChartX( dt ); while ( x < exposedRect.right() + offset ) { const QDateTime next = formatter->nextRangeBegin( dt ); const qreal nextx = d->dateTimeToChartX( next ); QStyleOptionHeader opt; if ( widget ) opt.init( widget ); opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect(); opt.textAlignment = formatter->alignment(); opt.text = formatter->text( dt ); style->drawControl( QStyle::CE_Header, &opt, painter, widget ); dt = next; x = nextx; } } void DateTimeGrid::Private::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget, Private::HeaderType headerType, DateTextFormatter *formatter ) { QStyle* style = widget?widget->style():QApplication::style(); const qreal left = exposedRect.left() + offset; const qreal right = exposedRect.right() + offset; // Paint a section for each hour QDateTime dt = chartXtoDateTime( left ); dt = adjustDateTimeForHeader( dt, headerType ); // Determine the time step per grid line int offsetSeconds = 0; int offsetDays = 0; int offsetMonths = 0; switch ( headerType ) { case Private::HeaderHour: offsetSeconds = 60*60; break; case Private::HeaderDay: offsetDays = 1; break; case Private::HeaderWeek: offsetDays = 7; break; case Private::HeaderMonth: offsetMonths = 1; break; case Private::HeaderYear: offsetMonths = 12; break; default: // Other scales cannot be painted with this method! assert( false ); break; } for ( qreal x = dateTimeToChartX( dt ); x < right; dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ), x = dateTimeToChartX( dt ) ) { QStyleOptionHeader opt; if ( widget ) opt.init( widget ); opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt ); opt.text = formatter->format( dt ); opt.textAlignment = Qt::AlignCenter; style->drawControl(QStyle::CE_Header, &opt, painter, widget); } } /*! Paints the hour scale header. * \sa paintHeader() */ void DateTimeGrid::paintHourScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget ) { class HourFormatter : public Private::DateTextFormatter { public: virtual ~HourFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return dt.time().toString( QString::fromLatin1( "hh" ) ); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { Q_UNUSED(dt); return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ), QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderHour, new HourFormatter ); // Custom parameters class DayFormatter : public Private::DateTextFormatter { public: virtual ~DayFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return dt.date().toString(); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { Q_UNUSED(dt); return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ), QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderDay, new DayFormatter ); // Custom parameters } /*! Paints the day scale header. * \sa paintHeader() */ void DateTimeGrid::paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget ) { class DayFormatter : public Private::DateTextFormatter { public: virtual ~DayFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return dt.toString( QString::fromLatin1( "ddd" ) ).left( 1 ); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { Q_UNUSED(dt); return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ), QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderDay, new DayFormatter ); // Custom parameters class WeekFormatter : public Private::DateTextFormatter { public: virtual ~WeekFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return QString::number(dt.date().weekNumber()) + QLatin1String("/") + QString::number(dt.date().year()); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { Q_UNUSED(dt); return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ), QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderWeek, new WeekFormatter ); // Custom parameters } /*! Paints the week scale header. * \sa paintHeader() */ void DateTimeGrid::paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget ) { class WeekFormatter : public Private::DateTextFormatter { public: virtual ~WeekFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return QString::number( dt.date().weekNumber() ); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { Q_UNUSED(dt); return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ), QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderWeek, new WeekFormatter ); // Custom parameters class MonthFormatter : public Private::DateTextFormatter { public: virtual ~MonthFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return QLocale().monthName(dt.date().month(), QLocale::LongFormat) + QLatin1String("/") + QString::number(dt.date().year()); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ), QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderMonth, new MonthFormatter ); // Custom parameters } /*! Paints the week scale header. * \sa paintHeader() */ void DateTimeGrid::paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget ) { class MonthFormatter : public Private::DateTextFormatter { public: virtual ~MonthFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return QLocale().monthName(dt.date().month(), QLocale::ShortFormat) + QLatin1String("/") + QString::number(dt.date().year()); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ), QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderMonth, new MonthFormatter ); // Custom parameters class YearFormatter : public Private::DateTextFormatter { public: virtual ~YearFormatter() {} QString format( const QDateTime& dt ) Q_DECL_OVERRIDE { return QString::number( dt.date().year() ); } QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) Q_DECL_OVERRIDE { return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ), QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect(); } }; d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters Private::HeaderYear, new YearFormatter ); // Custom parameters } /*! Draw the background for a day. */ void DateTimeGrid::drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date) { + Q_UNUSED(painter); + Q_UNUSED(rect); Q_UNUSED(date); - if (d->timelineOptions & Background) { - d->drawTimeline(painter, rect); - } } /*! Draw the foreground for a day. */ void DateTimeGrid::drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date) { + Q_UNUSED(painter); + Q_UNUSED(rect); Q_UNUSED(date); - if (d->timelineOptions & Foreground) { - d->drawTimeline(painter, rect); - } } /** Return the rectangle that represents the date-range. */ QRectF DateTimeGrid::computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const { qreal topLeft = d->dateTimeToChartX(from); qreal topRight = d->dateTimeToChartX(to); return QRectF(topLeft, rect.top(), topRight - topLeft, rect.height()); } /** Return a date-range represented by the rectangle. */ QPair DateTimeGrid::dateTimeRange(const QRectF& rect) const { QDateTime start; QDateTime end; start = d->chartXtoDateTime(rect.left()); end = d->chartXtoDateTime(rect.right()); return qMakePair(start, end); } void DateTimeGrid::drawBackground(QPainter* paint, const QRectF& rect) { int offset = (int)dayWidth(); assert( offset>0 ); // Figure out the date at the extreme left QDate date = d->chartXtoDateTime(rect.left()).date(); // We need to paint from one end to the other int startx = rect.left(); int endx = rect.right(); // Save the painter state paint->save(); // Paint the first date column while (1) { QDate nextDate = d->chartXtoDateTime(startx+1).date(); if (date != nextDate) { QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height()); dayRect = dayRect.adjusted(1, 0, 0, 0); drawDayBackground(paint, dayRect, date); break; } ++startx; } // Paint the remaining dates for (int i=startx; ichartXtoDateTime(i+1).date(); QRectF dayRect(i, rect.top(), dayWidth(), rect.height()); dayRect = dayRect.adjusted(1, 0, 0, 0); drawDayBackground(paint, dayRect, date); } // Restore the painter state paint->restore(); } void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect) { int offset = (int)dayWidth(); // Figure out the date at the extreme left QDate date = d->chartXtoDateTime(rect.left()).date(); // We need to paint from one end to the other int startx = rect.left(); int endx = rect.right(); // Save the painter state paint->save(); // Paint the first date column while (1) { QDate nextDate = d->chartXtoDateTime(startx+1).date(); if (date != nextDate) { QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height()); dayRect = dayRect.adjusted(1, 0, 0, 0); drawDayForeground(paint, dayRect, date); break; } ++startx; } // Paint the remaining dates for (int i=startx; ichartXtoDateTime(i+1).date(); QRectF dayRect(i, rect.top(), dayWidth(), rect.height()); dayRect = dayRect.adjusted(1, 0, 0, 0); drawDayForeground(paint, dayRect, date); } // Restore the painter state paint->restore(); } -void DateTimeGrid::setTimelineTime(const QDateTime &dt) -{ - d->timelineTime = dt; - emit gridChanged(); -} - -QDateTime DateTimeGrid::timelineTime() const -{ - return d->timelineTime; -} - -void DateTimeGrid::setTimelinePen(const QPen &pen) -{ - d->timelinePen = pen; - emit gridChanged(); -} - -void DateTimeGrid::setTimelineOptions(DateTimeGrid::TimelineOptions mode) -{ - d->timelineOptions = mode; - emit gridChanged(); -} - -DateTimeGrid::TimelineOptions DateTimeGrid::timelineOptions() const -{ - return d->timelineOptions; -} - #undef d #ifndef KDAB_NO_UNIT_TESTS #include #include "unittest/test.h" static std::ostream& operator<<( std::ostream& os, const QDateTime& dt ) { #ifdef QT_NO_STL os << dt.toString().toLatin1().constData(); #else os << dt.toString().toStdString(); #endif return os; } KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, DateTimeGrid, "test" ) { QStandardItemModel model( 3, 2 ); DateTimeGrid grid; QDateTime dt = QDateTime::currentDateTime(); grid.setModel( &model ); QDateTime startdt = dt.addDays( -10 ); grid.setStartDateTime( startdt ); model.setData( model.index( 0, 0 ), dt, StartTimeRole ); model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole ); model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole ); model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole ); Span s = grid.mapToChart( model.index( 0, 0 ) ); //qDebug() << "span="<0 ); assertTrue( s.length()>0 ); assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) ); grid.mapFromChart( s, model.index( 1, 0 ) ); QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime(); QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime(); QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime(); QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime(); assertTrue( s1.isValid() ); assertTrue( e1.isValid() ); assertTrue( s2.isValid() ); assertTrue( e2.isValid() ); assertEqual( s1, s2 ); assertEqual( e1, e2 ); assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) ); assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) ); s = grid.mapToChart( model.index( 0, 0 ) ); s.setEnd( s.end()+100000. ); bool rc = grid.mapFromChart( s, model.index( 0, 0 ) ); assertTrue( rc ); assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() ); Span newspan = grid.mapToChart( model.index( 0, 0 ) ); assertEqual( newspan.start(), s.start() ); assertEqual( newspan.length(), s.length() ); { QDateTime startDateTime = QDateTime::currentDateTime(); qreal dayWidth = 100; QDate currentDate = QDate::currentDate(); QDateTime dt( QDate(currentDate.year(), 1, 1), QTime( 0, 0, 0, 0 ) ); assert( dt.isValid() ); qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.; result += startDateTime.time().msecsTo(dt.time())/1000.; result *= dayWidth/( 24.*60.*60. ); int days = static_cast( result/dayWidth ); qreal secs = result*( 24.*60.*60. )/dayWidth; QDateTime dt2 = startDateTime; QDateTime result2 = dt2.addDays( days ).addSecs( static_cast(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast( secs ) )*1000. ) ); assertEqual( dt, result2 ); } } #endif /* KDAB_NO_UNIT_TESTS */ #include "moc_kganttdatetimegrid.cpp" diff --git a/src/KGantt/kganttdatetimegrid.h b/src/KGantt/kganttdatetimegrid.h index 89d1676..c28876c 100644 --- a/src/KGantt/kganttdatetimegrid.h +++ b/src/KGantt/kganttdatetimegrid.h @@ -1,238 +1,165 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTDATETIMEGRID_H #define KGANTTDATETIMEGRID_H #include "kganttabstractgrid.h" #include #include namespace KGantt { class DateTimeScaleFormatter; class KGANTT_EXPORT DateTimeGrid : public AbstractGrid { Q_OBJECT KGANTT_DECLARE_PRIVATE_DERIVED( DateTimeGrid ) public: enum Scale { ScaleAuto, ScaleHour, ScaleDay, ScaleWeek, ScaleMonth, ScaleUserDefined }; DateTimeGrid(); virtual ~DateTimeGrid(); QDateTime startDateTime() const; void setStartDateTime( const QDateTime& dt ); qreal dayWidth() const; void setDayWidth( qreal ); qreal mapFromDateTime( const QDateTime& dt) const; QDateTime mapToDateTime( qreal x ) const; void setWeekStart( Qt::DayOfWeek ); Qt::DayOfWeek weekStart() const; void setFreeDays( const QSet& fd ); QSet freeDays() const; void setFreeDaysBrush(const QBrush brush); QBrush freeDaysBrush() const; void setScale( Scale s ); Scale scale() const; void setUserDefinedLowerScale( DateTimeScaleFormatter* lower ); void setUserDefinedUpperScale( DateTimeScaleFormatter* upper ); DateTimeScaleFormatter* userDefinedLowerScale() const; DateTimeScaleFormatter* userDefinedUpperScale() const; bool rowSeparators() const; void setRowSeparators( bool enable ); void setNoInformationBrush( const QBrush& brush ); QBrush noInformationBrush() const; /*reimp*/ Span mapToChart( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ bool mapFromChart( const Span& span, const QModelIndex& idx, const QList& constraints=QList() ) const Q_DECL_OVERRIDE; /*reimp*/ qreal mapToChart( const QVariant& value ) const Q_DECL_OVERRIDE; /*reimp*/ QVariant mapFromChart( qreal x ) const Q_DECL_OVERRIDE; /*reimp*/ void paintGrid( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, AbstractRowController* rowController = nullptr, QWidget* widget = nullptr ) Q_DECL_OVERRIDE; /*reimp*/ void paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget = nullptr ) Q_DECL_OVERRIDE; - /// \enum TimelineOptions controls how the timeline is displayed - enum TimelineOption { - Foreground = 1, /// Display the timeline in the foreground. - Background = 2, /// Display the timeline in the background. - UseCustomPen = 4, /// Paint the timeline using the pen set with setTimelinePen(). - TimelineModeMax = 0xFFFF - }; - Q_DECLARE_FLAGS(TimelineOptions, TimelineOption) - - /** - * \brief Set the timeline options to \p options - * - * By default the timeline is not displayed. - * - * To display the timeline you have to set the options to either - * Foreground or Background, e.g: - * \code - * grid->setTimelineOptions(KGantt::DateTimeGrid::Foreground); - * \endcode - * - * \sa TimelineOptions - * \sa DateTimeGrid::setTimelineTime - * \sa DateTimeGrid::setTimelinePen - */ - void setTimelineOptions(TimelineOptions options); - - /** - * Return the current timeline options - * - * \sa DateTimeGrid::setTimelineMode() - */ - TimelineOptions timelineOptions() const; - - /** - * Returns the current timeline time - * - * \sa DateTimeGrid::setTimelineTime() - */ - QDateTime timelineTime() const; - - /** - * \brief Set a cutsom pen to use when drawing the timeline. - * - * The default pen is a solid line, cosmetic pen - * with QApplication::palette().highlight() color. - * - * You must enable use by setting the flag UseCustomPen \see setTimelineOptions() - */ - void setTimelinePen(const QPen &pen); - - public Q_SLOTS: - /** - * \brief Set the timeline time to \p dt. - * - * If \p dt is not valid the current datetime will be used - * - * If you want the timeline to be periodically updated with the current time, - * you can do something like this in your program: - * \code - * QTimer *timelineTimer = new QTimer(this); - * timelineTimer->setInterval(5000); - * timelineTimer->start(); - * grid->setTimelineOptions(KGantt::DateTimeGrid::Foreground); - * connect(timelineTimer, SIGNAL(timeout()), grid, SLOT(setTimelineTime())); - * \endcode - * - * \sa DateTimeGrid::setTimelineOptions() - * \sa DateTimeGrid::setTimelinePen() - */ - void setTimelineTime(const QDateTime &dt = QDateTime()); - protected: virtual void paintHourScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget = nullptr ); virtual void paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget = nullptr ); virtual void paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget = nullptr ); virtual void paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget = nullptr ); virtual void paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const DateTimeScaleFormatter* formatter, QWidget* widget = nullptr ); virtual void drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date); virtual void drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date); QRectF computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const; QPair dateTimeRange(const QRectF& rect) const; /* reimp */ void drawBackground(QPainter* paint, const QRectF& rect) Q_DECL_OVERRIDE; /* reimp */ void drawForeground(QPainter* paint, const QRectF& rect) Q_DECL_OVERRIDE; }; class KGANTT_EXPORT DateTimeScaleFormatter { KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( DateTimeScaleFormatter ) public: enum Range { Second, Minute, Hour, Day, Week, Month, Year }; DateTimeScaleFormatter( Range range, const QString& formatString, Qt::Alignment alignment = Qt::AlignCenter ); DateTimeScaleFormatter( Range range, const QString& formatString, const QString& templ, Qt::Alignment alignment = Qt::AlignCenter ); DateTimeScaleFormatter( const DateTimeScaleFormatter& other ); virtual ~DateTimeScaleFormatter(); DateTimeScaleFormatter& operator=( const DateTimeScaleFormatter& other ); QString format() const; Range range() const; Qt::Alignment alignment() const; virtual QDateTime nextRangeBegin( const QDateTime& datetime ) const; virtual QDateTime currentRangeBegin( const QDateTime& datetime ) const; QString format( const QDateTime& datetime ) const; virtual QString text( const QDateTime& datetime ) const; }; } #ifndef QT_NO_DEBUG_STREAM QDebug KGANTT_EXPORT operator<<( QDebug dbg, KGantt::DateTimeScaleFormatter::Range ); #endif -Q_DECLARE_OPERATORS_FOR_FLAGS(KGantt::DateTimeGrid::TimelineOptions) - #endif /* KGANTTDATETIMEGRID_H */ diff --git a/src/KGantt/kganttdatetimegrid_p.h b/src/KGantt/kganttdatetimegrid_p.h index 8db031e..300f282 100644 --- a/src/KGantt/kganttdatetimegrid_p.h +++ b/src/KGantt/kganttdatetimegrid_p.h @@ -1,176 +1,166 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTDATETIMEGRID_P_H #define KGANTTDATETIMEGRID_P_H #include "kganttdatetimegrid.h" #include "kganttabstractgrid_p.h" #include #include -#include -#include namespace KGantt { class Q_DECL_HIDDEN DateTimeScaleFormatter::Private { public: Private( DateTimeScaleFormatter::Range _range, const QString& _format, const QString& _templ, Qt::Alignment _alignment ) : range( _range ), format( _format ), templ( _templ ), alignment( _alignment ) { } const DateTimeScaleFormatter::Range range; const QString format; const QString templ; const Qt::Alignment alignment; }; class Q_DECL_HIDDEN DateTimeGrid::Private : public AbstractGrid::Private { public: Private() : startDateTime( QDateTime::currentDateTime().addDays( -3 ) ), dayWidth( 100. ), scale( ScaleAuto ), weekStart( Qt::Monday ), freeDays( QSet() << Qt::Saturday << Qt::Sunday ), rowSeparators( false ), noInformationBrush( Qt::red, Qt::DiagCrossPattern ), freeDaysBrush(QBrush()), upper( new DateTimeScaleFormatter( DateTimeScaleFormatter::Week, QString::fromLatin1( "w" ) ) ), lower( new DateTimeScaleFormatter( DateTimeScaleFormatter::Day, QString::fromLatin1( "ddd" ) ) ), year_upper( DateTimeScaleFormatter::Year, QString::fromLatin1("yyyy" ) ), year_lower( DateTimeScaleFormatter::Month, QString::fromLatin1("MMM" ) ), month_upper( DateTimeScaleFormatter::Month, QString::fromLatin1("MMMM" ) ), month_lower( DateTimeScaleFormatter::Week, QString::fromLatin1("w" ) ), week_upper( DateTimeScaleFormatter::Week, QString::fromLatin1("w" ) ), week_lower( DateTimeScaleFormatter::Day, QString::fromLatin1("ddd" ) ), day_upper( DateTimeScaleFormatter::Day, QString::fromLatin1("dddd" ) ), day_lower( DateTimeScaleFormatter::Hour, QString::fromLatin1("hh" ) ), hour_upper( DateTimeScaleFormatter::Hour, QString::fromLatin1("hh" ) ), hour_lower( DateTimeScaleFormatter::Minute, QString::fromLatin1("m" ) ), minute_upper( DateTimeScaleFormatter::Minute, QString::fromLatin1("m" ) ), - minute_lower( DateTimeScaleFormatter::Second, QString::fromLatin1("s" ) ), - timelineOptions(nullptr) + minute_lower( DateTimeScaleFormatter::Second, QString::fromLatin1("s" ) ) { } ~Private() { delete lower; delete upper; } qreal dateTimeToChartX( const QDateTime& dt ) const; QDateTime chartXtoDateTime( qreal x ) const; int tabHeight( const QString& txt, QWidget* widget = nullptr ) const; - - void drawTimeline(QPainter* painter, const QRectF& rect); - void getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper); class DateTextFormatter { public: virtual ~DateTextFormatter() {} virtual QString format( const QDateTime& dt ) = 0; virtual QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) = 0; }; /*! * We need this because we have a header type for a year, but no such scale. */ enum HeaderType { HeaderHour, HeaderDay, HeaderWeek, HeaderMonth, HeaderYear }; HeaderType headerTypeForScale( DateTimeGrid::Scale scale ); void paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget, HeaderType headerType, DateTextFormatter *formatter ); void paintVerticalLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, QWidget* widget, HeaderType headerType ); void paintVerticalUserDefinedLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, const DateTimeScaleFormatter* formatter, QWidget* widget ); Qt::PenStyle gridLinePenStyle( QDateTime dt, HeaderType headerType ) const; QDateTime adjustDateTimeForHeader( QDateTime dt, HeaderType headerType ) const; QDateTime startDateTime; QDateTime endDateTime; qreal dayWidth; Scale scale; Qt::DayOfWeek weekStart; QSet freeDays; bool rowSeparators; QBrush noInformationBrush; QBrush freeDaysBrush; DateTimeScaleFormatter* upper; DateTimeScaleFormatter* lower; DateTimeScaleFormatter year_upper; DateTimeScaleFormatter year_lower; DateTimeScaleFormatter month_upper; DateTimeScaleFormatter month_lower; DateTimeScaleFormatter week_upper; DateTimeScaleFormatter week_lower; DateTimeScaleFormatter day_upper; DateTimeScaleFormatter day_lower; DateTimeScaleFormatter hour_upper; DateTimeScaleFormatter hour_lower; DateTimeScaleFormatter minute_upper; DateTimeScaleFormatter minute_lower; - - QDateTime timelineTime; - DateTimeGrid::TimelineOptions timelineOptions; - QPen timelinePen; }; inline DateTimeGrid::DateTimeGrid( DateTimeGrid::Private* d ) : AbstractGrid( d ) {} inline DateTimeGrid::Private* DateTimeGrid::d_func() { return static_cast( AbstractGrid::d_func() ); } inline const DateTimeGrid::Private* DateTimeGrid::d_func() const { return static_cast( AbstractGrid::d_func() ); } } #endif /* KGANTTDATETIMEGRID_P_H */