diff --git a/src/libs/models/CMakeLists.txt b/src/libs/models/CMakeLists.txt index f9221071..f627d87e 100644 --- a/src/libs/models/CMakeLists.txt +++ b/src/libs/models/CMakeLists.txt @@ -1,72 +1,73 @@ if(BUILD_TESTING) add_subdirectory( tests ) endif() include_directories( ${PLANKERNEL_INCLUDES} ${PLANWIDGETS_INCLUDES} ${PLANODF_INCLUDES} ${PLANKUNDO2_INCLUDES} ) if (KF5Contacts_FOUND) add_definitions(-DPLAN_KCONTACTS_FOUND) endif () ########### KPlato data models library ############### set(planmodels_LIB_SRCS reportgenerator/ReportGenerator.cpp reportgenerator/ReportGeneratorOdt.cpp kptcommonstrings.cpp kpttreecombobox.cpp kcalendar/kdatetable.cpp kcalendar/kdatepicker.cpp kptnodechartmodel.cpp kptflatproxymodel.cpp kptrelationmodel.cpp kptworkpackagemodel.cpp kptdocumentmodel.cpp kptitemmodelbase.cpp kptnodeitemmodel.cpp kptdurationspinbox.cpp kpttaskstatusmodel.cpp kptresourcemodel.cpp kptcalendarmodel.cpp kptschedulemodel.cpp kptaccountsmodel.cpp kptpertcpmmodel.cpp kptresourceappointmentsmodel.cpp kptresourceallocationmodel.cpp kpttaskcompletedelegate.cpp ) add_library(planmodels SHARED ${planmodels_LIB_SRCS}) generate_export_header(planmodels) target_link_libraries(planmodels PUBLIC plankernel planodf KChart KGantt KF5::KIOWidgets PRIVATE + Qt5::Gui KF5::Notifications KF5::TextWidgets ) if(KF5Contacts_FOUND) target_link_libraries(planmodels PRIVATE KF5::Contacts) endif() set_target_properties(planmodels PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS planmodels ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/libs/models/kptitemmodelbase.cpp b/src/libs/models/kptitemmodelbase.cpp index d816cd1d..9ba1184e 100644 --- a/src/libs/models/kptitemmodelbase.cpp +++ b/src/libs/models/kptitemmodelbase.cpp @@ -1,742 +1,835 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2007, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptitemmodelbase.h" #include "kptproject.h" #include "kptschedule.h" #include "kptdurationspinbox.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "kptdebug.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 namespace KPlato { //-------------------------------------- bool ItemDelegate::eventFilter(QObject *object, QEvent *event) { QWidget *editor = ::qobject_cast(object); if (!editor) { return false; } m_lastHint = Delegate::NoHint; if (event->type() == QEvent::KeyPress) { QKeyEvent *e = static_cast(event); if ( e->modifiers() & Qt::AltModifier && e->modifiers() & Qt::ControlModifier ) { switch ( e->key() ) { case Qt::Key_Left: m_lastHint = Delegate::EditLeftItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Right: m_lastHint = Delegate::EditRightItem; emit commitData(editor); emit closeEditor( editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Down: m_lastHint = Delegate::EditDownItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; case Qt::Key_Up: m_lastHint = Delegate::EditUpItem; emit commitData(editor); emit closeEditor(editor, QAbstractItemDelegate::NoHint ); return true; default: break; } } } return QStyledItemDelegate::eventFilter( object, event ); } QSize ItemDelegate::sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const { // 18 is a bit arbitrary, it gives (most?) editors a usable size QSize s = QStyledItemDelegate::sizeHint( option, index ); return QSize( s.width(), qMax( s.height(), 18 ) ); } //---------------------- CheckStateItemDelegate::CheckStateItemDelegate( QObject *parent ) : ItemDelegate( parent ) { } bool CheckStateItemDelegate::editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index ) { Q_ASSERT(event); Q_ASSERT(model); debugPlan; Qt::ItemFlags flags = model->flags(index); if ( ! ( option.state & QStyle::State_Enabled ) || ! ( flags & Qt::ItemIsEnabled ) ) { return false; } // make sure that we have a check state QVariant value = index.data( Qt::EditRole ); if ( ! value.isValid() ) { return false; } QStyle *style = QApplication::style(); // make sure that we have the right event type if ( ( event->type() == QEvent::MouseButtonRelease ) || ( event->type() == QEvent::MouseButtonDblClick ) || ( event->type() == QEvent::MouseButtonPress ) ) { QStyleOptionViewItem viewOpt( option ); initStyleOption( &viewOpt, index ); QRect checkRect = style->subElementRect( QStyle::SE_ItemViewItemDecoration, &viewOpt, 0 ); QMouseEvent *me = static_cast( event ); if ( me->button() != Qt::LeftButton || ! checkRect.contains( me->pos() ) ) { return false; } if ( ( event->type() == QEvent::MouseButtonPress ) || ( event->type() == QEvent::MouseButtonDblClick ) ) { return true; } } else if ( event->type() == QEvent::KeyPress ) { if (static_cast(event)->key() != Qt::Key_Space && static_cast(event)->key() != Qt::Key_Select) { return false; } } else { return false; } Qt::CheckState state = ( static_cast( value.toInt() ) == Qt::Checked ? Qt::Unchecked : Qt::Checked ); return model->setData(index, state, Qt::CheckStateRole); } //---------------------- DateTimeCalendarDelegate::DateTimeCalendarDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DateTimeCalendarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QDateTimeEdit *editor = new QDateTimeEdit(parent); editor->setCalendarPopup( true ); editor->installEventFilter(const_cast(this)); return editor; } void DateTimeCalendarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QDateTime value = index.model()->data(index, Qt::EditRole).toDateTime(); QDateTimeEdit *e = static_cast(editor); e->setDateTime( value ); } void DateTimeCalendarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QDateTimeEdit *e = static_cast(editor); model->setData( index, e->dateTime(), Qt::EditRole ); } void DateTimeCalendarDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() 50); editor->setGeometry(r); } //----------------------------- ProgressBarDelegate::ProgressBarDelegate( QObject *parent ) : ItemDelegate( parent ) { } ProgressBarDelegate::~ProgressBarDelegate() { } void ProgressBarDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { QStyle *style; QStyleOptionViewItem opt = option; initStyleOption( &opt, index ); style = opt.widget ? opt.widget->style() : QApplication::style(); style->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter ); if ( !( opt.state & QStyle::State_Editing ) ) { bool ok = false; (void) index.data().toInt(&ok); if ( ok ) { QStyleOptionProgressBar pbOption; pbOption.QStyleOption::operator=( option ); initStyleOptionProgressBar( &pbOption, index ); style->drawControl( QStyle::CE_ProgressBar, &pbOption, painter ); // Draw focus, copied from qt if (opt.state & QStyle::State_HasFocus) { painter->save(); QStyleOptionFocusRect o; o.QStyleOption::operator=( opt ); o.rect = style->subElementRect( QStyle::SE_ItemViewItemFocusRect, &opt, opt.widget ); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = ( opt.state & QStyle::State_Enabled ) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = opt.palette.color( cg, ( opt.state & QStyle::State_Selected ) ? QPalette::Highlight : QPalette::Window ); style->drawPrimitive( QStyle::PE_FrameFocusRect, &o, painter, opt.widget ); //debugPlan<<"Focus"<restore(); } } else { EnumDelegate del; del.paint( painter, option, index ); } } } QSize ProgressBarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const { QStyleOptionViewItem opt = option; // initStyleOption( &opt, index ); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); QStyleOptionProgressBar pbOption; pbOption.QStyleOption::operator=( option ); initStyleOptionProgressBar( &pbOption, index ); return style->sizeFromContents( QStyle::CT_ProgressBar, &pbOption, QSize(), opt.widget ); } void ProgressBarDelegate::initStyleOptionProgressBar( QStyleOptionProgressBar *option, const QModelIndex &index ) const { option->rect.adjust( 0, 1, 0, -1 ); option->minimum = 0; int max = index.data( Role::Maximum ).toInt(); option->maximum = max > option->minimum ? max : option->minimum + 100; option->progress = index.data().toInt(); option->text = QString::number( ( option->progress * 100 ) / ( option->maximum - option->minimum ) ) + QLatin1Char( '%' ); option->textAlignment = Qt::AlignCenter; option->textVisible = true; } QWidget *ProgressBarDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const { Slider *slider = new Slider( parent ); slider->setRange( 0, 100 ); slider->setOrientation( Qt::Horizontal ); //debugPlan<minimumSizeHint()<minimumSize(); return slider; } void ProgressBarDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const { QSlider *slider = static_cast( editor ); slider->setValue( index.data( Qt::EditRole ).toInt() ); } void ProgressBarDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { QSlider *slider = static_cast( editor ); model->setData( index, slider->value() ); } void ProgressBarDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & ) const { editor->setGeometry( option.rect ); //debugPlan<minimumSizeHint()<minimumSize()<geometry()<size(); } Slider::Slider( QWidget *parent ) : QSlider( parent ) { connect( this, &QAbstractSlider::valueChanged, this, &Slider::updateTip ); } void Slider::updateTip( int value ) { QPoint p; p.setY( height() / 2 ); p.setX( style()->sliderPositionFromValue ( minimum(), maximum(), value, width() ) ); QString text = QString::number( value ) + QLatin1Char( '%' ); QToolTip::showText( mapToGlobal( p ), text, this ); } //-------------------------------------- // Hmmm, a bit hacky, but this makes it possible to use index specific editors... SelectorDelegate::SelectorDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *SelectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index ) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QComboBox *editor = new KComboBox(parent); editor->installEventFilter(const_cast(this)); return editor; } case Delegate::TimeEditor: { QTimeEdit *editor = new QTimeEdit(parent); editor->installEventFilter(const_cast(this)); return editor; } } return 0; // FIXME: What to do? } void SelectorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QStringList lst = index.model()->data( index, Role::EnumList ).toStringList(); int value = index.model()->data(index, Role::EnumListValue).toInt(); QComboBox *box = static_cast(editor); box->addItems( lst ); box->setCurrentIndex( value ); return; } case Delegate::TimeEditor: QTime value = index.model()->data(index, Qt::EditRole).toTime(); QTimeEdit *e = static_cast(editor); e->setMinimumTime( index.model()->data( index, Role::Minimum ).toTime() ); e->setMaximumTime( index.model()->data( index, Role::Maximum ).toTime() ); e->setTime( value ); return; } } void SelectorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { switch ( index.model()->data( index, Role::EditorType ).toInt() ) { case Delegate::EnumEditor: { QComboBox *box = static_cast(editor); int value = box->currentIndex(); model->setData( index, value, Qt::EditRole ); return; } case Delegate::TimeEditor: { QTimeEdit *e = static_cast(editor); model->setData( index, e->time(), Qt::EditRole ); return; } } } void SelectorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } EnumDelegate::EnumDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QComboBox *editor = new KComboBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void EnumDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QStringList lst = index.model()->data( index, Role::EnumList ).toStringList(); int value = index.model()->data(index, Role::EnumListValue).toInt(); QComboBox *box = static_cast(editor); box->addItems( lst ); box->setCurrentIndex( value ); } void EnumDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *box = static_cast(editor); int value = box->currentIndex(); model->setData( index, value, Qt::EditRole ); } void EnumDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() 50); editor->setGeometry(r); } //--------------------------- RequieredResourceDelegate::RequieredResourceDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *RequieredResourceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { if ( index.data( Qt::CheckStateRole ).toInt() == Qt::Unchecked ) { return 0; } TreeComboBox *editor = new TreeComboBox(parent); editor->installEventFilter(const_cast(this)); ResourceItemSFModel *m = new ResourceItemSFModel( editor ); editor->setModel( m ); return editor; } void RequieredResourceDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); ResourceItemSFModel *pm = static_cast( box->model() ); ResourceItemModel *rm = qobject_cast( pm->sourceModel() ); Q_ASSERT( rm ); const ResourceAllocationItemModel *model = qobject_cast( index.model() ); Q_ASSERT( model ); rm->setProject( model->project() ); pm->addFilteredResource( model->resource( index ) ); QItemSelectionModel *sm = box->view()->selectionModel(); sm->clearSelection(); foreach ( const Resource *r, model->required( index ) ) { QModelIndex i = pm->mapFromSource( rm->index( r ) ); sm->select( i, QItemSelectionModel::Select | QItemSelectionModel::Rows ); } box->setCurrentIndexes( sm->selectedRows() ); box->view()->expandAll(); } void RequieredResourceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { TreeComboBox *box = static_cast(editor); QAbstractProxyModel *pm = static_cast( box->model() ); ResourceItemModel *rm = qobject_cast( pm->sourceModel() ); QList lst; foreach ( const QModelIndex &i, box->currentIndexes() ) { lst << rm->resource( pm->mapToSource( i ) ); } ResourceAllocationItemModel *mdl = qobject_cast( model ); Q_ASSERT( mdl ); mdl->setRequired( index, lst ); } void RequieredResourceDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; r.setWidth( qMax( 100, r.width() ) ); editor->setGeometry(r); } //------------------------------- DurationSpinBoxDelegate::DurationSpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DurationSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { DurationSpinBox *editor = new DurationSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void DurationSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { DurationSpinBox *dsb = static_cast(editor); // dsb->setScales( index.model()->data( index, Role::DurationScales ) ); dsb->setMinimumUnit( (Duration::Unit)(index.data( Role::Minimum ).toInt()) ); dsb->setMaximumUnit( (Duration::Unit)(index.data( Role::Maximum ).toInt()) ); dsb->setUnit( (Duration::Unit)( index.model()->data( index, Role::DurationUnit ).toInt() ) ); dsb->setValue( index.model()->data( index, Qt::EditRole ).toDouble() ); } void DurationSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { DurationSpinBox *dsb = static_cast(editor); QVariantList lst; lst << QVariant( dsb->value() ) << QVariant( (int)( dsb->unit() ) ); model->setData( index, QVariant( lst ), Qt::EditRole ); } void DurationSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() + 50); editor->setGeometry(r); } //--------------------------- SpinBoxDelegate::SpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QSpinBox *editor = new QSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.model()->data(index, Qt::EditRole).toInt(); int min = index.model()->data(index, Role::Minimum).toInt(); int max = index.model()->data(index, Role::Maximum).toInt(); QSpinBox *box = static_cast(editor); box->setRange( min, max ); box->setValue( value ); } void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *box = static_cast(editor); model->setData( index, box->value(), Qt::EditRole ); } void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { debugPlan<sizeHint(); QRect r = option.rect; //r.setHeight(r.height() + 50); editor->setGeometry(r); } //--------------------------- DoubleSpinBoxDelegate::DoubleSpinBoxDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *DoubleSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QDoubleSpinBox *editor = new QDoubleSpinBox(parent); editor->installEventFilter(const_cast(this)); return editor; } void DoubleSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { double value = index.model()->data(index, Qt::EditRole).toDouble(); double min = 0.0;//index.model()->data(index, Role::Minimum).toInt(); double max = 24.0;//index.model()->data(index, Role::Maximum).toInt(); QDoubleSpinBox *box = static_cast(editor); box->setDecimals( 1 ); box->setRange( min, max ); box->setValue( value ); box->selectAll(); } void DoubleSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QDoubleSpinBox *box = static_cast(editor); model->setData( index, box->value(), Qt::EditRole ); } void DoubleSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //--------------------------- MoneyDelegate::MoneyDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *MoneyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { KLineEdit *editor = new KLineEdit(parent); //TODO: validator editor->installEventFilter(const_cast(this)); return editor; } void MoneyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); KLineEdit *e = static_cast(editor); e->setText( value ); } void MoneyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { KLineEdit *e = static_cast(editor); model->setData( index, e->text(), Qt::EditRole ); } void MoneyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //--------------------------- TimeDelegate::TimeDelegate( QObject *parent ) : ItemDelegate( parent ) { } QWidget *TimeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QTimeEdit *editor = new QTimeEdit(parent); editor->installEventFilter(const_cast(this)); return editor; } void TimeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QTime value = index.model()->data(index, Qt::EditRole).toTime(); QTimeEdit *e = static_cast(editor); e->setMinimumTime( index.model()->data( index, Role::Minimum ).toTime() ); e->setMaximumTime( index.model()->data( index, Role::Maximum ).toTime() ); e->setTime( value ); } void TimeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QTimeEdit *e = static_cast(editor); model->setData( index, e->time(), Qt::EditRole ); } void TimeDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { QRect r = option.rect; editor->setGeometry(r); } //-------------------------- ItemModelBase::ItemModelBase( QObject *parent ) : QAbstractItemModel( parent ), m_project(0), m_manager( 0 ), m_readWrite( false )//part->isReadWrite() ) { } ItemModelBase::~ItemModelBase() { } void ItemModelBase::setProject( Project *project ) { m_project = project; } void ItemModelBase::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } void ItemModelBase::slotLayoutChanged() { debugPlan; emit layoutAboutToBeChanged(); emit layoutChanged(); } void ItemModelBase::slotLayoutToBeChanged() { debugPlan; emit layoutAboutToBeChanged(); } bool ItemModelBase::dropAllowed( const QModelIndex &index, int, const QMimeData *data ) { if ( flags( index ) & Qt::ItemIsDropEnabled ) { foreach ( const QString &s, data->formats() ) { if ( mimeTypes().contains( s ) ) { return true; } } } return false; } QVariant ItemModelBase::data( const QModelIndex &index, int role ) const { if ( index.isValid() && role == Role::ColumnTag ) { return columnMap().key( index.column() ); } return QVariant(); } QVariant ItemModelBase::headerData( int section, Qt::Orientation orientation, int role ) const { Q_UNUSED(orientation); if ( role == Role::ColumnTag ) { return columnMap().key( section ); } return QVariant(); } bool ItemModelBase::setData( const QModelIndex &index, const QVariant &value, int role ) { Q_UNUSED(index); if ( role == Role::ReadWrite ) { setReadWrite( value.toBool() ); return true; } return false; } void ItemModelBase::projectDeleted() { setProject(0); } +int numColumns( const QModelIndexList &indexes) +{ + int c = 0; + int r = indexes.first().row(); + QModelIndex currentParent = indexes.first().parent(); + for (const QModelIndex &idx : indexes) { + if (idx.row() == r && currentParent == idx.parent()) { + ++c; + } else { + break; + } + } + return c; +} + +int numRows(const QModelIndexList &indexes) +{ + int rows = 1; + int currentrow = indexes.value(0).row(); + QModelIndex currentParent = indexes.value(0).parent(); + for (int i = 1; i < indexes.count(); ++i) { + if (currentParent != indexes.at(i).parent() || currentrow != indexes.at(i).row()) { + ++rows; + currentrow = indexes.at(i).row(); + currentParent = indexes.at(i).parent(); + } + } + return rows; +} + +void ItemModelBase::writeText(QMimeData *m, const QModelIndexList &indexes) const +{ + if (!mimeTypes().contains("text/html") && !mimeTypes().contains("text/plain")) { + return; + } + int cols = numColumns(indexes); + int rows = numRows(indexes); + QTextDocument doc; + QTextCursor cursor(&doc); + cursor.insertTable(rows+1, cols); + QTextTableFormat tableFormat; + QVector v; + for (int i = 0; i < cols; ++i) { + QTextLength l(QTextLength::PercentageLength, 100 / cols); + v << l; + } + tableFormat.setColumnWidthConstraints(v); + cursor.currentTable()->setFormat(tableFormat); + // headers + for (int i = 0; i < cols; ++i) { + cursor.insertText(headerData(indexes.at(i).column(), Qt::Horizontal).toString()); + cursor.movePosition(QTextCursor::NextCell); + } + // data + for (int i = 0; i < indexes.count(); ++i) { + cursor.insertText(indexes.at(i).data().toString()); + cursor.movePosition(QTextCursor::NextCell); + } + if (mimeTypes().contains("text/html")) { + m->setData("text/html", doc.toHtml("utf-8").toUtf8()); + } + if (mimeTypes().contains("text/plain")) { + m->setData("text/plain", doc.toPlainText().toUtf8()); + } +} + +QStringList ItemModelBase::mimeTypes() const +{ + return QStringList() << "text/html" << "text/plain"; +} + +QMimeData *ItemModelBase::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *m = new QMimeData(); + if (indexes.isEmpty() || mimeTypes().isEmpty()) { + debugPlan<<"No indexes or no mimeTypes"; + return m; + } + writeText(m, indexes); + return m; +} + } //namespace KPlato diff --git a/src/libs/models/kptitemmodelbase.h b/src/libs/models/kptitemmodelbase.h index 0c760e1e..aaada3f9 100644 --- a/src/libs/models/kptitemmodelbase.h +++ b/src/libs/models/kptitemmodelbase.h @@ -1,323 +1,329 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2009 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTITEMMODELBASE_H #define KPTITEMMODELBASE_H #include "planmodels_export.h" #include "kptglobal.h" #include "kpttreecombobox.h" #include #include #include +#include #include class KUndo2Command; /// The main namespace namespace KPlato { class Project; class ScheduleManager; /// Namespace for item delegate specific enums namespace Delegate { /// For selector delegate enum EditorType { EnumEditor, TimeEditor }; /// Controls action when editor is closed. See QAbstractItemDelegate::EndEditHint. enum EndEditHint { NoHint = QAbstractItemDelegate::NoHint, EditNextItem = QAbstractItemDelegate::EditNextItem, EditPreviousItem = QAbstractItemDelegate::EditPreviousItem, SubmitModelCache = QAbstractItemDelegate::SubmitModelCache, RevertModelCache = QAbstractItemDelegate::RevertModelCache, EditLeftItem = 100, EditRightItem = 101, EditDownItem = 102, EditUpItem = 103 }; } /// ItemDelegate implements improved control over closeEditor class PLANMODELS_EXPORT ItemDelegate : public QStyledItemDelegate { Q_OBJECT public: /// Constructor explicit ItemDelegate(QObject *parent = 0) : QStyledItemDelegate( parent ), m_lastHint( Delegate::NoHint ) {} /// Extend EndEditHint for movement from edited item to next item to edit Delegate::EndEditHint endEditHint() const { return m_lastHint; } /// Increase the sizehint height a little to give room for editors QSize sizeHint( const QStyleOptionViewItem & option, const QModelIndex & index ) const; protected: /// Implements arrow key navigation bool eventFilter(QObject *object, QEvent *event); /// Draw custom focus // virtual void drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect ) const; private: Delegate::EndEditHint m_lastHint; }; class PLANMODELS_EXPORT CheckStateItemDelegate : public ItemDelegate { Q_OBJECT public: explicit CheckStateItemDelegate(QObject *parent = 0); protected: bool editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index ); }; class PLANMODELS_EXPORT DateTimeCalendarDelegate : public ItemDelegate { Q_OBJECT public: explicit DateTimeCalendarDelegate(QObject *parent = 0); QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; void setEditorData( QWidget *editor, const QModelIndex &index ) const; void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const; void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const; }; class PLANMODELS_EXPORT ProgressBarDelegate : public ItemDelegate { Q_OBJECT public: explicit ProgressBarDelegate(QObject *parent = 0); ~ProgressBarDelegate(); void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const; QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const; QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; void setEditorData( QWidget *editor, const QModelIndex &index ) const; void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const; void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const; protected: void initStyleOptionProgressBar( QStyleOptionProgressBar *option, const QModelIndex &index ) const; }; class Slider : public QSlider { Q_OBJECT public: explicit Slider( QWidget *parent ); private Q_SLOTS: void updateTip( int value ); }; class PLANMODELS_EXPORT SelectorDelegate : public ItemDelegate { Q_OBJECT public: explicit SelectorDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT EnumDelegate : public ItemDelegate { Q_OBJECT public: explicit EnumDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; //------------------------------------ class PLANMODELS_EXPORT RequieredResourceDelegate : public ItemDelegate { Q_OBJECT public: explicit RequieredResourceDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT DurationSpinBoxDelegate : public ItemDelegate { Q_OBJECT public: explicit DurationSpinBoxDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT SpinBoxDelegate : public ItemDelegate { Q_OBJECT public: explicit SpinBoxDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT DoubleSpinBoxDelegate : public ItemDelegate { Q_OBJECT public: explicit DoubleSpinBoxDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT MoneyDelegate : public ItemDelegate { Q_OBJECT public: explicit MoneyDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT TimeDelegate : public ItemDelegate { Q_OBJECT public: explicit TimeDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; class PLANMODELS_EXPORT ItemModelBase : public QAbstractItemModel { Q_OBJECT public: // FIXME: Refactor, This is a copy from protected enum in QAbstractItemView enum DropIndicatorPosition { OnItem, /*QAbstractItemView::OnItem*/ /// The item will be dropped on the index. AboveItem, /*QAbstractItemView::AboveItem*/ /// The item will be dropped above the index. BelowItem, /*QAbstractItemView::BelowItem*/ /// The item will be dropped below the index. OnViewport /*QAbstractItemView::OnViewport*/ /// The item will be dropped onto a region of the viewport with no items if acceptDropsOnView is set. }; explicit ItemModelBase( QObject *parent = 0 ); virtual ~ItemModelBase(); virtual const QMetaEnum columnMap() const { return QMetaEnum(); } Project *project() const { return m_project; } ScheduleManager *scheduleManager() const { return m_manager; } bool isReadWrite() { return m_readWrite; } void setReadOnly( int column, bool ro ) { m_columnROMap[ column ] = ro; } /// Returns true if @p column has been set to ReadOnly. bool isColumnReadOnly( int column ) const { return m_columnROMap.contains( column ) && m_columnROMap[ column ]; } /** * Check if the @p data is allowed to be dropped on @p index, * @p dropIndicatorPosition indicates position relative @p index. * * Base implementation checks flags and mimetypes. */ virtual bool dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ); /// Create the correct delegate for @p column. @p parent is the delegates parent widget. /// If default should be used, return 0. virtual QAbstractItemDelegate *createDelegate( int column, QWidget *parent ) const { Q_UNUSED(column); Q_UNUSED(parent); return 0; } QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; bool setData( const QModelIndex &index, const QVariant &value, int role ); QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; /// Return the sortorder to be used for @p column virtual int sortRole( int /*column*/ ) const { return Qt::DisplayRole; } + QStringList mimeTypes() const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + void writeText(QMimeData *m, const QModelIndexList &indexes) const; + Q_SIGNALS: /// Connect to this signal if your model modifies data using undo commands. void executeCommand( KUndo2Command* ); public Q_SLOTS: virtual void setProject(KPlato::Project *project); virtual void setScheduleManager(KPlato::ScheduleManager *sm); virtual void setReadWrite( bool rw ) { m_readWrite = rw; } /// Reimplement if your model can be refreshed virtual void refresh() {} protected Q_SLOTS: virtual void slotLayoutToBeChanged(); virtual void slotLayoutChanged(); void projectDeleted(); + protected: Project *m_project; ScheduleManager *m_manager; bool m_readWrite; QMap m_columnROMap; }; } // namespace KPlato #endif diff --git a/src/libs/models/kptnodeitemmodel.cpp b/src/libs/models/kptnodeitemmodel.cpp index 1c8dddef..c778f8c8 100644 --- a/src/libs/models/kptnodeitemmodel.cpp +++ b/src/libs/models/kptnodeitemmodel.cpp @@ -1,5215 +1,5217 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2009, 2012 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptnodeitemmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- NodeModel::NodeModel() : QObject(), m_project( 0 ), m_manager( 0 ), m_now( QDate::currentDate() ), m_prec( 1 ) { } const QMetaEnum NodeModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void NodeModel::setProject( Project *project ) { debugPlan<"<"<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if ( node->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return static_cast( node )->completion().isFinished() ? m_project->config().taskFinishedColor() : m_project->config().taskNormalColor(); case Node::Type_Milestone: return static_cast( node )->completion().isFinished() ? m_project->config().milestoneFinishedColor() : m_project->config().milestoneNormalColor(); case Node::Type_Summarytask: return m_project->config().summaryTaskLevelColor( node->level() ); default: break; } break; } } return QVariant(); } QVariant NodeModel::leader( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return node->leader(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::allocation( const Node *node, int role ) const { if ( node->type() == Node::Type_Task ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->requests().requestNameList().join( "," ); case Qt::ToolTipRole: { QMap lst; foreach ( ResourceRequest *rr, node->requests().resourceRequests( false ) ) { QStringList sl; foreach( Resource *r, rr->requiredResources() ) { sl << r->name(); } lst.insert( rr->resource()->name(), sl ); } if ( lst.isEmpty() ) { return xi18nc( "@info:tooltip", "No resources has been allocated" ); } QStringList sl; for ( QMap::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) { if ( it.value().isEmpty() ) { sl << it.key(); } else { sl << xi18nc( "@info:tooltip 1=resource name, 2=list of required resources", "%1 (%2)", it.key(), it.value().join(", ") ); } } if ( sl.count() == 1 ) { return xi18nc( "@info:tooltip 1=resource name", "Allocated resource:%1", sl.first() ); } KLocalizedString ks = kxi18nc( "@info:tooltip 1=list of resources", "Allocated resources:%1"); // Hack to get around ks escaping '<' and '>' QString s = ks.subs(sl.join("#¤#")).toString(); return s.replace("#¤#", "
"); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::description( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); QString s = w.textOrHtml(); int i = s.indexOf( '\n' ); s = s.left( i ); if ( i > 0 ) { s += "..."; } return s; } case Qt::ToolTipRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); if ( w.textOrHtml().isEmpty() ) { return QVariant(); } return node->description(); } case Qt::EditRole: return node->description(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::type( const Node *node, int role ) const { //debugPlan<name()<<", "<typeToString( true ); case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return (int)(Qt::AlignLeft|Qt::AlignVCenter); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::constraint( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: return i18n( "Target times" ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Earliest start and latest finish" ); case Role::EnumList: case Qt::EditRole: case Role::EnumListValue: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return node->constraintToString( true ); case Role::EnumList: return Node::constraintList( true ); case Qt::EditRole: return node->constraint(); case Role::EnumListValue: return (int)node->constraint(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintStartTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::StartNotEarlier: case Node::MustStartOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintEndTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::FinishNotLater: case Node::MustFinishOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::FinishNotLater || c == Node::MustFinishOn || c == Node::FixedInterval ) { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::estimateType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return QString(); case Role::EnumList: return Estimate::typeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->type(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimateCalendar( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return i18n( "None" ); } return QString(); case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->type() == Estimate::Type_Effort ) { return xi18nc( "@info:tooltip", "Not applicable, estimate type is Effort" ); } if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return QVariant(); } return QString(); case Role::EnumList: { QStringList lst; lst << i18n( "None" ); const Node *n = const_cast( node )->projectNode(); if ( n ) { lst += static_cast( n )->calendarNames(); } return lst; } case Qt::EditRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() == 0 ) { return i18n( "None" ); } return node->estimate()->calendar()->name(); } return QString(); case Role::EnumListValue: { if ( node->estimate()->calendar() == 0 ) { return 0; } QStringList lst; const Node *n = const_cast( node )->projectNode(); if ( n ) { lst = static_cast( n )->calendarNames(); } return lst.indexOf( node->estimate()->calendar()->name() ) + 1; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimate( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); if ( node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { s = '(' + s + ')'; } return s; } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Estimated effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Estimated duration: %1", s ); } return s; } break; case Qt::EditRole: return node->estimate()->expectedEstimate(); case Role::DurationUnit: return static_cast( node->estimate()->unit() ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->optimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Optimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Optimistic duration: %1", s ); } return s; } break; case Role::Minimum: return -99; case Role::Maximum: return 0; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->pessimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Pessimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Pessimistic duration: %1", s ); } return s; } break; case Role::Minimum: return 0; case Role::Maximum: return INT_MAX; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::riskType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString( true ); } return QString(); case Role::EnumList: return Estimate::risktypeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->risktype(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::runningAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a ? xi18nc( "@info:tooltip", "Account for resource cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for resource cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->runningAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name() ) : xi18nc( "@info:tooltip", "Account for task startup cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->startupAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->startupCost() ); } break; case Qt::EditRole: return node->startupCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a ? xi18nc( "@info:tooltip", "Account for task shutdown cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for task shutdown cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->shutdownAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->shutdownCost() ); } break; case Qt::EditRole: return node->shutdownCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->startTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<startTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->startTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::endTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->endTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<endTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->endTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::duration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } break; case Qt::EditRole: { return node->duration( id() ).toDouble( Duration::Unit_h ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return QLocale().toString( v, 'f', 2 ); } break; case Qt::EditRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); return node->variance( id(), unit ); } return 0.0; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return xi18nc( "@info:tooltip", "PERT duration variance: %1", QLocale().toString( v ,'f', 2 ) ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceEstimate( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); //debugPlan<name()<<": "<variance( est->unit() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); return xi18nc( "@info:tooltip", "PERT estimate variance: %1", QLocale().toString( v, 'f', 2 ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); return d.toDouble( unit ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->optimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Optimistic estimate: %1", QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pertExpected( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return Estimate::scale( est->pertExpected(), est->unit(), est->scales() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return xi18nc( "@info:tooltip", "PERT expected estimate: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; return d.toDouble( node->estimate()->unit() ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->pessimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Pessimistic estimate: %1", QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::positiveFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->positiveFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::freeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->freeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->freeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->freeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::negativeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->negativeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->startFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->startFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->startFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->finishFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->finishFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->finishFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::assignedResources( const Node *node, int role ) const { if ( node->type() != Node::Type_Task ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->assignedNameList( id() ).join(","); case Qt::ToolTipRole: { QStringList lst = node->assignedNameList( id() ); if ( ! lst.isEmpty() ) { return xi18nc( "@info:tooltip 1=list of resources", "Assigned resources:%1", node->assignedNameList( id() ).join("") ); } break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::completed( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->completion().percentFinished(); case Qt::EditRole: return t->completion().percentFinished(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task is %1% completed", t->completion().percentFinished() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::status( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { return i18n( "Finished late" ); } if ( st & Node::State_FinishedEarly ) { return i18n( "Finished early" ); } return i18n( "Finished" ); } if ( st & Node::State_Running ) { if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Running" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { return i18n( "Started late" ); } if ( st & Node::State_StartedEarly ) { return i18n( "Started early" ); } if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Started" ); } if ( st & Node::State_ReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Not started" ); } return i18n( "Can start" ); } if ( st & Node::State_NotReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Delayed" ); } return i18n( "Cannot start" ); } return i18n( "Not started" ); break; } case Qt::ToolTipRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { Duration d = t->completion().finishTime() - t->endTime( id() ); return xi18nc( "@info:tooltip", "Finished %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_FinishedEarly ) { Duration d = t->endTime( id() ) - t->completion().finishTime(); return xi18nc( "@info:tooltip", "Finished %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Finished" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { Duration d = t->completion().startTime() - t->startTime( id() ); return xi18nc( "@info:tooltip", "Started %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_StartedEarly ) { Duration d = t->startTime( id() ) - t->completion().startTime(); return xi18nc( "@info:tooltip", "Started %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Started" ); } if ( st & Node::State_Running ) { return xi18nc( "@info:tooltip", "Running" ); } if ( st & Node::State_ReadyToStart ) { return xi18nc( "@info:tooltip", "Can start" ); } if ( st & Node::State_NotReadyToStart ) { QStringList names; // TODO: proxy relations foreach ( Relation *r, node->dependParentNodes() ) { switch ( r->type() ) { case Relation::FinishFinish: case Relation::FinishStart: if ( ! static_cast( r->parent() )->completion().isFinished() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; case Relation::StartStart: if ( ! static_cast( r->parent() )->completion().isStarted() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; } } return names.isEmpty() ? xi18nc( "@info:tooltip", "Cannot start" ) : xi18nc( "@info:tooltip 1=list of task names", "Cannot start, waiting for:%1", names.join( "" ) ); } return xi18nc( "@info:tooltip", "Not started" ); break; } case Qt::EditRole: return t->state( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isStarted() ) { return QLocale().toString( t->completion().startTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "Actual start: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isStarted() ) { return t->completion().startTime(); } return QDateTime::currentDateTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isStarted( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isStarted(); case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "The task started at: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not started" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isFinished() ) { return QLocale().toString( t->completion().finishTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "Actual finish: %1", QLocale().toString( t->completion().finishTime(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isFinished() ) { return t->completion().finishTime(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isFinished( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isFinished(); case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "The task finished at: %1", QLocale().toString( t->completion().finishTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not finished" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->plannedEffortTo( m_now, id() ).format(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned effort until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), node->plannedEffortTo( m_now, id() ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->plannedEffortTo( m_now, id() ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->actualEffortTo( m_now ).format(); case Qt::ToolTipRole: //debugPlan<actualEffortTo( m_now ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->actualEffortTo( m_now ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::remainingEffort( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t ) { return t->completion().remainingEffort().format(); } break; } case Qt::ToolTipRole: { const Task *t = dynamic_cast( node ); if ( t ) { return xi18nc( "@info:tooltip", "Remaining effort: %1", t->completion().remainingEffort().toString( Duration::Format_i18nHour ) ); } break; } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return t->completion().remainingEffort().toDouble( Duration::Unit_h ); } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->plannedCostTo( m_now, id() ) ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->plannedCostTo( m_now, id() ) ) ); case Qt::EditRole: return node->plannedCostTo( m_now ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->actualCostTo( id(), m_now ).cost() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->actualCostTo( id(), m_now ).cost() ) ); case Qt::EditRole: return node->actualCostTo( id(), m_now ).cost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::note( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Node *n = const_cast( node ); return static_cast( n )->completion().note(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeSchedulingStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->schedulingStatus( id(), true ).value( 0 ); case Qt::EditRole: return node->schedulingStatus( id(), false ).value( 0 ); case Qt::ToolTipRole: return node->schedulingStatus( id(), true ).join( "\n" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::resourceIsMissing( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceError( id() ); case Qt::ToolTipRole: if ( node->resourceError( id() ) ) { return xi18nc( "@info:tooltip", "Resource allocation is expected when the task estimate type is Effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsOverbooked( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceOverbooked( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceOverbooked( id() ); case Qt::ToolTipRole: if ( node->resourceOverbooked( id() ) ) { return xi18nc( "@info:tooltip", "A resource has been overbooked" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsNotAvailable( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceNotAvailable( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceNotAvailable( id() ); case Qt::ToolTipRole: if ( node->resourceNotAvailable( id() ) ) { return xi18nc( "@info:tooltip", "No resource is available for this task" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingConstraintsError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->constraintError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->constraintError( id() ); case Qt::ToolTipRole: if ( node->constraintError( id() ) ) { return xi18nc( "@info:tooltip", "Failed to comply with a timing constraint" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeIsNotScheduled( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->notScheduled( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->notScheduled( id() ); case Qt::ToolTipRole: if ( node->notScheduled( id() ) ) { return xi18nc( "@info:tooltip", "This task has not been scheduled" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::effortNotMet( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->effortMetError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->effortMetError( id() ); case Qt::ToolTipRole: if ( node->effortMetError( id() ) ) { return xi18nc( "@info:tooltip", "The assigned resources cannot deliver the required estimated effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->schedulingError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->schedulingError( id() ); case Qt::ToolTipRole: if ( node->schedulingError( id() ) ) { return xi18nc( "@info:tooltip", "Scheduling error" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wbsCode( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->wbsCode(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Work breakdown structure code: %1", node->wbsCode() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case SortableRole: return node->wbsCode(true); } return QVariant(); } QVariant NodeModel::nodeLevel( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->level(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task level: %1", node->level() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWS( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ); case Qt::EditRole: return node->bcws( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Scheduled at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ); case Qt::EditRole: return node->bcwp( id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeACWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost(), QString(), 0 ); case Qt::EditRole: return node->acwp( m_now, id() ).cost(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodePerformanceIndex( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ); case Qt::EditRole: return node->schedulePerformanceIndex( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Schedule Performance Index at %1: %2", m_now.toString(), QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::ForegroundRole: return QColor(node->schedulePerformanceIndex( m_now, id() ) < 1.0 ? Qt::red : Qt::black); } return QVariant(); } QVariant NodeModel::nodeIsCritical( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->isCritical( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeInCriticalPath( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->inCriticalPath( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wpOwnerName( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return t->wpOwnerName(); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent to %1 at %2", static_cast( node )->wpOwnerName(), t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received from %1 at %2", static_cast( node )->wpOwnerName(), t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), true ); } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), false ); } case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return QLocale().toString( t->wpTransmitionTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent: %1", t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received: %1", t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::data( const Node *n, int property, int role ) const { QVariant result; switch ( property ) { // Edited by user case NodeName: result = name( n, role ); break; case NodeType: result = type( n, role ); break; case NodeResponsible: result = leader( n, role ); break; case NodeAllocation: result = allocation( n, role ); break; case NodeEstimateType: result = estimateType( n, role ); break; case NodeEstimateCalendar: result = estimateCalendar( n, role ); break; case NodeEstimate: result = estimate( n, role ); break; case NodeOptimisticRatio: result = optimisticRatio( n, role ); break; case NodePessimisticRatio: result = pessimisticRatio( n, role ); break; case NodeRisk: result = riskType( n, role ); break; case NodeConstraint: result = constraint( n, role ); break; case NodeConstraintStart: result = constraintStartTime( n, role ); break; case NodeConstraintEnd: result = constraintEndTime( n, role ); break; case NodeRunningAccount: result = runningAccount( n, role ); break; case NodeStartupAccount: result = startupAccount( n, role ); break; case NodeStartupCost: result = startupCost( n, role ); break; case NodeShutdownAccount: result = shutdownAccount( n, role ); break; case NodeShutdownCost: result = shutdownCost( n, role ); break; case NodeDescription: result = description( n, role ); break; // Based on edited values case NodeExpected: result = pertExpected( n->estimate(), role ); break; case NodeVarianceEstimate: result = varianceEstimate( n->estimate(), role ); break; case NodeOptimistic: result = optimisticEstimate( n->estimate(), role ); break; case NodePessimistic: result = pessimisticEstimate( n->estimate(), role ); break; // After scheduling case NodeStartTime: result = startTime( n, role ); break; case NodeEndTime: result = endTime( n, role ); break; case NodeEarlyStart: result = earlyStart( n, role ); break; case NodeEarlyFinish: result = earlyFinish( n, role ); break; case NodeLateStart: result = lateStart( n, role ); break; case NodeLateFinish: result = lateFinish( n, role ); break; case NodePositiveFloat: result = positiveFloat( n, role ); break; case NodeFreeFloat: result = freeFloat( n, role ); break; case NodeNegativeFloat: result = negativeFloat( n, role ); break; case NodeStartFloat: result = startFloat( n, role ); break; case NodeFinishFloat: result = finishFloat( n, role ); break; case NodeAssignments: result = assignedResources( n, role ); break; // Based on scheduled values case NodeDuration: result = duration( n, role ); break; case NodeVarianceDuration: result = varianceDuration( n, role ); break; case NodeOptimisticDuration: result = optimisticDuration( n, role ); break; case NodePessimisticDuration: result = pessimisticDuration( n, role ); break; // Completion case NodeStatus: result = status( n, role ); break; case NodeCompleted: result = completed( n, role ); break; case NodePlannedEffort: result = plannedEffortTo( n, role ); break; case NodeActualEffort: result = actualEffortTo( n, role ); break; case NodeRemainingEffort: result = remainingEffort( n, role ); break; case NodePlannedCost: result = plannedCostTo( n, role ); break; case NodeActualCost: result = actualCostTo( n, role ); break; case NodeActualStart: result = startedTime( n, role ); break; case NodeStarted: result = isStarted( n, role ); break; case NodeActualFinish: result = finishedTime( n, role ); break; case NodeFinished: result = isFinished( n, role ); break; case NodeStatusNote: result = note( n, role ); break; // Scheduling errors case NodeSchedulingStatus: result = nodeSchedulingStatus( n, role ); break; case NodeNotScheduled: result = nodeIsNotScheduled( n, role ); break; case NodeAssignmentMissing: result = resourceIsMissing( n, role ); break; case NodeResourceOverbooked: result = resourceIsOverbooked( n, role ); break; case NodeResourceUnavailable: result = resourceIsNotAvailable( n, role ); break; case NodeConstraintsError: result = schedulingConstraintsError( n, role ); break; case NodeEffortNotMet: result = effortNotMet( n, role ); break; case NodeSchedulingError: result = schedulingError( n, role ); break; case NodeWBSCode: result = wbsCode( n, role ); break; case NodeLevel: result = nodeLevel( n, role ); break; // Performance case NodeBCWS: result = nodeBCWS( n, role ); break; case NodeBCWP: result = nodeBCWP( n, role ); break; case NodeACWP: result = nodeACWP( n, role ); break; case NodePerformanceIndex: result = nodePerformanceIndex( n, role ); break; case NodeCritical: result = nodeIsCritical( n, role ); break; case NodeCriticalPath: result = nodeInCriticalPath( n, role ); break; case WPOwnerName: result = wpOwnerName( n, role ); break; case WPTransmitionStatus: result = wpTransmitionStatus( n, role ); break; case WPTransmitionTime: result = wpTransmitionTime( n, role ); break; default: //debugPlan<<"Invalid property number: "<name() ) { return 0; } KUndo2MagicString s = kundo2_i18n( "Modify name" ); switch ( node->type() ) { case Node::Type_Task: s = kundo2_i18n( "Modify task name" ); break; case Node::Type_Milestone: s = kundo2_i18n( "Modify milestone name" ); break; case Node::Type_Summarytask: s = kundo2_i18n( "Modify summarytask name" ); break; case Node::Type_Project: s = kundo2_i18n( "Modify project name" ); break; } return new NodeModifyNameCmd( *node, value.toString(), s ); } } return 0; } KUndo2Command *NodeModel::setLeader( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( value.toString() != node->leader() ) { return new NodeModifyLeaderCmd( *node, value.toString(), kundo2_i18n( "Modify responsible" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setAllocation( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setDescription( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == node->description() ) { return 0; } return new NodeModifyDescriptionCmd( *node, value.toString(), kundo2_i18n( "Modify task description" ) ); } return 0; } KUndo2Command *NodeModel::setType( Node *, const QVariant &, int ) { return 0; } KUndo2Command *NodeModel::setConstraint( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Node::ConstraintType v; QStringList lst = node->constraintList( false ); if ( lst.contains( value.toString() ) ) { v = Node::ConstraintType( lst.indexOf( value.toString() ) ); } else { v = Node::ConstraintType( value.toInt() ); } //debugPlan<constraint() ) { return new NodeModifyConstraintCmd( *node, v, kundo2_i18n( "Modify constraint type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintStartTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintStartTime() ) { return new NodeModifyConstraintStartTimeCmd( *node, dt, kundo2_i18n( "Modify constraint start time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintEndTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintEndTime() ) { return new NodeModifyConstraintEndTimeCmd( *node, dt, kundo2_i18n( "Modify constraint end time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Estimate::Type v; QStringList lst = node->estimate()->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = Estimate::Type( lst.indexOf( value.toString() ) ); } else { v = Estimate::Type( value.toInt() ); } if ( v != node->estimate()->type() ) { return new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v, kundo2_i18n( "Modify estimate type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateCalendar( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Calendar *c = 0; Calendar *old = node->estimate()->calendar(); if ( value.toInt() > 0 ) { QStringList lst = estimateCalendar( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { c = m_project->calendarByName( lst.at( value.toInt() ) ); } } if ( c != old ) { return new ModifyEstimateCalendarCmd( *node, old, c, kundo2_i18n( "Modify estimate calendar" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimate( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { double d; Duration::Unit unit; if ( value.toList().count() == 2 ) { d = value.toList()[0].toDouble(); unit = static_cast( value.toList()[1].toInt() ); } else if ( value.canConvert() ) { bool ok = Duration::valueFromString( value.toString(), d, unit ); if ( ! ok ) { return 0; } } else { return 0; } //debugPlan<"<estimate()->expectedEstimate() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), d ) ); } if ( unit != node->estimate()->unit() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), unit ) ); } if ( cmd ) { return cmd; } break; } default: break; } return 0; } KUndo2Command *NodeModel::setOptimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->optimisticRatio() ) { return new EstimateModifyOptimisticRatioCmd( *node, node->estimate()->optimisticRatio(), value.toInt(), kundo2_i18n( "Modify optimistic estimate" ) ); } break; default: break; } return 0; } KUndo2Command *NodeModel::setPessimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->pessimisticRatio() ) { return new EstimateModifyPessimisticRatioCmd( *node, node->estimate()->pessimisticRatio(), value.toInt(), kundo2_i18n( "Modify pessimistic estimate" ) ); } default: break; } return 0; } KUndo2Command *NodeModel::setRiskType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { int val = 0; QStringList lst = node->estimate()->risktypeToStringList( false ); if ( lst.contains( value.toString() ) ) { val = lst.indexOf( value.toString() ); } else { val = value.toInt(); } if ( val != node->estimate()->risktype() ) { Estimate::Risktype v = Estimate::Risktype( val ); return new EstimateModifyRiskCmd( *node, node->estimate()->risktype(), v, kundo2_i18n( "Modify risk type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setRunningAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = runningAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->runningAccount(); if ( old != a ) { return new NodeModifyRunningAccountCmd( *node, old, a, kundo2_i18n( "Modify running account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setStartupAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = startupAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->startupAccount(); //debugPlan<<(value.toInt())<<";"<<(lst.at( value.toInt()))<<":"<startupCost() ) { return new NodeModifyStartupCostCmd( *node, v, kundo2_i18n( "Modify startup cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = shutdownAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->shutdownAccount(); if ( old != a ) { return new NodeModifyShutdownAccountCmd( *node, old, a, kundo2_i18n( "Modify shutdown account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownCost( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { double v = value.toDouble(); if ( v != node->shutdownCost() ) { return new NodeModifyShutdownCostCmd( *node, v, kundo2_i18n( "Modify shutdown cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setCompletion( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setRemainingEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ); } return 0; } KUndo2Command *NodeModel::setActualEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ); } return 0; } KUndo2Command *NodeModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) ); if ( ! t->completion().isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); } m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } return m; } default: break; } return 0; } KUndo2Command *NodeModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); } return m; } default: break; } return 0; } //---------------------------- NodeItemModel::NodeItemModel( QObject *parent ) : ItemModelBase( parent ), m_node( 0 ), m_projectshown( false ) { setReadOnly( NodeModel::NodeDescription, true ); } NodeItemModel::~NodeItemModel() { } void NodeItemModel::setShowProject( bool on ) { beginResetModel(); m_projectshown = on; endResetModel(); emit projectShownChanged( on ); } void NodeItemModel::slotNodeToBeInserted( Node *parent, int row ) { //debugPlan<name()<<"; "<parentNode()->name()<<"-->"<name(); Q_ASSERT( node->parentNode() == m_node ); endInsertRows(); m_node = 0; emit nodeInserted( node ); } void NodeItemModel::slotNodeToBeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( m_node == 0 ); m_node = node; int row = index( node ).row(); beginRemoveRows( index( node->parentNode() ), row, row ); } void NodeItemModel::slotNodeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( node == m_node ); #ifdef NDEBUG Q_UNUSED(node) #endif endRemoveRows(); m_node = 0; } void NodeItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { //debugPlan<parentNode()->name()<name()<parentNode() ), pos, pos, index( newParent ), newPos ); } void NodeItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); //debugPlan<parentNode()->name()<parentNode()->indexOf( node ); endMoveRows(); } void NodeItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void NodeItemModel::slotProjectCalculated(ScheduleManager *sm) { debugPlan<allNodes() ) { int row = n->parentNode()->indexOf( n ); QModelIndex idx = createIndex( row, NodeModel::NodeWBSCode, n ); emit dataChanged( idx, idx ); } } void NodeItemModel::setProject( Project *project ) { beginResetModel(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &NodeItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &NodeItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &NodeItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &NodeItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &NodeItemModel::slotNodeToBeRemoved); disconnect( m_project, &Project::nodeToBeMoved, this, &NodeItemModel::slotNodeToBeMoved); disconnect( m_project, &Project::nodeMoved, this, &NodeItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &NodeItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &NodeItemModel::slotNodeRemoved); disconnect( m_project, &Project::projectCalculated, this, &NodeItemModel::slotProjectCalculated); } m_project = project; debugPlan<"<isBaselined(); - flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeAllocation: // allocation if ( n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeEstimateType: // estimateType { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimate: // estimate { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeOptimisticRatio: // optimisticRatio case NodeModel::NodePessimisticRatio: // pessimisticRatio { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimateCalendar: { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeRisk: // risktype { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraint: // constraint type if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeConstraintEnd: { // constraint end if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeRunningAccount: // running account if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description flags |= Qt::ItemIsEditable; break; default: break; } Task *t = static_cast( n ); if ( manager() && t->isScheduled( id() ) ) { if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualFinish: if ( t->type() == Node::Type_Milestone ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { switch ( index.column() ) { case NodeModel::NodeActualFinish: case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } } } return flags; } QModelIndex NodeItemModel::parent( const QModelIndex &index ) const { if ( ! index.isValid() ) { return QModelIndex(); } Node *n = node( index ); if ( n == 0 || n == m_project ) { return QModelIndex(); } Node *p = n->parentNode(); if ( p == m_project ) { return m_projectshown ? createIndex( 0, 0, p ) : QModelIndex(); } int row = p->parentNode()->indexOf( p ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, p ); } QModelIndex NodeItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { Q_ASSERT( parent.model() == this ); } //debugPlan<= columnCount() || row < 0 ) { //debugPlan<= p->numChildren() ) { errorPlan<name()<<" row too high"<childNode( row ); QModelIndex idx = createIndex(row, column, n); //debugPlan<parentNode(); if ( par ) { //debugPlan<"<indexOf( node ), column, const_cast(node) ); } if ( m_projectshown && node == m_project ) { return createIndex( 0, column, m_project ); } //debugPlan<( node ); if ( task == 0 ) { return false; } switch ( role ) { case Qt::EditRole: { MacroCommand *cmd = 0; QStringList res = m_project->resourceNameList(); QStringList req = node->requestNameList(); QStringList alloc; foreach ( const QString &s, value.toString().split( QRegExp(" *, *"), QString::SkipEmptyParts ) ) { alloc << s.trimmed(); } // first add all new resources (to "default" group) ResourceGroup *pargr = m_project->groupByName( i18n( "Resources" ) ); foreach ( const QString &s, alloc ) { Resource *r = m_project->resourceByName( s.trimmed() ); if ( r != 0 ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Add resource" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s.trimmed() ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } KUndo2MagicString c = kundo2_i18n( "Modify resource allocations" ); // Handle deleted requests foreach ( const QString &s, req ) { // if a request is not in alloc, it must have been be removed by the user if ( alloc.indexOf( s ) == -1 ) { // remove removed resource request ResourceRequest *r = node->resourceRequest( s ); if ( r ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); cmd->addCommand( new RemoveResourceRequestCmd( r->parent(), r ) ); } } } // Handle new requests QHash groupmap; foreach ( const QString &s, alloc ) { // if an allocation is not in req, it must be added if ( req.indexOf( s ) == -1 ) { ResourceGroup *pargr = 0; Resource *r = m_project->resourceByName( s ); if ( r == 0 ) { // Handle request to non existing resource pargr = m_project->groupByName( i18n( "Resources" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } else { pargr = r->parentGroup(); //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest( pargr ); if ( g == 0 ) { g = groupmap.value( pargr ); } if ( g == 0 ) { // create a group request if ( cmd == 0 ) cmd = new MacroCommand( c ); g = new ResourceGroupRequest( pargr ); cmd->addCommand( new AddResourceGroupRequestCmd( *task, g ) ); groupmap.insert( pargr, g ); //debugPlan<<"add group request:"<addCommand( new AddResourceRequestCmd( g, new ResourceRequest( r, r->units() ) ) ); //debugPlan<<"add request:"<name()<<" group:"<name()<type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); } emit executeCommand( m ); // also adds a new entry if necessary if ( c.entrymode() == Completion::EnterCompleted ) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); cmd->execute(); m->addCommand( cmd ); } return true; } if ( node->type() == Node::Type_Milestone ) { Completion &c = static_cast( node )->completion(); if ( value.toInt() > 0 ) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } QVariant NodeItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( role == Role::Object ) { return n ? QVariant::fromValue( static_cast( n ) ) : QVariant(); } QVariant result; if ( n != 0 ) { result = m_nodemodel.data( n, index.column(), role ); //debugPlan<name()<<": "<numChildren(); } Qt::DropActions NodeItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList NodeItemModel::mimeTypes() const { - return QStringList() << "application/x-vnd.kde.plan.nodeitemmodel.internal" - << "application/x-vnd.kde.plan.resourceitemmodel.internal" - << "application/x-vnd.kde.plan.project" - << "text/uri-list"; + return ItemModelBase::mimeTypes() + << "application/x-vnd.kde.plan.nodeitemmodel.internal" + << "application/x-vnd.kde.plan.resourceitemmodel.internal" + << "application/x-vnd.kde.plan.project" + << "text/uri-list"; } QMimeData *NodeItemModel::mimeData( const QModelIndexList & indexes ) const { - QMimeData *m = new QMimeData(); + QMimeData *m = ItemModelBase::mimeData(indexes); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool NodeItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { debugPlan; if ( m_projectshown && ! index.isValid() ) { return false; } Node *dn = node( index ); // returns project if ! index.isValid() if ( dn == 0 ) { errorPlan<<"no node (or project) to drop on!"; return false; // hmmm } if ( data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal") ) { switch ( dropIndicatorPosition ) { case ItemModelBase::OnItem: if ( index.column() == NodeModel::NodeAllocation ) { debugPlan<<"resource:"<type() == Node::Type_Task); return dn->type() == Node::Type_Task; } else if ( index.column() == NodeModel::NodeResponsible ) { debugPlan<<"resource:"<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal") || data->hasFormat( "application/x-vnd.kde.plan.project" ) || data->hasUrls() ) { switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling, if not project if ( dn == m_project ) { return dropAllowed( dn, data ); } return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } - } else { - debugPlan<<"Unknown mimetype"; } return false; } QList NodeItemModel::resourceList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; debugPlan<<"id"<findResource( id ); if ( r ) { lst << r; } } debugPlan<isBaselined() && on->type() != Node::Type_Summarytask ) { return false; } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( n->type() == Node::Type_Project || on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } } return true; } QList NodeItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList NodeItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool NodeItemModel::dropResourceMimeData( const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *n = node( parent ); debugPlan<name(); if ( parent.column() == NodeModel::NodeResponsible ) { QString s; foreach ( Resource *r, resourceList( stream ) ) { s += r->name(); } if ( ! s.isEmpty() ) { if ( action == Qt::CopyAction && ! n->leader().isEmpty() ) { s += ',' + n->leader(); } KUndo2Command *cmd = m_nodemodel.setLeader( n, s, Qt::EditRole ); if ( cmd ) { emit executeCommand( cmd ); } debugPlan<type() == Node::Type_Task ) { QList lst = resourceList( stream ); if ( action == Qt::CopyAction ) { lst += static_cast( n )->requestedResources(); } KUndo2Command *cmd = createAllocationCommand( static_cast( *n ), lst ); if ( cmd ) { emit executeCommand( cmd ); } return true; } return true; } bool NodeItemModel::dropProjectMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { Node *n = node( parent ); if ( n == 0 ) { n = m_project; } debugPlan<data( "application/x-vnd.kde.plan.project" ) ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project"; return false; } project.generateUniqueNodeIds(); KUndo2Command *cmd = new InsertProjectCmd( project, n, n->childNode( row - 1 ), kundo2_i18nc("1=project or task name", "Insert %1", project.name() ) ); emit executeCommand( cmd ); return true; } bool NodeItemModel::dropUrlMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project from:"<childNode( row - 1 ), kundo2_i18n( "Insert %1", url.fileName() ) ); emit executeCommand( cmd ); return true; } KUndo2Command *NodeItemModel::createAllocationCommand( Task &task, const QList &lst ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Modify resource allocations" ) ); QHash groups; foreach ( Resource *r, lst ) { if ( ! groups.contains( r->parentGroup() ) && task.resourceGroupRequest( r->parentGroup() ) == 0 ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); groups[ r->parentGroup() ] = gr; cmd->addCommand( new AddResourceGroupRequestCmd( task, gr ) ); } } QList resources = task.requestedResources(); foreach ( Resource *r, lst ) { if ( resources.contains( r ) ) { continue; } ResourceGroupRequest *gr = groups.value( r->parentGroup() ); if ( gr == 0 ) { gr = task.resourceGroupRequest( r->parentGroup() ); } if ( gr == 0 ) { errorPlan<<"No group request found, cannot add resource request:"<name(); continue; } cmd->addCommand( new AddResourceRequestCmd( gr, new ResourceRequest( r, 100 ) ) ); } foreach ( Resource *r, resources ) { if ( ! lst.contains( r ) ) { ResourceGroupRequest *gr = task.resourceGroupRequest( r->parentGroup() ); ResourceRequest *rr = task.requests().find( r ); if ( gr && rr ) { cmd->addCommand( new RemoveResourceRequestCmd( gr, rr ) ); } } } if ( cmd->isEmpty() ) { delete cmd; return 0; } return cmd; } bool NodeItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { debugPlan<hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { return dropResourceMimeData( data, action, row, column, parent ); } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; if ( pos >= 0 && n->parentNode() == par && par->indexOf( n ) < pos ) { --pos; } if ( n->parentNode() == par ) { // avoid drop into the same position, QAbstractItemModel does not like it int crow = par->indexOf( n ); if ( ( ( pos == -1 ) && ( crow == par->numChildren() - 1 ) ) || ( pos == crow ) ) { delete cmd; cmd = 0; continue; } } cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } } if ( data->hasFormat( "application/x-vnd.kde.plan.project" ) ) { debugPlan; return dropProjectMimeData( data, action, row, column, parent ); } if ( data->hasUrls() ) { return dropUrlMimeData( data, action, row, column, parent ); } return false; } Node *NodeItemModel::node( const QModelIndex &index ) const { Node *n = m_project; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); Q_ASSERT( n ); } return n; } void NodeItemModel::slotNodeChanged( Node *node ) { if ( node == 0 || ( ! m_projectshown && node->type() == Node::Type_Project ) ) { return; } if ( node->type() == Node::Type_Project ) { emit dataChanged( createIndex( 0, 0, node ), createIndex( 0, columnCount()-1, node ) ); return; } int row = node->parentNode()->findChildNode( node ); Q_ASSERT( row >= 0 ); emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount()-1, node ) ); } QModelIndex NodeItemModel::insertTask( Node *node, Node *after ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Add task" ) ); cmd->addCommand( new TaskAddCmd( m_project, node, after ) ); if ( m_project && node->type() == Node::Type_Task ) { QHash groups; foreach ( Resource *r, m_project->autoAllocateResources() ) { if ( ! groups.contains( r->parentGroup() ) ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); cmd->addCommand( new AddResourceGroupRequestCmd( static_cast(*node), gr ) ); groups[ r->parentGroup() ] = gr; } ResourceRequest *rr = new ResourceRequest( r, 100 ); cmd->addCommand( new AddResourceRequestCmd( groups[ r->parentGroup() ], rr ) ); } } emit executeCommand( cmd ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<<"Inserted: "<name()<<"; "<name(); return QModelIndex(); } QModelIndex NodeItemModel::insertSubtask( Node *node, Node *parent ) { emit executeCommand( new SubtaskAddCmd( m_project, node, parent, kundo2_i18n( "Add sub-task" ) ) ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<parentNode()<<" inserted: "<name()<<"; "<name(); return QModelIndex(); } int NodeItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } debugPlan< lst = parentmap.values(); while ( ! lst.isEmpty() ) delete (int*)(lst.takeFirst()); } int GanttItemModel::rowCount( const QModelIndex &parent ) const { if ( m_showSpecial ) { if (parentmap.values().contains(parent.internalPointer())) { // clazy:exclude=container-anti-pattern return 0; } Node *n = node( parent ); if ( n && n->type() == Node::Type_Task ) { return 5; // the task + early start + late finish ++ } } return NodeItemModel::rowCount( parent ); } QModelIndex GanttItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_showSpecial && parent.isValid() ) { Node *p = node( parent ); if ( p->type() == Node::Type_Task ) { void *v = 0; foreach ( void *i, parentmap.values( p ) ) { // clazy:exclude=container-anti-pattern if ( *( (int*)( i ) ) == row ) { v = i; break; } } if ( v == 0 ) { v = new int( row ); const_cast( this )->parentmap.insertMulti( p, v ); } return createIndex( row, column, v ); } } return NodeItemModel::index( row, column, parent ); } QModelIndex GanttItemModel::parent( const QModelIndex &idx ) const { if ( m_showSpecial ) { QList lst = parentmap.keys( idx.internalPointer() ); if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); return index( lst.first() ); } } return NodeItemModel::parent( idx ); } QVariant GanttItemModel::data( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } QModelIndex idx = index; QList lst; if ( m_showSpecial ) { lst = parentmap.keys( idx.internalPointer() ); } if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); int row = *((int*)(idx.internalPointer())); Node *n = lst.first(); if ( role == SpecialItemTypeRole ) { return row; // 0=task, 1=early start, 2=late finish... } switch ( row ) { case 0: // the task if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { switch ( n->type() ) { case Node::Type_Task: return KGantt::TypeTask; default: break; } } break; case 1: { // early start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyStart( id() ); default: break; } return QVariant(); } case 2: { // late finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateFinish( id() ); default: break; } return QVariant(); } case 3: { // late start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateStart( id() ); default: break; } return QVariant(); } case 4: { // early finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyFinish( id() ); default: break; } return QVariant(); } default: return QVariant(); } idx = createIndex( idx.row(), idx.column(), n ); } else { if ( role == SpecialItemTypeRole ) { return 0; // task of some type } if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { QModelIndex notScheduled = idx.sibling(idx.row(), NodeModel::NodeNotScheduled); if (notScheduled.data(Qt::EditRole).toBool()) { return QVariant(); } QVariant result = NodeItemModel::data( idx, Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Project: return KGantt::TypeSummary; case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return m_showSpecial ? KGantt::TypeMulti : KGantt::TypeTask; } } } return NodeItemModel::data( idx, role ); } //---------------------------- MilestoneItemModel::MilestoneItemModel( QObject *parent ) : ItemModelBase( parent ) { } MilestoneItemModel::~MilestoneItemModel() { } QList MilestoneItemModel::mileStones() const { QList lst; foreach( Node* n, m_nodemap ) { if ( n->type() == Node::Type_Milestone ) { lst << n; } } return lst; } void MilestoneItemModel::slotNodeToBeInserted( Node *parent, int row ) { Q_UNUSED(parent); Q_UNUSED(row); } void MilestoneItemModel::slotNodeInserted( Node *node ) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::slotNodeToBeRemoved( Node *node ) { Q_UNUSED(node); //debugPlan<name(); /* int row = m_nodemap.values().indexOf( node ); if ( row != -1 ) { Q_ASSERT( m_nodemap.contains( node->wbsCode() ) ); Q_ASSERT( m_nodemap.keys().indexOf( node->wbsCode() ) == row ); beginRemoveRows( QModelIndex(), row, row ); m_nodemap.remove( node->wbsCode() ); endRemoveRows(); }*/ } void MilestoneItemModel::slotNodeRemoved( Node *node ) { Q_UNUSED(node); resetModel(); //endRemoveRows(); } void MilestoneItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void MilestoneItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); } void MilestoneItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); resetModel(); } void MilestoneItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &MilestoneItemModel::projectDeleted); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged())); disconnect( m_project, &Project::wbsDefinitionChanged, this, &MilestoneItemModel::slotWbsDefinitionChanged); disconnect( m_project, &Project::nodeChanged, this, &MilestoneItemModel::slotNodeChanged); disconnect( m_project, &Project::nodeToBeAdded, this, &MilestoneItemModel::slotNodeToBeInserted); disconnect( m_project, &Project::nodeToBeRemoved, this, &MilestoneItemModel::slotNodeToBeRemoved); disconnect(m_project, &Project::nodeToBeMoved, this, &MilestoneItemModel::slotNodeToBeMoved); disconnect(m_project, &Project::nodeMoved, this, &MilestoneItemModel::slotNodeMoved); disconnect( m_project, &Project::nodeAdded, this, &MilestoneItemModel::slotNodeInserted); disconnect( m_project, &Project::nodeRemoved, this, &MilestoneItemModel::slotNodeRemoved); } m_project = project; //debugPlan<"<allNodes() ) { m_nodemap.insert( n->wbsCode(true), n ); } } return cnt != m_nodemap.count(); } void MilestoneItemModel::resetModel() { beginResetModel(); resetData(); endResetModel(); } Qt::ItemFlags MilestoneItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( !index.isValid() ) { if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; } return flags; } + flags |= Qt::ItemIsDragEnabled; if ( m_readWrite ) { - flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraint: // constraint type flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraintStart: { // constraint start Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraintEnd: { // constraint end Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost Node *n = node( index ); if ( n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description break; default: flags &= ~Qt::ItemIsEditable; } } return flags; } QModelIndex MilestoneItemModel::parent( const QModelIndex &index ) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex MilestoneItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= m_nodemap.count() ) { //debugPlan<<"No index for"<( node ) ), 0, const_cast(node) ); // clazy:exclude=container-anti-pattern } QVariant MilestoneItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( n != 0 ) { if ( index.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { result = m_nodemodel.data( n, index.column(), Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return KGantt::TypeTask; } return result; } } result = m_nodemodel.data( n, index.column(), role ); return result; } bool MilestoneItemModel::setData( const QModelIndex &index, const QVariant &/*value*/, int role ) { if ( ( flags(index) &Qt::ItemIsEditable ) == 0 || role != Qt::EditRole ) { return false; } // Node *n = node( index ); switch (index.column()) { default: qWarning("data: invalid display value column %d", index.column()); return false; } return false; } QVariant MilestoneItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole || role == Qt::EditRole) { return m_nodemodel.headerData(section, role); } } if ( role == Qt::ToolTipRole ) { return NodeModel::headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *MilestoneItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeEstimateType: return new EnumDelegate( parent ); case NodeModel::NodeEstimateCalendar: return new EnumDelegate( parent ); case NodeModel::NodeEstimate: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeOptimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodePessimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodeRisk: return new EnumDelegate( parent ); case NodeModel::NodeConstraint: return new EnumDelegate( parent ); case NodeModel::NodeRunningAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupCost: return new MoneyDelegate( parent ); case NodeModel::NodeShutdownAccount: return new EnumDelegate( parent ); case NodeModel::NodeShutdownCost: return new MoneyDelegate( parent ); case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int MilestoneItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return m_nodemodel.propertyCount(); } int MilestoneItemModel::rowCount( const QModelIndex &parent ) const { //debugPlan< rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool MilestoneItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { //debugPlan; Node *dn = node( index ); if ( dn == 0 ) { errorPlan<<"no node to drop on!"; return false; // hmmm } switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } return false; } bool MilestoneItemModel::dropAllowed( Node *on, const QMimeData *data ) { if ( !data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal") ) { return false; } if ( on == m_project ) { return true; } QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } return true; } QList MilestoneItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList MilestoneItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool MilestoneItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { //debugPlan<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { return false; } if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } return false; } Node *MilestoneItemModel::node( const QModelIndex &index ) const { Node *n = 0; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); } return n; } void MilestoneItemModel::slotNodeChanged( Node *node ) { //debugPlan<name(); if ( node == 0 ) { return; } beginResetModel(); resetData(); endResetModel(); } void MilestoneItemModel::slotWbsDefinitionChanged() { //debugPlan; if ( m_project == 0 ) { return; } if ( ! m_nodemap.isEmpty() ) { beginResetModel(); resetData(); endResetModel(); } } int MilestoneItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; break; default: break; } return v; } //-------------- NodeSortFilterProxyModel::NodeSortFilterProxyModel( ItemModelBase* model, QObject *parent, bool filterUnscheduled ) : QSortFilterProxyModel( parent ), m_filterUnscheduled( filterUnscheduled ) { setSourceModel( model ); setDynamicSortFilter( true ); } ItemModelBase *NodeSortFilterProxyModel::itemModel() const { return static_cast( sourceModel() ); } void NodeSortFilterProxyModel::setFilterUnscheduled( bool on ) { m_filterUnscheduled = on; invalidateFilter(); } bool NodeSortFilterProxyModel::filterAcceptsRow ( int row, const QModelIndex & parent ) const { //debugPlan<project() == 0 ) { //debugPlan<project(); return false; } if ( m_filterUnscheduled ) { QString s = sourceModel()->data( sourceModel()->index( row, NodeModel::NodeNotScheduled, parent ), Qt::EditRole ).toString(); if ( s == "true" ) { //debugPlan<<"Filtered unscheduled:"<index( row, 0, parent ); return false; } } bool accepted = QSortFilterProxyModel::filterAcceptsRow( row, parent ); //debugPlan<index( row, 0, parent )<<"accepted ="<sortRole(column)); QSortFilterProxyModel::sort(column, order); } //------------------ TaskModuleModel::TaskModuleModel( QObject *parent ) : QAbstractItemModel( parent ) { } void TaskModuleModel::addTaskModule( Project *project, const QUrl &url ) { beginInsertRows( QModelIndex(), m_modules.count(), m_modules.count() ); m_modules << project; m_urls << url; endInsertRows(); } Qt::ItemFlags TaskModuleModel::flags( const QModelIndex &idx ) const { Qt::ItemFlags f = QAbstractItemModel::flags( idx ) | Qt::ItemIsDropEnabled; if ( idx.isValid() ) { f |= Qt::ItemIsDragEnabled; } return f; } int TaskModuleModel::columnCount (const QModelIndex &/*idx*/ ) const { return 1; } int TaskModuleModel::rowCount( const QModelIndex &idx ) const { return idx.isValid() ? 0 : m_modules.count(); } QVariant TaskModuleModel::data( const QModelIndex& idx, int role ) const { if (!idx.isValid() || idx.row() >= m_modules.count()) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: return m_modules.value( idx.row() )->name(); case Qt::ToolTipRole: return m_modules.value( idx.row() )->description(); case Qt::WhatsThisRole: return m_modules.value( idx.row() )->description(); case Qt::UserRole: return m_urls.value(idx.row()); default: break; } return QVariant(); } QVariant TaskModuleModel::headerData( int /*section*/, Qt::Orientation orientation , int role ) const { if ( orientation == Qt::Horizontal ) { switch ( role ) { case Qt::DisplayRole: return xi18nc( "@title:column", "Name" ); default: break; } } return QVariant(); } QModelIndex TaskModuleModel::parent( const QModelIndex& /*idx*/ ) const { return QModelIndex(); } QModelIndex TaskModuleModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column, m_modules.value( row ) ); } QStringList TaskModuleModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan" << "text/uri-list"; } bool TaskModuleModel::dropMimeData( const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &/*parent*/ ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project *project = new Project(); XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( project ); if ( project->load( element, status ) ) { stripProject( project ); addTaskModule( project, url ); if ( emitsignal ) { // FIXME: save destroys the project, so give it a copy (see kptview.cpp) Project p; status.setProject( &p ); p.load( element, status ); emit saveTaskModule( url, &p ); } } else { debugPlan<<"Failed to load project from:"<save( doc ); mime->setData( "application/x-vnd.kde.plan.project", document.toByteArray() ); } } return mime; } void TaskModuleModel::stripProject( Project *project ) const { foreach ( ScheduleManager *sm, project->scheduleManagers() ) { DeleteScheduleManagerCmd c( *project, sm ); } } void TaskModuleModel::loadTaskModules( const QStringList &files ) { debugPlan< Copyright (C) 2011, 2012 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptresourcemodel.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptdebug.h" #include #include #include #include #include #include #include #ifdef PLAN_KCONTACTS_FOUND #include #include #endif namespace KPlato { //-------------------------------------- ResourceModel::ResourceModel( QObject *parent ) : QObject( parent ), m_project( 0 ) { } ResourceModel::~ResourceModel() { } const QMetaEnum ResourceModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void ResourceModel::setProject( Project *project ) { m_project = project; } int ResourceModel::propertyCount() const { return columnMap().keyCount(); } QVariant ResourceModel::name( const Resource *res, int role ) const { //debugPlan<name()<<","<name(); case Qt::ToolTipRole: if (res->isShared()) { return xi18nc("@info:tooltip", "%1 is a Shared resource and can thus be shared with other projects", res->name()); } if ( res->autoAllocate() ) { return xi18nc( "@info:tooltip", "%1:This resource will be automatically allocated to new tasks", res->name() ); } return res->name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if ( res->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; case Qt::CheckStateRole: return res->autoAllocate() ? Qt::Checked : Qt::Unchecked; default: break; } return QVariant(); } QVariant ResourceModel::name( const ResourceGroup *res, int role ) const { //debugPlan<name()<<","<name(); case Qt::ToolTipRole: if (!res->isShared()) { return res->name(); } return xi18nc("@info:tooltip", "%1 is a Shared resource group and can thus be shared with other projects", res->name()); break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::scope( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return res->isShared() ? i18n("Shared") : i18n("Local"); case Qt::ToolTipRole: if (!res->isShared()) { return xi18nc("@info:tooltip", "%1 is a Local resource and can only be used in this project", res->name()); } return xi18nc("@info:tooltip", "%1 is a Shared resource and can thus be shared with other projects", res->name()); case Qt::EditRole: return res->isShared() ? "Shared" : "Local"; case Role::EnumList: return QStringList() << i18n("Local") << i18n("Shared"); case Role::EnumListValue: return res->isShared() ? 1 : 0; case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::scope( const ResourceGroup *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return res->isShared() ? i18n("Shared") : i18n("Local"); case Qt::ToolTipRole: if (!res->isShared()) { return xi18nc("@info:tooltip", "%1 is a Local resource group and can only be used in this project", res->name()); } return xi18nc("@info:tooltip", "%1 is a Shared resource group and can thus be shared with other projects", res->name()); case Qt::EditRole: return res->isShared() ? "Shared" : "Local"; case Role::EnumList: return QStringList() << i18n("Local") << i18n("Shared"); case Role::EnumListValue: return res->isShared() ? 1 : 0; case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::type( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return res->typeToString( true ); case Qt::EditRole: return res->typeToString( false ); case Role::EnumList: return res->typeToStringList( true ); case Role::EnumListValue: return (int)res->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::type( const ResourceGroup *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return res->typeToString( true ); case Qt::EditRole: return res->typeToString( false ); case Role::EnumList: return res->typeToStringList( true ); case Role::EnumListValue: return (int)res->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::initials( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return res->initials(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::email( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return res->email(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::calendar( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( res->type() == Resource::Type_Team ) { return " "; } QString s = i18n( "None" ); Calendar *cal = res->calendar( true ); // don't check for default calendar if ( cal ) { s = cal->name(); } else if ( res->type() == Resource::Type_Work ) { // Do we get a default calendar cal = res->calendar(); if ( cal ) { s = i18nc( "Default (calendar name)", "Default (%1)", cal->name() ); } } return s; } case Qt::ToolTipRole: { if ( res->type() == Resource::Type_Team ) { return xi18nc( "@info:tooltip", "A team resource does not have a calendar" ); } QString s = xi18nc( "@info:tooltip", "No calendar" ); Calendar *cal = res->calendar( true ); // don't check for default calendar if ( cal ) { s = cal->name(); } else if ( res->type() == Resource::Type_Work ) { // Do we get a default calendar cal = res->calendar(); if ( cal ) { s = xi18nc( "@info:tooltip 1=calendar name", "Using default calendar: %1", cal->name() ); } } return s; } case Role::EnumList: { Calendar *cal = m_project->defaultCalendar(); QString s = i18n( "None" ); if ( cal && res->type() == Resource::Type_Work ) { s = i18nc( "Default (calendar name)", "Default (%1)", cal->name() ); } return QStringList() << s << m_project->calendarNames(); } case Qt::EditRole: case Role::EnumListValue: { Calendar *cal = res->calendar( true ); // don't check for default calendar return cal == 0 ? 0 : m_project->calendarNames().indexOf( cal->name() ) + 1; } case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::units( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return res->units(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::availableFrom( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( res->availableFrom(), QLocale::ShortFormat ); case Qt::EditRole: return res->availableFrom(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( res->availableFrom().isValid() ) { return xi18nc( "infor:tooltip", "Available from: %1", QLocale().toString( res->availableFrom(), QLocale::LongFormat ) ); } return xi18nc( "infor:tooltip", "Available from project target start time: %1", QLocale().toString( m_project->constraintStartTime(), QLocale::LongFormat ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::availableUntil( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( res->availableUntil(), QLocale::ShortFormat ); case Qt::EditRole: return res->availableUntil(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( res->availableUntil().isValid() ) { return xi18nc( "infor:tooltip", "Available until: %1", QLocale().toString( res->availableUntil(), QLocale::LongFormat ) ); } return xi18nc( "infor:tooltip", "Available from project target finish time: %1", QLocale().toString( m_project->constraintEndTime(), QLocale::LongFormat ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::normalRate( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( res->normalRate() ); case Qt::EditRole: return res->normalRate(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: return i18n( "Cost per hour, normal time: %1", m_project->locale()->formatMoney( res->normalRate() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::overtimeRate( const Resource *res, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( res->overtimeRate() ); case Qt::EditRole: return res->overtimeRate(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: return i18n( "Cost per hour, overtime: %1", m_project->locale()->formatMoney( res->overtimeRate() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::account( const Resource *resource, int role ) const { switch ( role ) { case Qt::DisplayRole: { Account *a = resource->account(); return a == 0 ? i18n( "None" ) : a->name(); } case Qt::ToolTipRole: { Account *a = resource->account(); return i18n( "Account: %1", (a == 0 ? i18n( "None" ) : a->name() ) ); } case Role::EnumListValue: case Qt::EditRole: { Account *a = resource->account(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceModel::data( const Resource *resource, int property, int role ) const { if ( role == Role::ObjectType ) { return OT_Resource; } QVariant result; if ( resource == 0 ) { return result; } switch ( property ) { case ResourceName: result = name( resource, role ); break; case ResourceScope: result = scope( resource, role ); break; case ResourceType: result = type( resource, role ); break; case ResourceInitials: result = initials( resource, role ); break; case ResourceEmail: result = email( resource, role ); break; case ResourceCalendar: result = calendar( resource, role ); break; case ResourceLimit: result = units( resource, role ); break; case ResourceAvailableFrom: result = availableFrom( resource, role ); break; case ResourceAvailableUntil: result = availableUntil( resource, role ); break; case ResourceNormalRate: result = normalRate( resource, role ); break; case ResourceOvertimeRate: result = overtimeRate( resource, role ); break; case ResourceAccount: result = account( resource, role ); break; default: debugPlan<<"data: invalid display value: property="<name()<<","<(group); beginInsertRows( index( group ), row, row ); } void ResourceItemModel::slotResourceInserted( const Resource *resource ) { //debugPlan<name(); Q_ASSERT( resource->parentGroup() == m_group ); #ifdef NDEBUG Q_UNUSED(resource) #endif endInsertRows(); m_group = 0; emit layoutChanged(); //HACK to make the right view react! Bug in qt? } void ResourceItemModel::slotResourceToBeRemoved( const Resource *resource ) { //debugPlan<name(); Q_ASSERT( m_resource == 0 ); #ifdef NDEBUG Q_UNUSED(resource) #endif m_resource = const_cast(resource); int row = index( resource ).row(); beginRemoveRows( index( resource->parentGroup() ), row, row ); } void ResourceItemModel::slotResourceRemoved( const Resource *resource ) { //debugPlan<name(); Q_ASSERT( resource == m_resource ); #ifdef NDEBUG Q_UNUSED(resource) #endif endRemoveRows(); m_resource = 0; } void ResourceItemModel::slotResourceGroupToBeInserted( const ResourceGroup *group, int row ) { //debugPlan<name(); Q_ASSERT( m_group == 0 ); m_group = const_cast(group); beginInsertRows( QModelIndex(), row, row ); } void ResourceItemModel::slotResourceGroupInserted( const ResourceGroup *group ) { //debugPlan<name(); Q_ASSERT( group == m_group ); #ifdef NDEBUG Q_UNUSED(group) #endif endInsertRows(); m_group = 0; } void ResourceItemModel::slotResourceGroupToBeRemoved( const ResourceGroup *group ) { //debugPlan<name(); Q_ASSERT( m_group == 0 ); m_group = const_cast(group); int row = index( group ).row(); beginRemoveRows( QModelIndex(), row, row ); } void ResourceItemModel::slotResourceGroupRemoved( const ResourceGroup *group ) { //debugPlan<name(); Q_ASSERT( group == m_group ); #ifdef NDEBUG Q_UNUSED(group) #endif endRemoveRows(); m_group = 0; } void ResourceItemModel::setProject( Project *project ) { beginResetModel(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted); disconnect( m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged ); disconnect( m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged ); disconnect( m_project, &Project::resourceGroupChanged, this, &ResourceItemModel::slotResourceGroupChanged ); disconnect( m_project, &Project::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeInserted ); disconnect( m_project, &Project::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved ); disconnect( m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeInserted ); disconnect( m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved ); disconnect( m_project, &Project::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupInserted ); disconnect( m_project, &Project::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved ); disconnect( m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceInserted ); disconnect( m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved ); disconnect( m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged ); } m_project = project; if ( m_project ) { connect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted); connect( m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged ); connect( m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged ); connect( m_project, &Project::resourceGroupChanged, this, &ResourceItemModel::slotResourceGroupChanged ); connect( m_project, &Project::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeInserted ); connect( m_project, &Project::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved ); connect( m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeInserted ); connect( m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved ); connect( m_project, &Project::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupInserted ); connect( m_project, &Project::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved ); connect( m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceInserted ); connect( m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved ); connect( m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged ); } m_model.setProject( m_project ); endResetModel(); } Qt::ItemFlags ResourceItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = ItemModelBase::flags( index ); if ( !m_readWrite ) { //debugPlan<<"read only"<( object ( index ) ); if ( r != 0 ) { flags |= Qt::ItemIsDragEnabled; if (r->isShared()) { flags &= ~Qt::ItemIsEditable; if (index.column() == ResourceModel::ResourceName) { flags |= Qt::ItemIsUserCheckable; } return flags; } switch ( index.column() ) { case ResourceModel::ResourceName: flags |= Qt::ItemIsEditable | Qt::ItemIsUserCheckable; break; case ResourceModel::ResourceScope: flags &= ~Qt::ItemIsEditable; break; case ResourceModel::ResourceType: if ( ! r->isBaselined() ) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceAccount: if ( ! r->isBaselined() ) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceNormalRate: if ( ! r->isBaselined() ) { flags |= Qt::ItemIsEditable; } break; case ResourceModel::ResourceOvertimeRate: if ( ! r->isBaselined() ) { flags |= Qt::ItemIsEditable; } break; default: flags |= Qt::ItemIsEditable; } //debugPlan<<"resource"<( object( index ) ); if ( g ) { + flags |= Qt::ItemIsDragEnabled; if (g->isShared()) { flags &= ~Qt::ItemIsEditable; return flags; } flags |= Qt::ItemIsDropEnabled; switch ( index.column() ) { case ResourceModel::ResourceName: flags |= Qt::ItemIsEditable; break; case ResourceModel::ResourceType: flags |= Qt::ItemIsEditable; break; default: flags &= ~Qt::ItemIsEditable; } //debugPlan<<"group"<parentGroup() ) { // only resources have parent int row = m_project->indexOf( r->parentGroup() ); return createIndex( row, 0, r->parentGroup() ); } return QModelIndex(); } QModelIndex ResourceItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) { return QModelIndex(); } if ( ! parent.isValid() ) { if ( row < m_project->numResourceGroups() ) { return createIndex( row, column, m_project->resourceGroupAt( row ) ); } return QModelIndex(); } ResourceGroup *g = group( parent ); if ( g ) { if ( row < g->numResources() ) { return createIndex( row, column, g->resourceAt( row ) ); } return QModelIndex(); } return QModelIndex(); } QModelIndex ResourceItemModel::index( const Resource *resource, int column ) const { if ( m_project == 0 || resource == 0 ) { return QModelIndex(); } Resource *r = const_cast(resource); int row = -1; ResourceGroup *par = r->parentGroup(); if ( par ) { row = par->indexOf( r ); return createIndex( row, column, r ); } return QModelIndex(); } QModelIndex ResourceItemModel::index( const ResourceGroup *group, int column ) const { if ( m_project == 0 || group == 0 ) { return QModelIndex(); } ResourceGroup *g = const_cast(group); int row = m_project->indexOf( g ); return createIndex( row, column, g ); } int ResourceItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return m_model.propertyCount(); } int ResourceItemModel::rowCount( const QModelIndex &parent ) const { if ( m_project == 0 ) { return 0; } if ( ! parent.isValid() ) { return m_project->numResourceGroups(); } ResourceGroup *g = group( parent ); if ( g ) { return g->numResources(); } return 0; } QVariant ResourceItemModel::name( const ResourceGroup *res, int role ) const { //debugPlan<name()<<","<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool ResourceItemModel::setName( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == res->name() ) { return false; } emit executeCommand( new ModifyResourceNameCmd( res, value.toString(), kundo2_i18n( "Modify resource name" ) ) ); return true; case Qt::CheckStateRole: emit executeCommand( new ModifyResourceAutoAllocateCmd( res, value.toBool(), kundo2_i18n( "Modify resource auto allocate" ) ) ); return true; } return false; } bool ResourceItemModel::setName( ResourceGroup *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == res->name() ) { return false; } emit executeCommand( new ModifyResourceGroupNameCmd( res, value.toString(), kundo2_i18n( "Modify resourcegroup name" ) ) ); return true; } return false; } QVariant ResourceItemModel::type( const ResourceGroup *res, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return res->typeToString( true ); case Qt::EditRole: return res->typeToString( false ); case Role::EnumList: return res->typeToStringList( true ); case Role::EnumListValue: return (int)res->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool ResourceItemModel::setType( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Resource::Type v; QStringList lst = res->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = static_cast( lst.indexOf( value.toString() ) ); } else { v = static_cast( value.toInt() ); } if ( v == res->type() ) { return false; } emit executeCommand( new ModifyResourceTypeCmd( res, v, kundo2_i18n( "Modify resource type" ) ) ); return true; } } return false; } bool ResourceItemModel::setType( ResourceGroup *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { ResourceGroup::Type v; QStringList lst = res->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = static_cast( lst.indexOf( value.toString() ) ); } else { v = static_cast( value.toInt() ); } if ( v == res->type() ) { return false; } emit executeCommand( new ModifyResourceGroupTypeCmd( res, v, kundo2_i18n( "Modify resourcegroup type" ) ) ); return true; } } return false; } bool ResourceItemModel::setInitials( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == res->initials() ) { return false; } emit executeCommand( new ModifyResourceInitialsCmd( res, value.toString(), kundo2_i18n( "Modify resource initials" ) ) ); return true; } return false; } bool ResourceItemModel::setEmail( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == res->email() ) { return false; } emit executeCommand( new ModifyResourceEmailCmd( res, value.toString(), kundo2_i18n( "Modify resource email" ) ) ); return true; } return false; } bool ResourceItemModel::setCalendar( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Calendar *c = 0; if ( value.toInt() > 0 ) { QStringList lst = m_model.calendar( res, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { c = m_project->calendarByName( lst.at( value.toInt() ) ); } } if ( c == res->calendar( true ) ) { return false; } emit executeCommand( new ModifyResourceCalendarCmd( res, c, kundo2_i18n( "Modify resource calendar" ) ) ); return true; } } return false; } bool ResourceItemModel::setUnits( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toInt() == res->units() ) { return false; } emit executeCommand( new ModifyResourceUnitsCmd( res, value.toInt(), kundo2_i18n( "Modify resource available units" ) ) ); return true; } return false; } bool ResourceItemModel::setAvailableFrom( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toDateTime() == res->availableFrom() ) { return false; } emit executeCommand( new ModifyResourceAvailableFromCmd( res, value.toDateTime(), kundo2_i18n( "Modify resource available from" ) ) ); return true; } return false; } bool ResourceItemModel::setAvailableUntil( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toDateTime() == res->availableUntil() ) { return false; } emit executeCommand( new ModifyResourceAvailableUntilCmd( res, value.toDateTime(), kundo2_i18n( "Modify resource available until" ) ) ); return true; } return false; } bool ResourceItemModel::setNormalRate( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toDouble() == res->normalRate() ) { return false; } emit executeCommand( new ModifyResourceNormalRateCmd( res, value.toDouble(), kundo2_i18n( "Modify resource normal rate" ) ) ); return true; } return false; } bool ResourceItemModel::setOvertimeRate( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toDouble() == res->overtimeRate() ) { return false; } emit executeCommand( new ModifyResourceOvertimeRateCmd( res, value.toDouble(), kundo2_i18n( "Modify resource overtime rate" ) ) ); return true; } return false; } bool ResourceItemModel::setAccount( Resource *res, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Account *a = 0; if ( value.type() == QVariant::Int ) { QStringList lst = m_model.account( res, Role::EnumList ).toStringList(); if ( value.toInt() >= lst.count() ) { return false; } a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); } else if ( value.type() == QVariant::String ) { a = m_project->accounts().findAccount( value.toString() ); } Account *old = res->account(); if ( old != a ) { emit executeCommand( new ResourceModifyAccountCmd( *res, old, a, kundo2_i18n( "Modify resource account" ) ) ); return true; } } default: break; } return false; } QVariant ResourceItemModel::notUsed( const ResourceGroup *, int role ) const { switch ( role ) { case Qt::DisplayRole: return QString(" "); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::EditRole: case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant ResourceItemModel::data( const QModelIndex &index, int role ) const { QVariant result; QObject *obj = object( index ); if ( obj == 0 ) { return QVariant(); } if ( role == Qt::TextAlignmentRole ) { // use same alignment as in header (headers always horizontal) return headerData( index.column(), Qt::Horizontal, role ); } Resource *r = qobject_cast( obj ); if ( r ) { result = m_model.data( r, index.column(), role ); } else { ResourceGroup *g = qobject_cast( obj ); if ( g ) { result = m_model.data( g, index.column(), role ); } } return result; } bool ResourceItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() ) { return ItemModelBase::setData( index, value, role ); } if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if ((flags( index ) & (Qt::ItemIsEditable | Qt::ItemIsUserCheckable)) == 0) { return false; } QObject *obj = object( index ); Resource *r = qobject_cast( obj ); if ( r ) { switch (index.column()) { case ResourceModel::ResourceName: return setName( r, value, role ); case ResourceModel::ResourceScope: return false; // Not editable case ResourceModel::ResourceType: return setType( r, value, role ); case ResourceModel::ResourceInitials: return setInitials( r, value, role ); case ResourceModel::ResourceEmail: return setEmail( r, value, role ); case ResourceModel::ResourceCalendar: return setCalendar( r, value, role ); case ResourceModel::ResourceLimit: return setUnits( r, value, role ); case ResourceModel::ResourceAvailableFrom: return setAvailableFrom( r, value, role ); case ResourceModel::ResourceAvailableUntil: return setAvailableUntil( r, value, role ); case ResourceModel::ResourceNormalRate: return setNormalRate( r, value, role ); case ResourceModel::ResourceOvertimeRate: return setOvertimeRate( r, value, role ); case ResourceModel::ResourceAccount: return setAccount( r, value, role ); default: qWarning("data: invalid display value column %d", index.column()); return false; } } else { ResourceGroup *g = qobject_cast( obj ); if ( g ) { switch (index.column()) { case ResourceModel::ResourceName: return setName( g, value, role ); case ResourceModel::ResourceScope: return false; // Not editable case ResourceModel::ResourceType: return setType( g, value, role ); default: qWarning("data: invalid display value column %d", index.column()); return false; } } } return false; } QVariant ResourceItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if ( role == Qt::DisplayRole || role == Qt::TextAlignmentRole ) { return m_model.headerData( section, role ); } } if ( role == Qt::ToolTipRole ) { return m_model.headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *ResourceItemModel::createDelegate( int col, QWidget *parent ) const { switch ( col ) { case ResourceModel::ResourceType: return new EnumDelegate( parent ); case ResourceModel::ResourceCalendar: return new EnumDelegate( parent ); case ResourceModel::ResourceAvailableFrom: return new DateTimeCalendarDelegate( parent ); case ResourceModel::ResourceAvailableUntil: return new DateTimeCalendarDelegate( parent ); case ResourceModel::ResourceAccount: return new EnumDelegate( parent ); default: break; } return 0; } QObject *ResourceItemModel::object( const QModelIndex &index ) const { QObject *o = 0; if ( index.isValid() ) { Q_ASSERT( m_project ); //debugPlan<<(void*)index.internalPointer()<resourceGroups()<resourceList(); Q_ASSERT(m_project->resourceGroups().contains(static_cast(index.internalPointer())) || m_project->resourceList().contains(static_cast(index.internalPointer()))); o = static_cast( index.internalPointer() ); Q_ASSERT( o ); } return o; } ResourceGroup *ResourceItemModel::group( const QModelIndex &index ) const { return qobject_cast( object( index ) ); } Resource *ResourceItemModel::resource( const QModelIndex &index ) const { return qobject_cast( object( index ) ); } void ResourceItemModel::slotCalendarChanged( Calendar* ) { foreach ( Resource *r, m_project->resourceList() ) { if ( r->calendar( true ) == 0 ) { slotResourceChanged( r ); } } } void ResourceItemModel::slotResourceChanged( Resource *res ) { ResourceGroup *g = res->parentGroup(); if ( g ) { int row = g->indexOf( res ); emit dataChanged( createIndex( row, 0, res ), createIndex( row, columnCount() - 1, res ) ); return; } } void ResourceItemModel::slotResourceGroupChanged( ResourceGroup *res ) { Project *p = res->project(); if ( p ) { int row = p->resourceGroups().indexOf( res ); emit dataChanged( createIndex( row, 0, res ), createIndex( row, columnCount() - 1, res ) ); } } Qt::DropActions ResourceItemModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool ResourceItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { if ( data->hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach ( Resource *r, resourceList( stream ) ) { if (r->isShared()) { return false; } } } //debugPlan<( object( index ) ); // Allow only on group default: break; } return false; } QStringList ResourceItemModel::mimeTypes() const { - return QStringList() + return ItemModelBase::mimeTypes() #ifdef PLAN_KDEPIMLIBS_FOUND << "text/x-vcard" << "text/directory" << "text/uri-list" #endif << "application/x-vnd.kde.plan.resourceitemmodel.internal"; } void ResourceItemModel::slotDataArrived( KIO::Job *job, const QByteArray &data ) { if ( m_dropDataMap.contains( job ) ) { m_dropDataMap[ job ].data += data; } } void ResourceItemModel::slotJobFinished( KJob *job ) { if ( job->error() || ! m_dropDataMap.contains( job ) ) { debugPlan<<(job->error() ? "Job error":"Error: no such job"); } else if ( QMimeDatabase().mimeTypeForData( m_dropDataMap[ job ].data ).inherits(QStringLiteral("text/x-vcard") ) ) { ResourceGroup *g = 0; if ( m_dropDataMap[ job ].parent.isValid() ) { g = qobject_cast( object( m_dropDataMap[ job ].parent ) ); } else { g = qobject_cast( object( index( m_dropDataMap[ job ].row, m_dropDataMap[ job ].column, m_dropDataMap[ job ].parent ) ) ); } if ( g == 0 ) { debugPlan<<"No group"<findResource( uid ) ) { r->setId( uid ); } r->setName( lst[a].formattedName() ); r->setEmail( lst[a].preferredEmail() ); m->addCommand( new AddResourceCmd( group, r ) ); } if ( m->isEmpty() ) { delete m; return false; } emit executeCommand( m ); return true; #else Q_UNUSED(group); Q_UNUSED(data); return false; #endif } bool ResourceItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { debugPlan< 0) { return false; } ResourceGroup *g = 0; if ( parent.isValid() ) { g = qobject_cast( object( parent ) ); } else { g = qobject_cast( object( index( row, column, parent ) ) ); } if ( g == 0 ) { debugPlan<<"No group"<formats()<name(); if ( data->hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { debugPlan<data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach ( Resource *r, resourceList( stream ) ) { if ( r->parentGroup() == g ) { continue; } if ( m == 0 ) m = new MacroCommand( KUndo2MagicString() ); m->addCommand( new MoveResourceCmd( g, r ) ); ++i; } if ( m ) { KUndo2MagicString msg = kundo2_i18np( "Move resource", "Move %1 resources", i ); MacroCommand *c = new MacroCommand( msg ); c->addCommand( m ); emit executeCommand( c ); } return true; } if ( action == Qt::CopyAction ) { MacroCommand *m = 0; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); int i = 0; foreach ( Resource *r, resourceList( stream ) ) { Resource *nr = new Resource( r ); if ( m == 0 ) m = new MacroCommand( KUndo2MagicString() ); m->addCommand( new AddResourceCmd( g, nr ) ); ++i; } if ( m ) { KUndo2MagicString msg = kundo2_i18np( "Copy resource", "Copy %1 resources", i ); MacroCommand *c = new MacroCommand( msg ); c->addCommand( m ); emit executeCommand( c ); } return true; } return true; } if ( data->hasFormat( "text/x-vcard" ) || data->hasFormat( "text/directory" ) ) { if ( action != Qt::CopyAction ) { return false; } QString f = data->hasFormat( "text/x-vcard" ) ? "text/x-vcard" : "text/directory"; return createResources( g, data->data( f ) ); } if ( data->hasFormat( "text/uri-list" ) ) { const QList urls = data->urls(); if ( urls.isEmpty() ) { return false; } bool result = false; foreach ( const QUrl &url, urls ) { if ( url.scheme() != "akonadi" ) { debugPlan<setSide( KIO::StatJob::SourceSide ); const bool isUrlReadable = statJob->exec(); if (! isUrlReadable ) { debugPlan<start(); result = true; } return result; } return false; } QList ResourceItemModel::resourceList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Resource *r = m_project->findResource( id ); if ( r ) { lst << r; } } debugPlan< rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<( object( index ) ); if ( r ) { rows << index.row(); stream << r->id(); } else if ( ::qobject_cast( object( index ) ) ) { rows.clear(); break; } } } - if ( rows.isEmpty() ) { - delete m; - return 0; + if (!rows.isEmpty()) { + m->setData("application/x-vnd.kde.plan.resourceitemmodel.internal", encodedData); } - m->setData("application/x-vnd.kde.plan.resourceitemmodel.internal", encodedData); return m; } QModelIndex ResourceItemModel::insertGroup( ResourceGroup *g ) { //debugPlan; emit executeCommand( new AddResourceGroupCmd( m_project, g, kundo2_i18n( "Add resource group" ) ) ); int row = m_project->resourceGroups().indexOf( g ); if ( row != -1 ) { return createIndex( row, 0, g ); } return QModelIndex(); } QModelIndex ResourceItemModel::insertResource( ResourceGroup *g, Resource *r, Resource * /*after*/ ) { //debugPlan; emit executeCommand( new AddResourceCmd( g, r, kundo2_i18n( "Add resource" ) ) ); int row = g->indexOf( r ); if ( row != -1 ) { return createIndex( row, 0, r ); } return QModelIndex(); } int ResourceItemModel::sortRole( int column ) const { switch ( column ) { case ResourceModel::ResourceAvailableFrom: case ResourceModel::ResourceAvailableUntil: return Qt::EditRole; default: break; } return Qt::DisplayRole; } //------------------- ResourceItemSFModel::ResourceItemSFModel( QObject *parent ) : QSortFilterProxyModel( parent ) { setDynamicSortFilter( true ); setSourceModel( new ResourceItemModel( this ) ); } void ResourceItemSFModel::setProject( Project *project ) { static_cast( sourceModel() )->setProject( project ); } Resource *ResourceItemSFModel::resource( const QModelIndex &idx ) const { return static_cast( sourceModel() )->resource( mapToSource( idx ) ); } QModelIndex ResourceItemSFModel::index( Resource *r ) const { return mapFromSource( static_cast( sourceModel() )->index( r ) ); } Qt::ItemFlags ResourceItemSFModel::flags( const QModelIndex & index ) const { Qt::ItemFlags f = QSortFilterProxyModel::flags( index ); if ( index.isValid() && ! parent( index ).isValid() ) { // group, not selectable f &= ~Qt::ItemIsSelectable; } return f; } void ResourceItemSFModel::addFilteredResource( const Resource *r ) { if ( ! m_filteredResources.contains( r ) ) { m_filteredResources << r; } } bool ResourceItemSFModel::filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const { //TODO make this general filter ResourceItemModel *m = static_cast( sourceModel() ); if ( m->index( source_row, ResourceModel::ResourceType, source_parent ).data( Role::EnumListValue ).toInt() == ResourceGroup::Type_Work ) { return false; } QModelIndex idx = m->index( source_row, 0, source_parent ); return ! m_filteredResources.contains( m->resource( idx ) ); } //----------------------- AllocatedResourceItemModel::AllocatedResourceItemModel( QObject *parent ) : QSortFilterProxyModel( parent ), m_task( 0 ) { setDynamicSortFilter( true ); setSourceModel( new ResourceItemModel( this ) ); } int AllocatedResourceItemModel::columnCount( const QModelIndex &idx ) const { Q_UNUSED(idx); return 2; } Project *AllocatedResourceItemModel::project() const { return static_cast( sourceModel() )->project(); } void AllocatedResourceItemModel::setProject( Project *project ) { debugPlan<project()<<"="<project(); if ( p ) { disconnect(p, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged); } static_cast( sourceModel() )->setProject( project ); if ( project ) { connect(project, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged); } debugPlan<rowCount(); } void AllocatedResourceItemModel::reset() { beginResetModel(); endResetModel(); emit expandAll(); emit resizeColumnToContents( 0 ); } void AllocatedResourceItemModel::slotNodeChanged( Node *n ) { debugPlan<<(n==m_task)<name(); if ( n != m_task ) { return; } reset(); } Task *AllocatedResourceItemModel::task() const { return m_task; } void AllocatedResourceItemModel::setTask( Task *task ) { debugPlan<name():""); m_task = task; reset(); debugPlan<rowCount(); } QObject* AllocatedResourceItemModel::object(const QModelIndex& idx) const { return static_cast( sourceModel() )->object( mapToSource( idx ) ); } Resource *AllocatedResourceItemModel::resource( const QModelIndex &idx ) const { return qobject_cast( object( idx ) ); } QModelIndex AllocatedResourceItemModel::index( Resource *r ) const { return mapFromSource( static_cast( sourceModel() )->index( r ) ); } Qt::ItemFlags AllocatedResourceItemModel::flags( const QModelIndex & index ) const { Qt::ItemFlags f = QSortFilterProxyModel::flags( index ); f &= ~Qt::ItemIsUserCheckable; return f; } QVariant AllocatedResourceItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( section == 1 ) { if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { return xi18nc( "@title:column", "Allocation" ); } return QVariant(); } return QSortFilterProxyModel::headerData( section, orientation, role ); } QVariant AllocatedResourceItemModel::allocation( const Resource *res, int role ) const { ResourceRequest *rr = m_task->requests().find( res ); ResourceGroupRequest *gr = m_task->requests().find( res->parentGroup() ); if ( rr == 0 || gr == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: { case Qt::EditRole: // xgettext: no-c-format return i18nc( "%", "%1%",rr->units() ); } case Qt::ToolTipRole: { if ( rr->units() == 0 ) { return xi18nc( "@info:tooltip", "Not allocated" ); } return xi18nc( "@info:tooltip", "%1 allocated out of %2 available", gr->count(), res->parentGroup()->numResources() ); } default: break; } return QVariant(); } QVariant AllocatedResourceItemModel::allocation( const ResourceGroup *res, int role ) const { ResourceGroupRequest *gr = m_task->requests().find( res ); if ( gr == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return QString( "%1 (%2)" ).arg( gr->units() ).arg( gr->count() ); case Qt::ToolTipRole: { QString s1 = i18ncp( "@info:tooltip", "%1 resource requested for dynamic allocation", "%1 resources requested for dynamic allocation", gr->units() ); QString s2 = i18ncp( "@info:tooltip", "%1 resource allocated", "%1 resources allocated", gr->count() ); return xi18nc( "@info:tooltip", "%1%2", s1, s2 ); } case Qt::WhatsThisRole: { return xi18nc( "@info:whatsthis", "Group allocations" "You can allocate a number of resources from a group and let" " the scheduler select from the available resources at the time of scheduling." " These dynamically allocated resources will be in addition to any resource you have allocated specifically." ); } case Role::Minimum: { return 0; } case Role::Maximum: { return res->numResources() - gr->units(); } default: break; } return QVariant(); } QVariant AllocatedResourceItemModel::data(const QModelIndex& idx, int role) const { if ( m_task == 0 || role == Qt::CheckStateRole || role == Qt::DecorationRole ) { return QVariant(); } if ( idx.column() == 1 ) { switch ( role ) { case Qt::TextAlignmentRole: return Qt::AlignLeft; default: { QObject *o = object( idx ); Resource *r = qobject_cast( o ); if ( r ) { return allocation( r, role ); } ResourceGroup *g = qobject_cast( o ); if ( g ) { return allocation( g, role ); } break; } return QVariant(); } } return QSortFilterProxyModel::data( idx, role ); } bool AllocatedResourceItemModel::filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const { if ( m_task == 0 ) { return false; } QModelIndex idx = sourceModel()->index( source_row, 0, source_parent ); if ( ! idx.isValid() ) { return false; } bool result = false; const ResourceRequestCollection &req = m_task->requests(); if ( source_parent.isValid() ) { const Resource *r = static_cast( sourceModel() )->resource( idx ); result = (bool) req.find( r ); } else { const ResourceGroup *g = static_cast( sourceModel() )->group( idx ); ResourceGroupRequest *gr = req.find( g ); result = (bool) gr && ( gr->units() > 0 || gr->count() > 0 ); } debugPlan< This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskstatusmodel.h" #include "PlanMacros.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptitemmodelbase.h" #include "kpttaskcompletedelegate.h" #include "kptcommand.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptnodeitemmodel.h" #include "kptdebug.h" #include #include #include namespace KPlato { TaskStatusItemModel::TaskStatusItemModel( QObject *parent ) : ItemModelBase( parent ), m_period( 7 ), m_periodType( UseCurrentDate ), m_weekday( Qt::Friday ) { m_topNames << i18n( "Not Started" ); m_topTips << i18n( "Tasks that should have been started" ); m_top.append(&m_notstarted ); m_topNames << i18n( "Running" ); m_topTips << i18n( "Tasks that are running" ); m_top.append(&m_running ); m_topNames << i18n( "Finished" ); m_topTips << i18n( "Tasks that have finished during this period" ); m_top.append(&m_finished ); m_topNames << i18n( "Next Period" ); m_topTips << i18n( "Tasks that are scheduled to start next period" ); m_top.append(&m_upcoming ); /* connect( this, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()) ); connect( this, SIGNAL(modelReset()), SLOT(slotReset()) );*/ } TaskStatusItemModel::~TaskStatusItemModel() { } void TaskStatusItemModel::slotAboutToBeReset() { debugPlan; clear(); } void TaskStatusItemModel::slotReset() { debugPlan; refresh(); } void TaskStatusItemModel::slotNodeToBeInserted( Node *, int ) { //debugPlan<name(); clear(); } void TaskStatusItemModel::slotNodeInserted( Node * /*node*/ ) { //debugPlan<getParent->name()<<"-->"<name(); refresh(); } void TaskStatusItemModel::slotNodeToBeRemoved( Node * /*node*/ ) { //debugPlan<name(); clear(); } void TaskStatusItemModel::slotNodeRemoved( Node * /*node*/ ) { //debugPlan<name(); refresh(); } void TaskStatusItemModel::slotNodeToBeMoved(Node *node, int pos, Node *newParent, int newPos) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); clear(); } void TaskStatusItemModel::slotNodeMoved( Node * /*node*/ ) { //debugPlan<name(); refresh(); } void TaskStatusItemModel::setProject( Project *project ) { beginResetModel(); clear(); if ( m_project ) { disconnect(m_project, &Project::aboutToBeDeleted, this, &TaskStatusItemModel::projectDeleted); disconnect(m_project, &Project::localeChanged, this, &TaskStatusItemModel::slotLayoutChanged); disconnect( m_project, &Project::wbsDefinitionChanged, this, &TaskStatusItemModel::slotWbsDefinitionChanged ); disconnect( m_project, &Project::nodeChanged, this, &TaskStatusItemModel::slotNodeChanged ); disconnect( m_project, &Project::nodeToBeAdded, this, &TaskStatusItemModel::slotNodeToBeInserted ); disconnect( m_project, &Project::nodeToBeRemoved, this, &TaskStatusItemModel::slotNodeToBeRemoved ); disconnect(m_project, &Project::nodeToBeMoved, this, &TaskStatusItemModel::slotNodeToBeMoved); disconnect( m_project, &Project::nodeAdded, this, &TaskStatusItemModel::slotNodeInserted ); disconnect( m_project, &Project::nodeRemoved, this, &TaskStatusItemModel::slotNodeRemoved ); disconnect(m_project, &Project::nodeMoved, this, &TaskStatusItemModel::slotNodeMoved); } m_project = project; m_nodemodel.setProject( project ); if ( project ) { connect(m_project, &Project::aboutToBeDeleted, this, &TaskStatusItemModel::projectDeleted); connect(m_project, &Project::localeChanged, this, &TaskStatusItemModel::slotLayoutChanged); connect( m_project, &Project::wbsDefinitionChanged, this, &TaskStatusItemModel::slotWbsDefinitionChanged ); connect( m_project, &Project::nodeChanged, this, &TaskStatusItemModel::slotNodeChanged ); connect( m_project, &Project::nodeToBeAdded, this, &TaskStatusItemModel::slotNodeToBeInserted ); connect( m_project, &Project::nodeToBeRemoved, this, &TaskStatusItemModel::slotNodeToBeRemoved ); connect(m_project, &Project::nodeToBeMoved, this, &TaskStatusItemModel::slotNodeToBeMoved); connect( m_project, &Project::nodeAdded, this, &TaskStatusItemModel::slotNodeInserted ); connect( m_project, &Project::nodeRemoved, this, &TaskStatusItemModel::slotNodeRemoved ); connect(m_project, &Project::nodeMoved, this, &TaskStatusItemModel::slotNodeMoved); } endResetModel(); } void TaskStatusItemModel::setScheduleManager( ScheduleManager *sm ) { beginResetModel(); if (sm == m_nodemodel.manager()) { return; } clear(); if ( m_nodemodel.manager() ) { } m_nodemodel.setManager( sm ); ItemModelBase::setScheduleManager( sm ); if ( sm ) { } endResetModel(); refresh(); } void TaskStatusItemModel::clear() { foreach ( NodeMap *l, m_top ) { int c = l->count(); if ( c > 0 ) { //FIXME: gives error msg: // Can't select indexes from different model or with different parents QModelIndex i = index( l ); debugPlan<clear(); endRemoveRows(); } } } void TaskStatusItemModel::setNow() { switch ( m_periodType ) { case UseWeekday: { QDate date = QDate::currentDate(); int wd = date.dayOfWeek(); date = date.addDays( m_weekday - wd ); if ( wd < m_weekday ) { date = date.addDays( -7 ); } m_nodemodel.setNow( date ); break; } case UseCurrentDate: m_nodemodel.setNow( QDate::currentDate() ); break; default: m_nodemodel.setNow( QDate::currentDate() ); break; } } void TaskStatusItemModel::refresh() { clear(); if ( m_project == 0 ) { return; } m_id = m_nodemodel.id(); if ( m_id == -1 ) { return; } setNow(); const QDate begin = m_nodemodel.now().addDays( -m_period ); const QDate end = m_nodemodel.now().addDays( m_period ); foreach( Node* n, m_project->allNodes() ) { if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) { continue; } Task *task = static_cast( n ); const TaskStatus status = taskStatus(task, begin, end); if (status != TaskUnknownStatus) { m_top.at(status)->insert(task->wbsCode(), task); } } foreach ( NodeMap *l, m_top ) { int c = l->count(); if ( c > 0 ) { debugPlan<isScheduled( m_id ) ) { return flags; } if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) { return flags; } Task *t = static_cast( n ); if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { // task is running switch ( index.column() ) { case NodeModel::NodeActualFinish: case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } return flags; } QModelIndex TaskStatusItemModel::parent( const QModelIndex &index ) const { if ( !index.isValid() ) { return QModelIndex(); } //debugPlan<( index.internalPointer() ) ); if ( row != -1 ) { return QModelIndex(); // top level has no parent } Node *n = node( index ); if ( n == 0 ) { return QModelIndex(); } NodeMap *lst = 0; foreach ( NodeMap *l, m_top ) { if (CONTAINS((*l), n)) { lst = l; break; } } if ( lst == 0 ) { return QModelIndex(); } return createIndex( m_top.indexOf( lst ), 0, lst ); } QModelIndex TaskStatusItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= columnCount() || row < 0 ) { return QModelIndex(); } if ( ! parent.isValid() ) { if ( row >= m_top.count() ) { return QModelIndex(); } return createIndex(row, column, m_top.value( row ) ); } NodeMap *l = list( parent ); if ( l == 0 ) { return QModelIndex(); } if ( row >= rowCount( parent ) ) { warnPlan<<"Row >= rowCount, Qt4.4 asks, so we need to handle it"< &nodes = l->values(); QModelIndex i = createIndex(row, column, nodes.value(row)); Q_ASSERT( i.internalPointer() != 0 ); return i; } QModelIndex TaskStatusItemModel::index( const Node *node ) const { if ( m_project == 0 || node == 0 ) { return QModelIndex(); } foreach( NodeMap *l, m_top ) { const QList &nodes = l->values(); int row = nodes.indexOf( const_cast( node ) ); if ( row != -1 ) { return createIndex( row, 0, const_cast( node ) ); } } return QModelIndex(); } QModelIndex TaskStatusItemModel::index( const NodeMap *lst ) const { if ( m_project == 0 || lst == 0 ) { return QModelIndex(); } NodeMap *l = const_cast( lst ); int row = m_top.indexOf( l ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, l ); } QVariant TaskStatusItemModel::name( int row, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return m_topNames.value( row ); case Qt::ToolTipRole: return m_topTips.value( row ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool TaskStatusItemModel::setCompletion( Node *node, const QVariant &value, int role ) { if ( role != Qt::EditRole ) { return false; } if ( node->type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); // xgettext: no-c-format MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); } emit executeCommand( m ); // also adds a new entry if necessary if ( c.entrymode() == Completion::EnterCompleted ) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); cmd->execute(); m->addCommand( cmd ); } return true; } if ( node->type() == Node::Type_Milestone ) { Completion &c = static_cast( node )->completion(); if ( value.toInt() > 0 ) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } bool TaskStatusItemModel::setRemainingEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); emit executeCommand( new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ) ); return true; } return false; } bool TaskStatusItemModel::setActualEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); emit executeCommand( new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ) ); return true; } return false; } bool TaskStatusItemModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return false; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) ); if ( ! t->completion().isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); } m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } emit executeCommand( m ); return true; } } return false; } bool TaskStatusItemModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return false; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); } emit executeCommand( m ); return true; } } return false; } QVariant TaskStatusItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( ! index.isValid() ) { return result; } if ( role == Qt::TextAlignmentRole ) { return alignment( index.column() ); } Node *n = node( index ); if ( n == 0 ) { switch ( index.column() ) { case NodeModel::NodeName: return name( index.row(), role ); default: break; } return QVariant(); } result = m_nodemodel.data( n, index.column(), role ); if ( role == Qt::DisplayRole ) { switch ( index.column() ) { case NodeModel::NodeActualStart: if ( ! result.isValid() ) { return m_nodemodel.data( n, NodeModel::NodeStatus, role ); } break; } } else if ( role == Qt::EditRole ) { switch ( index.column() ) { case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: if ( ! result.isValid() ) { return QDateTime::currentDateTime(); } break; } } return result; } bool TaskStatusItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { if ( ! index.isValid() ) { return ItemModelBase::setData( index, value, role ); } switch ( index.column() ) { case NodeModel::NodeCompleted: return setCompletion( node( index ), value, role ); case NodeModel::NodeRemainingEffort: return setRemainingEffort( node( index ), value, role ); case NodeModel::NodeActualEffort: return setActualEffort( node( index ), value, role ); case NodeModel::NodeActualStart: return setStartedTime( node( index ), value, role ); case NodeModel::NodeActualFinish: return setFinishedTime( node( index ), value, role ); default: break; } return false; } QVariant TaskStatusItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_nodemodel.headerData( section, role ); } else if ( role == Qt::TextAlignmentRole ) { return alignment( section ); } } if ( role == Qt::ToolTipRole ) { return m_nodemodel.headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QVariant TaskStatusItemModel::alignment( int column ) const { return m_nodemodel.headerData( column, Qt::TextAlignmentRole ); } QAbstractItemDelegate *TaskStatusItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int TaskStatusItemModel::columnCount( const QModelIndex & ) const { return m_nodemodel.propertyCount(); } int TaskStatusItemModel::rowCount( const QModelIndex &parent ) const { if ( ! parent.isValid() ) { //debugPlan<<"top="<count()<count(); } //debugPlan<<"node"< rows; - foreach (const QModelIndex &index, indexes) { - if ( index.isValid() && !rows.contains( index.row() ) ) { - //debugPlan<id(); - } - } - } - m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); - return m; + return ItemModelBase::mimeData(indexes); } bool TaskStatusItemModel::dropAllowed( Node *, const QMimeData * ) { return false; } bool TaskStatusItemModel::dropMimeData( const QMimeData *, Qt::DropAction , int , int , const QModelIndex & ) { return false; } NodeMap *TaskStatusItemModel::list( const QModelIndex &index ) const { if ( index.isValid() ) { Q_ASSERT( index.internalPointer() ); if ( m_top.contains( static_cast( index.internalPointer() ) ) ) { return static_cast( index.internalPointer() ); } } return 0; } Node *TaskStatusItemModel::node( const QModelIndex &index ) const { if ( index.isValid() ) { foreach ( NodeMap *l, m_top ) { const QList &nodes = l->values(); int row = nodes.indexOf( static_cast( index.internalPointer() ) ); if ( row != -1 ) { return static_cast( index.internalPointer() ); } } } return 0; } TaskStatusItemModel::TaskStatus TaskStatusItemModel::taskStatus(const Task *task, const QDate &begin, const QDate &end) { TaskStatus result = TaskUnknownStatus; const Completion &completion = task->completion(); if (completion.isFinished()) { if (completion.finishTime().date() > begin) { result = TaskFinished; } } else if (completion.isStarted()) { result = TaskRunning; } else if (task->startTime(m_id).date() < m_nodemodel.now()) { // should have been started result = TaskNotStarted; } else if (task->startTime(m_id).date() <= end) { // start next period result = TaskUpcoming; } return result; } void TaskStatusItemModel::slotNodeChanged( Node *node ) { debugPlan; if (node == 0 || node->type() == Node::Type_Project || (node->type() != Node::Type_Task && node->type() != Node::Type_Milestone)) { return; } Task *task = static_cast(node); const QDate begin = m_nodemodel.now().addDays( -m_period ); const QDate end = m_nodemodel.now().addDays( m_period ); const TaskStatus status = taskStatus(task, begin, end); int row = -1; if (status != TaskUnknownStatus) { // find the row of the task const QString wbs = node->wbsCode(); // TODO: not enough to just check the result of indexOf? wbs not unique? if (m_top.at(status)->value(wbs) == node ) { row = m_top.at(status)->keys().indexOf(wbs); } } if (row >= 0) { // task in old group, just changed values emit dataChanged(createIndex(row, 0, node), createIndex(row, columnCount() - 1, node)); } else { // task is new or changed groups refresh(); } } void TaskStatusItemModel::slotWbsDefinitionChanged() { debugPlan; foreach ( NodeMap *l, m_top ) { for ( int row = 0; row < l->count(); ++row ) { const QList &nodes = l->values(); emit dataChanged( createIndex( row, NodeModel::NodeWBSCode, nodes.value( row ) ), createIndex( row, NodeModel::NodeWBSCode, nodes.value( row ) ) ); } } } int TaskStatusItemModel::sortRole( int column ) const { switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: return Qt::EditRole; default: break; } return Qt::DisplayRole; } } // namespace KPlato diff --git a/src/libs/ui/kptresourceeditor.cpp b/src/libs/ui/kptresourceeditor.cpp index 6ed39ddb..bfdbb130 100644 --- a/src/libs/ui/kptresourceeditor.cpp +++ b/src/libs/ui/kptresourceeditor.cpp @@ -1,409 +1,414 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2011, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptresourceeditor.h" #include "kptresourcemodel.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptitemviewsettup.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include namespace KPlato { ResourceTreeView::ResourceTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { + setDragPixmap(koIcon("resource-group").pixmap(32)); // header()->setContextMenuPolicy( Qt::CustomContextMenu ); setStretchLastSection( false ); ResourceItemModel *m = new ResourceItemModel( this ); setModel( m ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &ResourceTreeView::slotDropAllowed ); } void ResourceTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { event->ignore(); if ( model()->dropAllowed( index, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } QObject *ResourceTreeView::currentObject() const { return model()->object( selectionModel()->currentIndex() ); } QList ResourceTreeView::selectedObjects() const { QList lst; foreach (const QModelIndex &i, selectionModel()->selectedRows() ) { lst << static_cast( i.internalPointer() ); } return lst; } QList ResourceTreeView::selectedGroups() const { QList gl; foreach ( QObject *o, selectedObjects() ) { ResourceGroup* g = qobject_cast( o ); if ( g ) { gl << g; } } return gl; } QList ResourceTreeView::selectedResources() const { QList rl; foreach ( QObject *o, selectedObjects() ) { Resource* r = qobject_cast( o ); if ( r ) { rl << r; } } return rl; } //----------------------------------- ResourceEditor::ResourceEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { Help::add(this, xi18nc("@info:whatsthis", "Resource Editor" "" "Resources are organized in a Resource Breakdown Structure. " "Resources can be of type Work or Material. " "When assigned to a task, a resource of type Work can affect the duration of the task, while a resource of type Material does not. " "A resource must refer to a Calendar defined in the Work and Vacation Editor." "More..." "", Help::page("Manual/Resource_Editor"))); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new ResourceTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); // m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; QList lst2; lst2 << 0; m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); QList show; for ( int c = 1; c < model()->columnCount(); ++c ) { show << c; } m_view->slaveView()->setDefaultColumns( show ); connect( model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &ResourceEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &ResourceEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &ResourceEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); } void ResourceEditor::updateReadWrite( bool readwrite ) { m_view->setReadWrite( readwrite ); } void ResourceEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void ResourceEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void ResourceEditor::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { //debugPlan<model()->object( index ); ResourceGroup *g = qobject_cast( obj ); if ( g ) { //name = "resourceeditor_group_popup"; } else { Resource *r = qobject_cast( obj ); if ( r && !r->isShared() ) { name = "resourceeditor_resource_popup"; } } } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } Resource *ResourceEditor::currentResource() const { return qobject_cast( m_view->currentObject() ); } ResourceGroup *ResourceEditor::currentResourceGroup() const { return qobject_cast( m_view->currentObject() ); } void ResourceEditor::slotCurrentChanged( const QModelIndex & ) { //debugPlan<project(); QList groupList = m_view->selectedGroups(); bool nogroup = groupList.isEmpty(); bool group = groupList.count() == 1; QList resourceList = m_view->selectedResources(); bool noresource = resourceList.isEmpty(); bool resource = resourceList.count() == 1; bool any = !nogroup || !noresource; actionAddResource->setEnabled( o && ( (group && noresource) || (resource && nogroup) ) ); actionAddGroup->setEnabled( o ); if ( o && any ) { foreach ( ResourceGroup *g, groupList ) { if ( g->isBaselined() ) { o = false; break; } } } if ( o && any ) { foreach ( Resource *r, resourceList ) { if ( r->isBaselined() ) { o = false; break; } } } actionDeleteSelection->setEnabled( o && any ); } void ResourceEditor::setupGui() { QString name = "resourceeditor_edit_list"; actionAddGroup = new QAction(koIcon("resource-group-new"), i18n("Add Resource Group"), this); actionCollection()->addAction("add_group", actionAddGroup ); actionCollection()->setDefaultShortcut(actionAddGroup, Qt::CTRL + Qt::Key_I); connect( actionAddGroup, &QAction::triggered, this, &ResourceEditor::slotAddGroup ); addAction( name, actionAddGroup ); actionAddResource = new QAction(koIcon("list-add-user"), i18n("Add Resource"), this); actionCollection()->addAction("add_resource", actionAddResource ); actionCollection()->setDefaultShortcut(actionAddResource, Qt::CTRL + Qt::SHIFT + Qt::Key_I); connect( actionAddResource, &QAction::triggered, this, &ResourceEditor::slotAddResource ); addAction( name, actionAddResource ); actionDeleteSelection = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->addAction("delete_selection", actionDeleteSelection ); actionCollection()->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); connect( actionDeleteSelection, &QAction::triggered, this, &ResourceEditor::slotDeleteSelection ); addAction( name, actionDeleteSelection ); // Add the context menu actions for the view options connect(m_view->actionSplitView(), &QAction::triggered, this, &ResourceEditor::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void ResourceEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void ResourceEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void ResourceEditor::slotAddResource() { //debugPlan; QList gl = m_view->selectedGroups(); if ( gl.count() > 1 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); ResourceGroup *g = 0; if ( !gl.isEmpty() ) { g = gl.first(); } else { QList rl = m_view->selectedResources(); if ( rl.count() != 1 ) { return; } g = rl.first()->parentGroup(); } if ( g == 0 ) { return; } Resource *r = new Resource(); if ( g->type() == ResourceGroup::Type_Material ) { r->setType( Resource::Type_Material ); } QModelIndex i = m_view->model()->insertResource( g, r ); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->edit( i ); } } void ResourceEditor::slotAddGroup() { //debugPlan; m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); ResourceGroup *g = new ResourceGroup(); QModelIndex i = m_view->model()->insertGroup( g ); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->edit( i ); } } void ResourceEditor::slotDeleteSelection() { QObjectList lst = m_view->selectedObjects(); //debugPlan<selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } } bool ResourceEditor::loadContext( const KoXmlElement &context ) { debugPlan<loadContext( model()->columnMap(), context ); } void ResourceEditor::saveContext( QDomElement &context ) const { debugPlan<saveContext( model()->columnMap(), context ); } KoPrintJob *ResourceEditor::createPrintJob() { return m_view->createPrintJob( this ); } +void ResourceEditor::slotEditCopy() +{ + m_view->editCopy(); +} } // namespace KPlato diff --git a/src/libs/ui/kptresourceeditor.h b/src/libs/ui/kptresourceeditor.h index 670aab98..791edeed 100644 --- a/src/libs/ui/kptresourceeditor.h +++ b/src/libs/ui/kptresourceeditor.h @@ -1,123 +1,124 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTRESOURCEEDITOR_H #define KPTRESOURCEEDITOR_H #include "planui_export.h" #include #include class KoDocument; class QPoint; namespace KPlato { class Project; class Resource; class ResourceGroup; class PLANUI_EXPORT ResourceTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit ResourceTreeView(QWidget *parent); ResourceItemModel *model() const { return static_cast( DoubleTreeViewBase::model() ); } Project *project() const { return model()->project(); } void setProject( Project *project ) { model()->setProject( project ); } QObject *currentObject() const; QList selectedObjects() const; QList selectedGroups() const; QList selectedResources() const; protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); }; class PLANUI_EXPORT ResourceEditor : public ViewBase { Q_OBJECT public: ResourceEditor(KoPart *part, KoDocument *dic, QWidget *parent); void setupGui(); Project *project() const { return m_view->project(); } virtual void setProject( Project *project ); ResourceItemModel *model() const { return m_view->model(); } virtual void updateReadWrite( bool readwrite ); virtual Resource *currentResource() const; virtual ResourceGroup *currentResourceGroup() const; /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); Q_SIGNALS: void addResource(KPlato::ResourceGroup*); void deleteObjectList( const QObjectList& ); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); + void slotEditCopy(); protected Q_SLOTS: virtual void slotOptions(); protected: void updateActionsEnabled( bool on = true ); private Q_SLOTS: void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotSplitView(); void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex& ); void slotEnableActions( bool on ); void slotAddResource(); void slotAddGroup(); void slotDeleteSelection(); private: ResourceTreeView *m_view; QAction *actionAddResource; QAction *actionAddGroup; QAction *actionDeleteSelection; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kpttaskeditor.cpp b/src/libs/ui/kpttaskeditor.cpp index 89c22438..782c52bf 100644 --- a/src/libs/ui/kpttaskeditor.cpp +++ b/src/libs/ui/kpttaskeditor.cpp @@ -1,1712 +1,1728 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskeditor.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptnodeitemmodel.h" #include "kptcommand.h" #include "kptproject.h" #include "kptitemviewsettup.h" #include "kptworkpackagesenddialog.h" #include "kptworkpackagesendpanel.h" #include "kptdatetime.h" #include "kptdebug.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "ResourceAllocationView.h" #include "kpttaskdialog.h" #include "TasksEditController.h" #include "Help.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- TaskEditorItemModel::TaskEditorItemModel( QObject *parent ) : NodeItemModel( parent ) { } Qt::ItemFlags TaskEditorItemModel::flags( const QModelIndex &index ) const { if ( index.column() == NodeModel::NodeType ) { if ( ! m_readWrite || isColumnReadOnly( index.column() ) ) { return QAbstractItemModel::flags( index ); } Node *n = node( index ); bool baselined = n ? n->isBaselined() : false; if ( n && ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { return QAbstractItemModel::flags( index ) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; } return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled; } return NodeItemModel::flags( index ); } QVariant TaskEditorItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && section == NodeModel::NodeType ) { if ( role == Qt::ToolTipRole ) { return xi18nc( "@info:tooltip", "The type of task or the estimate type of the task" ); } else if ( role == Qt::WhatsThisRole ) { return xi18nc( "@info:whatsthis", "

Indicates the type of task or the estimate type of the task.

" "The type can be set to Milestone, Effort or Duration." "If the type is Summary or Project the type is not editable."); } } return NodeItemModel::headerData(section, orientation, role); } QVariant TaskEditorItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return NodeItemModel::data( index, role ); } Node *n = node( index ); if ( n != 0 && index.column() == NodeModel::NodeType ) { return type( n, role ); } return NodeItemModel::data( index, role ); } bool TaskEditorItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Node *n = node( index ); if ( n != 0 && role == Qt::EditRole && index.column() == NodeModel::NodeType ) { return setType( n, value, role ); } return NodeItemModel::setData( index, value, role ); } QVariant TaskEditorItemModel::type( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return node->typeToString( true ); } case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( node->type() == Node::Type_Task ) { return xi18nc( "@info:tooltip", "Task with estimate type: %1", node->estimate()->typeToString( true ) ); } return xi18nc( "@info:tooltip", "Task type: %1", node->typeToString( true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::EnumListValue: { if ( node->type() == Node::Type_Milestone ) { return 0; } if ( node->type() == Node::Type_Task ) { return node->estimate()->type() + 1; } return -1; } case Role::EnumList: { QStringList lst; lst << Node::typeToString( Node::Type_Milestone, true ); lst += Estimate::typeToStringList( true ); return lst; } } return QVariant(); } bool TaskEditorItemModel::setType( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( node->type() == Node::Type_Summarytask ) { return false; } int v = value.toInt(); switch ( v ) { case 0: { // Milestone NamedCommand *cmd = 0; if ( node->constraint() == Node::FixedInterval ) { cmd = new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime(), kundo2_i18n( "Set type to Milestone" ) ); } else { cmd = new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 0.0, kundo2_i18n( "Set type to Milestone" ) ); } emit executeCommand( cmd ); return true; } default: { // Estimate --v; MacroCommand *m = new MacroCommand( kundo2_i18n( "Set type to %1", Estimate::typeToString( (Estimate::Type)v, true ) ) ); m->addCommand( new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v ) ); if ( node->type() == Node::Type_Milestone ) { if ( node->constraint() == Node::FixedInterval ) { m->addCommand( new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime().addDays( 1 ) ) ); } else { m->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), Duration::Unit_d ) ); m->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 1.0 ) ); } } emit executeCommand( m ); return true; } } break; } default: break; } return false; } //-------------------- TaskEditorTreeView::TaskEditorTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { + setDragPixmap(koIcon("view-task").pixmap(32)); TaskEditorItemModel *m = new TaskEditorItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); setItemDelegateForColumn( NodeModel::NodeType, new EnumDelegate( this ) ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &TaskEditorTreeView::slotDropAllowed ); } NodeItemModel *TaskEditorTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void TaskEditorTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } //-------------------- NodeTreeView::NodeTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { + setDragPixmap(koIcon("view-task").pixmap(32)); NodeItemModel *m = new NodeItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &NodeTreeView::slotDropAllowed ); } NodeItemModel *NodeTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void NodeTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } //----------------------------------- TaskEditor::TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ) { debugPlan<<"----------------- Create TaskEditor ----------------------"; QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new TaskEditorTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); debugPlan<actionSplitView(); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; // only display column 0 (NodeName) in left view QList show; show << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; QList lst2; for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } for ( int i = 0; i < show.count(); ++i ) { int sec = m_view->slaveView()->header()->visualIndex( show[ i ] ); //debugPlan<<"move section:"<slaveView()->header()->moveSection( sec, i ); } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << NodeModel::NodeName ); m_view->slaveView()->setDefaultColumns( show ); connect( model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(baseModel(), &NodeItemModel::projectShownChanged, this, &TaskEditor::slotProjectShown); connect(model(), &QAbstractItemModel::rowsMoved, this, &TaskEditor::slotEnableActions); Help::add(this, xi18nc("@info:whatsthis", "Task Editor" "" "The Task Editor is used to create, edit, and delete tasks. " "Tasks are organized into a Work Breakdown Structure (WBS) to any depth." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Editor"))); } void TaskEditor::slotProjectShown( bool on ) { debugPlan<rowCount() > 0 ) { idx = proxyModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } } else if ( baseModel() && baseModel()->rowCount() > 0 ) { idx = baseModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } if ( on && idx.isValid() ) { m_view->masterView()->expand( idx ); } slotEnableActions(); } void TaskEditor::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void TaskEditor::createDockers() { // Add dockers DockWidget *ds = 0; { ds = new DockWidget( this, "Allocations", xi18nc( "@title resource allocations", "Allocations" ) ); QTreeView *x = new QTreeView( ds ); AllocatedResourceItemModel *m1 = new AllocatedResourceItemModel( x ); x->setModel( m1 ); m1->setProject( project() ); // x->setHeaderHidden( true ); x->setSelectionBehavior( QAbstractItemView::SelectRows ); x->setSelectionMode( QAbstractItemView::ExtendedSelection ); x->expandAll(); x->resizeColumnToContents( 0 ); x->setDragDropMode( QAbstractItemView::DragOnly ); x->setDragEnabled ( true ); ds->setWidget( x ); connect(this, &ViewBase::projectChanged, m1, &AllocatedResourceItemModel::setProject); connect(this, &TaskEditor::taskSelected, m1, &AllocatedResourceItemModel::setTask); connect(m1, &AllocatedResourceItemModel::expandAll, x, &QTreeView::expandAll); connect(m1, &AllocatedResourceItemModel::resizeColumnToContents, x, &QTreeView::resizeColumnToContents); addDocker( ds ); } { ds = new DockWidget( this, "Resources", xi18nc( "@title", "Resources" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag resources into the Task Editor" " and drop into the allocations- or responsible column" ) ); ResourceAllocationView *e = new ResourceAllocationView(part(), ds ); ResourceItemModel *m = new ResourceItemModel( e ); e->setModel( m ); m->setProject( project() ); m->setReadWrite( isReadWrite() ); QList show; show << ResourceModel::ResourceName; for ( int i = m->columnCount() - 1; i >= 0; --i ) { e->setColumnHidden( i, ! show.contains( i ) ); } e->setHeaderHidden( true ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::ExtendedSelection ); e->expandAll(); e->resizeColumnToContents( ResourceModel::ResourceName ); e->setDragDropMode( QAbstractItemView::DragOnly ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, e, &ResourceAllocationView::setSelectedTasks); connect(this, SIGNAL(projectChanged(KPlato::Project*)), m, SLOT(setProject(KPlato::Project*))); connect(this, &ViewBase::readWriteChanged, m, &ItemModelBase::setReadWrite); connect(m, &ItemModelBase::executeCommand, part(), &KoDocument::addCommand); addDocker( ds ); } { ds = new DockWidget( this, "Taskmodules", xi18nc( "@title", "Task Modules" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag a task module into the Task Editor to add it to the project" ) ); ds->setLocation( Qt::LeftDockWidgetArea ); ds->setShown(false); // hide by default QTreeView *e = new QTreeView( ds ); QSortFilterProxyModel *sf = new QSortFilterProxyModel(e); TaskModuleModel *m = new TaskModuleModel(sf); sf->setSourceModel(m); e->setModel(sf); e->sortByColumn(0, Qt::AscendingOrder); e->setSortingEnabled(true); e->setHeaderHidden( true ); e->setRootIsDecorated( false ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::SingleSelection ); // e->resizeColumnToContents( 0 ); e->setDragDropMode( QAbstractItemView::DragDrop ); e->setAcceptDrops( true ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(e, &QAbstractItemView::doubleClicked, this, &TaskEditor::taskModuleDoubleClicked); connect(this, &TaskEditor::loadTaskModules, m, &TaskModuleModel::loadTaskModules); connect(m, &TaskModuleModel::saveTaskModule, this, &TaskEditor::saveTaskModule); connect(m, &TaskModuleModel::removeTaskModule, this, &TaskEditor::removeTaskModule); addDocker( ds ); } } void TaskEditor::taskModuleDoubleClicked(QModelIndex idx) { QUrl url = idx.data(Qt::UserRole).toUrl(); if (url.isValid()) { emit openDocument(url); } } void TaskEditor::setTaskModules(const QStringList& files) { emit loadTaskModules( files ); } void TaskEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskEditor::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<( selectedNode() ) ); } QModelIndexList TaskEditor::selectedRows() const { #if 0 // Qt bug? return m_view->selectionModel()->selectedRows(); #else QModelIndexList lst; foreach ( QModelIndex i, m_view->selectionModel()->selectedIndexes() ) { if ( i.column() == 0 ) { lst << i; } } return lst; #endif } int TaskEditor::selectedRowCount() const { return selectedRows().count(); } QList TaskEditor::selectedNodes() const { QList lst; foreach ( const QModelIndex &i, selectedRows() ) { Node * n = m_view->baseModel()->node( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskEditor::currentNode() const { Node * n = m_view->baseModel()->node( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskEditor::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos, const QModelIndexList &rows ) { QString name; if (rows.count() > 1) { debugPlan< summarytasks; QList tasks; QList milestones; for (const QModelIndex &idx : rows) { Node *node = m_view->baseModel()->node( idx ); if (node) { switch ( node->type() ) { case Node::Type_Task: tasks << static_cast(node); break; case Node::Type_Milestone: milestones << static_cast(node); break; case Node::Type_Summarytask: summarytasks << static_cast(node); break; default: break; } } } if (!tasks.isEmpty()) { editTasks(tasks, pos); return; } return; } Node *node = m_view->baseModel()->node( index ); if ( node == 0 ) { return; } debugPlan<name()<<" :"<type() ) { case Node::Type_Project: name = "task_edit_popup"; break; case Node::Type_Task: name = node->isScheduled( baseModel()->id() ) ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = node->isScheduled( baseModel()->id() ) ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: name = "node_popup"; break; } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } debugPlan<setContextMenuIndex(QModelIndex()); } void TaskEditor::editTasks(const QList &tasks, const QPoint &pos) { QList lst; QAction tasksEdit(i18n( "Edit..."), nullptr); if (!tasks.isEmpty()) { TasksEditController *ted = new TasksEditController(*project(), tasks, this); connect(&tasksEdit, &QAction::triggered, ted, &TasksEditController::activate); connect(ted, &TasksEditController::addCommand, koDocument(), &KoDocument::addCommand); lst << &tasksEdit; } lst += contextActionList(); if (!lst.isEmpty()) { QMenu::exec( lst, pos, lst.first() ); } } void TaskEditor::setScheduleManager( ScheduleManager *sm ) { if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_view->masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskEditor::slotEnableActions() { updateActionsEnabled( isReadWrite() ); } void TaskEditor::updateActionsEnabled( bool on ) { // debugPlan<setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } int selCount = selectedRowCount(); if ( selCount == 0 ) { if ( currentNode() ) { // there are tasks but none is selected menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } else { // we need to be able to add the first task menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } return; } Node *n = selectedNode(); // 0 if not a single task, summarytask or milestone if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } if ( selCount == 1 && n != currentNode() ) { // multi selection in progress menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); Node *s = n->siblingBefore(); actionMoveTaskUp->setEnabled( s ); actionMoveTaskDown->setEnabled( n->siblingAfter() ); s = n->siblingBefore(); actionIndentTask->setEnabled( ! baselined && s && ! s->isBaselined() ); actionUnindentTask->setEnabled( ! baselined && n->level() > 1 ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } void TaskEditor::setupGui() { QString name = "taskeditor_add_list"; menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); actionCollection()->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); addAction( name, menuAddTask ); actionAddTask = new QAction( i18n( "Add Task" ), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n( "Add Milestone" ), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &TaskEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); actionCollection()->addAction("add_subtask", menuAddTask ); connect( menuAddSubTask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); addAction( name, menuAddSubTask ); actionAddSubtask = new QAction( i18n( "Add Sub-Task" ), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n( "Add Sub-Milestone" ), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &TaskEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->setDefaultShortcut( actionDeleteTask, Qt::Key_Delete ); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, &QAction::triggered, this, &TaskEditor::slotDeleteTask ); addAction( name, actionDeleteTask ); name = "taskeditor_move_list"; actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect(actionIndentTask, &QAction::triggered, this, &TaskEditor::slotIndentTask); addAction( name, actionIndentTask ); actionUnindentTask = new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect(actionUnindentTask, &QAction::triggered, this, &TaskEditor::slotUnindentTask); addAction( name, actionUnindentTask ); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect(actionMoveTaskUp, &QAction::triggered, this, &TaskEditor::slotMoveTaskUp); addAction( name, actionMoveTaskUp ); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect(actionMoveTaskDown, &QAction::triggered, this, &TaskEditor::slotMoveTaskDown); addAction( name, actionMoveTaskDown ); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskEditor::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); createDockers(); } void TaskEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void TaskEditor::slotAddTask() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddMilestone() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { // None selected or only project selected: insert under main project m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); // sibling if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubMilestone() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubtask() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->setParentsExpanded( i, true ); // in case treeview does not have focus m_view->edit( i ); } } void TaskEditor::slotDeleteTask() { //debugPlan; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } //foreach ( Node* n, lst ) { debugPlan<name(); } emit deleteTaskList( lst ); QModelIndex i = m_view->selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotIndentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit indentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->setParentsExpanded( i, true ); } } void TaskEditor::slotUnindentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit unindentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskUp() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskUp(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskDown() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskDown(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } bool TaskEditor::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? bool res = m_view->loadContext( baseModel()->columnMap(), context ); return res; } void TaskEditor::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( baseModel()->columnMap(), context ); } KoPrintJob *TaskEditor::createPrintJob() { return m_view->createPrintJob( this ); } +void TaskEditor::slotEditCopy() +{ + m_view->editCopy(); +} //----------------------------------- TaskView::TaskView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new NodeTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); NodeSortFilterProxyModel *p = new NodeSortFilterProxyModel( m_view->baseModel(), m_view ); m_view->setModel( p ); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); - m_view->setDragDropMode( QAbstractItemView::InternalMove ); + m_view->setDragDropMode(QAbstractItemView::DragOnly); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodePerformanceIndex << NodeModel::NodeBCWS << NodeModel::NodeBCWP << NodeModel::NodeACWP << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Task Execution View" "" "The view is used to edit and inspect task progress during project execution." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Execution_View"))); } void TaskView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskView::draw( Project &project ) { m_view->setProject( &project ); } void TaskView::draw() { } void TaskView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( i ) ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskView::currentNode() const { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( m_view->selectionModel()->currentIndex() ) ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = m_view->baseModel()->node( proxyModel()->mapToSource( index ) ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager() && sm != scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskView::slotEnableActions() { updateActionsEnabled( true ); } void TaskView::updateActionsEnabled( bool /*on*/ ) { } void TaskView::setupGui() { // KActionCollection *coll = actionCollection(); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool TaskView::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskView::createPrintJob() { return m_view->createPrintJob( this ); } +void TaskView::slotEditCopy() +{ + m_view->editCopy(); +} + //--------------------------------- WorkPackageTreeView::WorkPackageTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { debugPlan<<"----------"<baseModel(); } void WorkPackageTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { Q_UNUSED(index); Q_UNUSED(dropIndicatorPosition); Q_UNUSED(event); /* QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); }*/ } //-------------------------------- TaskWorkPackageView::TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ), m_cmd( 0 ) { QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new WorkPackageTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::InternalMove ); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskWorkPackageView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskWorkPackageView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); } Project *TaskWorkPackageView::project() const { return m_view->project(); } void TaskWorkPackageView::setProject( Project *project ) { m_view->setProject( project ); } WorkPackageProxyModel *TaskWorkPackageView::proxyModel() const { return m_view->proxyModel(); } void TaskWorkPackageView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskWorkPackageView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskWorkPackageView::slotRefreshView() { emit checkForWorkPackages(false); } void TaskWorkPackageView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskWorkPackageView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = proxyModel()->taskFromIndex( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskWorkPackageView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskWorkPackageView::currentNode() const { Node * n = proxyModel()->taskFromIndex( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskWorkPackageView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = proxyModel()->taskFromIndex( index ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "workpackage_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskWorkPackageView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<baseModel()->setScheduleManager( sm ); } void TaskWorkPackageView::slotEnableActions() { updateActionsEnabled( true ); } void TaskWorkPackageView::updateActionsEnabled( bool on ) { bool o = ! selectedNodes().isEmpty(); actionMailWorkpackage->setEnabled( o && on ); } void TaskWorkPackageView::setupGui() { // KActionCollection *coll = actionCollection(); QString name = "workpackage_list"; actionMailWorkpackage = new QAction(koIcon("mail-send"), i18n("Send..."), this); actionCollection()->setDefaultShortcut( actionMailWorkpackage, Qt::CTRL + Qt::Key_M ); actionCollection()->addAction("send_workpackage", actionMailWorkpackage ); connect( actionMailWorkpackage, &QAction::triggered, this, &TaskWorkPackageView::slotMailWorkpackage ); addAction( name, actionMailWorkpackage ); // Add the context menu actions for the view options connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskWorkPackageView::slotMailWorkpackage() { QList lst = selectedNodes(); if ( ! lst.isEmpty() ) { // TODO find a better way to log to avoid undo/redo m_cmd = new MacroCommand( kundo2_i18n( "Log Send Workpackage" ) ); QPointer dlg = new WorkPackageSendDialog( lst, scheduleManager(), this ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::mailWorkpackages ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::slotWorkPackageSent ); dlg->exec(); delete dlg; if ( ! m_cmd->isEmpty() ) { part()->addCommand( m_cmd ); m_cmd = 0; } delete m_cmd; m_cmd = 0; } } void TaskWorkPackageView::slotWorkPackageSent( const QList &nodes, Resource *resource ) { foreach ( Node *n, nodes ) { WorkPackage *wp = new WorkPackage( static_cast( n )->workPackage() ); wp->setOwnerName( resource->name() ); wp->setOwnerId( resource->id() ); wp->setTransmitionTime( DateTime::currentDateTime() ); wp->setTransmitionStatus( WorkPackage::TS_Send ); m_cmd->addCommand( new WorkPackageAddCmd( static_cast( n->projectNode() ), n, wp ) ); } } void TaskWorkPackageView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskWorkPackageView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool TaskWorkPackageView::loadContext( const KoXmlElement &context ) { debugPlan; ViewBase::loadContext( context ); return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskWorkPackageView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskWorkPackageView::createPrintJob() { return m_view->createPrintJob( this ); } +void TaskWorkPackageView::slotEditCopy() +{ + m_view->editCopy(); +} + } // namespace KPlato diff --git a/src/libs/ui/kpttaskeditor.h b/src/libs/ui/kpttaskeditor.h index 90742728..47f5147f 100644 --- a/src/libs/ui/kpttaskeditor.h +++ b/src/libs/ui/kpttaskeditor.h @@ -1,356 +1,359 @@ /* This file is part of the KDE project Copyright (C) 2006 -20010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASKEDITOR_H #define KPTTASKEDITOR_H #include "planui_export.h" #include "kptglobal.h" #include "kptnodeitemmodel.h" #include "kptviewbase.h" class KoDocument; class KActionMenu; namespace KPlato { class Project; class Node; class NodeItemModel; class MacroCommand; class PLANUI_EXPORT TaskEditorItemModel : public NodeItemModel { Q_OBJECT public: explicit TaskEditorItemModel( QObject *parent = 0 ); virtual Qt::ItemFlags flags( const QModelIndex & index ) const; virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex & index, const QVariant &value, int role = Qt::EditRole ); protected: QVariant type( const Node *node, int role ) const; virtual bool setType( Node *node, const QVariant &value, int role ); }; class PLANUI_EXPORT TaskEditorTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit TaskEditorTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; NodeSortFilterProxyModel *proxyModel() const { return qobject_cast( model() ); } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { baseModel()->setProject( project ); } Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); }; class PLANUI_EXPORT NodeTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit NodeTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; NodeSortFilterProxyModel *proxyModel() const { return qobject_cast( model() ); } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { baseModel()->setProject( project ); } Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); }; class PLANUI_EXPORT TaskEditor : public ViewBase { Q_OBJECT public: TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); void setProject( Project *project ); Project *project() const { return m_view->project(); } virtual void createDockers(); virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); NodeItemModel *baseModel() const { return m_view->baseModel(); } NodeSortFilterProxyModel *proxyModel() const { return m_view->proxyModel(); } QAbstractItemModel *model() const { return m_view->model(); } /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; virtual KoPrintJob *createPrintJob(); Q_SIGNALS: void taskSelected(KPlato::Task *task); void openNode(); void addTask(); void addMilestone(); void addSubtask(); void addSubMilestone(); void deleteTaskList(const QList&); void moveTaskUp(); void moveTaskDown(); void indentTask(); void unindentTask(); void loadTaskModules( const QStringList &files ); void saveTaskModule(const QUrl &url, KPlato::Project *project); void removeTaskModule( const QUrl &url ); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void setScheduleManager(KPlato::ScheduleManager *sm); void setTaskModules( const QStringList &files ); + void slotEditCopy(); protected: void updateActionsEnabled( bool on ); int selectedRowCount() const; QModelIndexList selectedRows() const; void editTasks(const QList &tasks, const QPoint &pos); protected Q_SLOTS: virtual void slotOptions(); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested(const QModelIndex &index, const QPoint& pos , const QModelIndexList &rows); void slotEnableActions(); void slotAddTask(); void slotAddSubtask(); void slotAddMilestone(); void slotAddSubMilestone(); void slotDeleteTask(); void slotIndentTask(); void slotUnindentTask(); void slotMoveTaskUp(); void slotMoveTaskDown(); void slotSplitView(); void slotProjectShown( bool ); void taskModuleDoubleClicked(QModelIndex idx); private: void edit( const QModelIndex &index ); private: TaskEditorTreeView *m_view; KActionMenu *menuAddTask; KActionMenu *menuAddSubTask; QAction *actionAddTask; QAction *actionAddMilestone; QAction *actionAddSubtask; QAction *actionAddSubMilestone; QAction *actionDeleteTask; QAction *actionMoveTaskUp; QAction *actionMoveTaskDown; QAction *actionIndentTask; QAction *actionUnindentTask; QAction *actionShowProject; QDomDocument m_domdoc; }; class PLANUI_EXPORT TaskView : public ViewBase { Q_OBJECT public: TaskView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const { return m_view->project(); } virtual void draw( Project &project ); virtual void draw(); NodeItemModel *baseModel() const { return m_view->baseModel(); } NodeSortFilterProxyModel *proxyModel() const { return m_view->proxyModel(); } virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); Q_SIGNALS: void openNode(); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void setScheduleManager(KPlato::ScheduleManager *sm); + void slotEditCopy(); protected: void updateActionsEnabled( bool on ); int selectedNodeCount() const; protected Q_SLOTS: virtual void slotOptions(); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotEnableActions(); void slotSplitView(); private: NodeTreeView *m_view; QAction *actionShowProject; QDomDocument m_domdoc; }; //----------------------------------- class WorkPackageTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit WorkPackageTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); NodeItemModel *baseModel() const; WorkPackageProxyModel *proxyModel() const { return m; } Project *project() const { return baseModel()->project(); } void setProject( Project *project ) { m->setProject( project ); } ScheduleManager *scheduleManager() const { return baseModel()->manager(); } Q_SIGNALS: void currentColumnChanged( const QModelIndex&, const QModelIndex& ); protected Q_SLOTS: void slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); protected: WorkPackageProxyModel *m; }; class PLANUI_EXPORT TaskWorkPackageView : public ViewBase { Q_OBJECT public: TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const; void setProject( Project *project ); ScheduleManager *scheduleManager() const { return m_view->scheduleManager(); } WorkPackageProxyModel *proxyModel() const; virtual Node *currentNode() const; QList selectedNodes() const ; Node *selectedNode() const; virtual void updateReadWrite( bool readwrite ); /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); Q_SIGNALS: void mailWorkpackage(KPlato::Node *n, KPlato::Resource *r = 0); void mailWorkpackages(const QList &nodes, KPlato::Resource *r); void checkForWorkPackages(bool); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void slotRefreshView(); void setScheduleManager(KPlato::ScheduleManager *sm); + void slotEditCopy(); protected: void updateActionsEnabled( bool on ); int selectedNodeCount() const; protected Q_SLOTS: virtual void slotOptions(); void slotMailWorkpackage(); void slotWorkPackageSent(const QList &nodes, KPlato::Resource *resource); private Q_SLOTS: void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotEnableActions(); void slotSplitView(); private: WorkPackageTreeView *m_view; MacroCommand *m_cmd; QAction *actionMailWorkpackage; }; } //namespace KPlato #endif diff --git a/src/libs/ui/kpttaskstatusview.cpp b/src/libs/ui/kpttaskstatusview.cpp index 87ca860d..f7e8facf 100644 --- a/src/libs/ui/kpttaskstatusview.cpp +++ b/src/libs/ui/kpttaskstatusview.cpp @@ -1,459 +1,427 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * Copyright (C) 2007 - 2010, 2012 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskstatusview.h" #include "kpttaskstatusmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptproject.h" #include "kptschedule.h" #include "kpteffortcostmap.h" #include "Help.h" #include "kptdebug.h" #include #include "KoDocument.h" #include "KoPageLayoutWidget.h" +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; using namespace KPlato; TaskStatusTreeView::TaskStatusTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { + setDragPixmap(koIcon("view-task").pixmap(32)); setContextMenuPolicy( Qt::CustomContextMenu ); TaskStatusItemModel *m = new TaskStatusItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); + setSelectionBehavior( QAbstractItemView::SelectRows ); setStretchLastSection( false ); createItemDelegates( m ); QList lst1; lst1 << 1 << -1; // only display column 0 (NodeName) in left view masterView()->setDefaultColumns( QList() << 0 ); QList show; show << NodeModel::NodeCompleted << NodeModel::NodeActualEffort << NodeModel::NodeRemainingEffort << NodeModel::NodePlannedEffort << NodeModel::NodePlannedCost << NodeModel::NodeActualCost << NodeModel::NodeStatus << NodeModel::NodeActualStart << NodeModel::NodeActualFinish << NodeModel::NodeStatusNote; QList lst2; for ( int i = 0; i < m->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } hideColumns( lst1, lst2 ); slaveView()->setDefaultColumns( show ); } int TaskStatusTreeView::weekday() const { return model()->weekday(); } void TaskStatusTreeView::setWeekday( int day ) { model()->setWeekday( day ); refresh(); } int TaskStatusTreeView::defaultPeriodType() const { return TaskStatusItemModel::UseCurrentDate; } int TaskStatusTreeView::periodType() const { return model()->periodType(); } void TaskStatusTreeView::setPeriodType( int type ) { model()->setPeriodType( type ); refresh(); } int TaskStatusTreeView::period() const { return model()->period(); } void TaskStatusTreeView::setPeriod( int days ) { model()->setPeriod( days ); refresh(); } TaskStatusItemModel *TaskStatusTreeView::model() const { return static_cast( DoubleTreeViewBase::model() ); } Project *TaskStatusTreeView::project() const { return model()->project(); } void TaskStatusTreeView::setProject( Project *project ) { model()->setProject( project ); } -void TaskStatusTreeView::dragMoveEvent(QDragMoveEvent */*event*/) -{ -/* if (dragDropMode() == InternalMove - && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) - return; - - TreeViewBase::dragMoveEvent( event ); - if ( ! event->isAccepted() ) { - return; - } - //QTreeView thinks it's ok to drop - event->ignore(); - QModelIndex index = indexAt( event->pos() ); - if ( ! index.isValid() ) { - event->accept(); - return; // always ok to drop on main project - } - Node *dn = model()->node( index ); - if ( dn == 0 ) { - errorPlan<<"no node to drop on!" - return; // hmmm - } - switch ( dropIndicatorPosition() ) { - case AboveItem: - case BelowItem: - //dn == sibling - if ( model()->dropAllowed( dn->parentNode(), event->mimeData() ) ) { - event->accept(); - } - break; - case OnItem: - //dn == new parent - if ( model()->dropAllowed( dn, event->mimeData() ) ) { - event->accept(); - } - break; - default: - break; - }*/ -} - //----------------------------------- TaskStatusView::TaskStatusView(KoPart *part, KoDocument *doc, QWidget *parent ) : ViewBase(part, doc, parent), m_id( -1 ) { debugPlan<<"-------------------- creating TaskStatusView -------------------"; QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new TaskStatusTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); + m_view->setDragDropMode(QAbstractItemView::DragOnly); + m_view->setDropIndicatorShown( false ); + m_view->setDragEnabled ( true ); + m_view->setAcceptDrops( false ); + m_view->setAcceptDropsOnView( false ); + l->addWidget( m_view ); setupGui(); connect( model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint,QModelIndexList)), SLOT(slotContextMenuRequested(QModelIndex,QPoint)) ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Task Status View" "" "The Task Status View is used to inspect task progress information. " "The tasks are divided into groups dependent on the task status:" "" "Not Started Tasks that should have been started by now." "Running Tasks that has been started, but not yet finished." "Finished Tasks that where finished in this period." "Next Period Tasks that is scheduled to be started in the next period." "" "The time period is configurable." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Status_View"))); } void TaskStatusView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); } void TaskStatusView::setScheduleManager( ScheduleManager *sm ) { //debugPlan; if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_view->masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); bool expand = sm && scheduleManager() && sm != scheduleManager(); QDomDocument doc; if (expand) { QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); static_cast( m_view->model() )->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } Node *TaskStatusView::currentNode() const { return m_view->model()->node( m_view->selectionModel()->currentIndex() ); } void TaskStatusView::setProject( Project *project ) { m_project = project; m_view->model()->setProject( m_project ); } void TaskStatusView::draw( Project &project ) { setProject( &project ); } void TaskStatusView::setGuiActive( bool activate ) { debugPlan<setContextMenuIndex(index); Node *node = m_view->model()->node( index ); if ( node == 0 ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } slotContextMenuRequested( node, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskStatusView::slotContextMenuRequested( Node *node, const QPoint& pos ) { debugPlan<name()<<" :"<type() ) { case Node::Type_Task: name = "taskstatus_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } debugPlan<actionSplitView(), &QAction::triggered, this, &TaskStatusView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskStatusView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskStatusView::slotRefreshView() { model()->refresh(); } void TaskStatusView::slotOptions() { debugPlan; TaskStatusViewSettingsDialog *dlg = new TaskStatusViewSettingsDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } bool TaskStatusView::loadContext( const KoXmlElement &context ) { debugPlan; ViewBase::loadContext( context ); m_view->setPeriod( context.attribute( "period", QString("%1").arg( m_view->defaultPeriod() ) ).toInt() ); m_view->setPeriodType( context.attribute( "periodtype", QString("%1").arg( m_view->defaultPeriodType() ) ).toInt() ); m_view->setWeekday( context.attribute( "weekday", QString("%1").arg( m_view->defaultWeekday() ) ).toInt() ); return m_view->loadContext( model()->columnMap(), context ); } void TaskStatusView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "period", QString::number(m_view->period()) ); context.setAttribute( "periodtype", QString::number(m_view->periodType()) ); context.setAttribute( "weekday", QString::number(m_view->weekday()) ); m_view->saveContext( model()->columnMap(), context ); } KoPrintJob *TaskStatusView::createPrintJob() { return m_view->createPrintJob( this ); } //------------------------------------------------ TaskStatusViewSettingsPanel::TaskStatusViewSettingsPanel( TaskStatusTreeView *view, QWidget *parent ) : QWidget( parent ), m_view( view ) { setupUi( this ); QStringList lst; QLocale locale; for ( int i = 1; i <= 7; ++i ) { lst << locale.dayName( i, QLocale::ShortFormat ); } weekdays->addItems( lst ); period->setValue( view->period() ); switch ( view->periodType() ) { case TaskStatusItemModel::UseCurrentDate: useCurrentDate->setChecked( true ); break; case TaskStatusItemModel::UseWeekday: useWeekday->setChecked( true ); break; default: break; } weekdays->setCurrentIndex( m_view->weekday() - 1 ); connect( period, SIGNAL(valueChanged(int)), SIGNAL(changed()) ); connect( useWeekday, &QAbstractButton::toggled, this, &TaskStatusViewSettingsPanel::changed ); connect( useCurrentDate, &QAbstractButton::toggled, this, &TaskStatusViewSettingsPanel::changed ); connect( weekdays, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()) ); } void TaskStatusViewSettingsPanel::slotOk() { if ( period->value() != m_view->period() ) { m_view->setPeriod( period->value() ); } if ( weekdays->currentIndex() != m_view->weekday() - 1 ) { m_view->setWeekday( weekdays->currentIndex() + 1 ); } if ( useCurrentDate->isChecked() && m_view->periodType() != TaskStatusItemModel::UseCurrentDate ) { m_view->setPeriodType( TaskStatusItemModel::UseCurrentDate ); } else if ( useWeekday->isChecked() && m_view->periodType() != TaskStatusItemModel::UseWeekday ) { m_view->setPeriodType( TaskStatusItemModel::UseWeekday ); } } void TaskStatusViewSettingsPanel::setDefault() { period->setValue( m_view->defaultPeriod() ); switch ( m_view->defaultPeriodType() ) { case TaskStatusItemModel::UseCurrentDate: useCurrentDate->setChecked( true ); break; case TaskStatusItemModel::UseWeekday: useWeekday->setChecked( true ); break; default: break; } weekdays->setCurrentIndex( m_view->defaultWeekday() - 1 ); } TaskStatusViewSettingsDialog::TaskStatusViewSettingsDialog( ViewBase *view, TaskStatusTreeView *treeview, QWidget *parent ) : SplitItemViewSettupDialog( view, treeview, parent ) { TaskStatusViewSettingsPanel *panel = new TaskStatusViewSettingsPanel( treeview ); KPageWidgetItem *page = insertWidget( 0, panel, i18n( "General" ), i18n( "General Settings" ) ); setCurrentPage( page ); //connect( panel, SIGNAL(changed(bool)), this, SLOT(enableButtonOk(bool)) ); connect( this, &QDialog::accepted, panel, &TaskStatusViewSettingsPanel::slotOk ); connect( button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, panel, &TaskStatusViewSettingsPanel::setDefault ); } diff --git a/src/libs/ui/kpttaskstatusview.h b/src/libs/ui/kpttaskstatusview.h index b016bc30..11e6ace6 100644 --- a/src/libs/ui/kpttaskstatusview.h +++ b/src/libs/ui/kpttaskstatusview.h @@ -1,175 +1,172 @@ /* This file is part of the KDE project * Copyright (C) 2019 Dag Andersen * Copyright (C) 2007 - 2010 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef TASKSTATUSVIEW_H #define TASKSTATUSVIEW_H #include "planui_export.h" #include "kptitemmodelbase.h" #include "kptviewbase.h" #include "ui_kpttaskstatusviewsettingspanel.h" #include "kptitemviewsettup.h" #include "kptnodechartmodel.h" #include #include class QItemSelection; class KoDocument; class KoPageLayoutWidget; class PrintingHeaderFooter; namespace KChart { class CartesianCoordinatePlane; class CartesianAxis; class Legend; }; namespace KPlato { class Project; class Node; class ScheduleManager; class TaskStatusItemModel; class NodeItemModel; class PerformanceStatusBase; typedef QList NodeList; class PLANUI_EXPORT TaskStatusTreeView : public DoubleTreeViewBase { Q_OBJECT public: explicit TaskStatusTreeView(QWidget *parent); //void setSelectionModel( QItemSelectionModel *selectionModel ); TaskStatusItemModel *model() const; Project *project() const; void setProject( Project *project ); int defaultWeekday() const { return Qt::Friday; } int weekday() const; void setWeekday( int day ); int defaultPeriod() const { return 7; } int period() const; void setPeriod( int days ); int defaultPeriodType() const; int periodType() const; void setPeriodType( int type ); - -protected: - void dragMoveEvent(QDragMoveEvent *event); }; class PLANUI_EXPORT TaskStatusView : public ViewBase { Q_OBJECT public: TaskStatusView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); virtual void setProject( Project *project ); Project *project() const { return m_view->project(); } using ViewBase::draw; virtual void draw( Project &project ); TaskStatusItemModel *model() const { return m_view->model(); } virtual void updateReadWrite( bool readwrite ); virtual Node *currentNode() const; /// Loads context info into this view. Reimplement. virtual bool loadContext( const KoXmlElement &/*context*/ ); /// Save context info from this view. Reimplement. virtual void saveContext( QDomElement &/*context*/ ) const; KoPrintJob *createPrintJob(); Q_SIGNALS: void openNode(); public Q_SLOTS: /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); void setScheduleManager(KPlato::ScheduleManager *sm); virtual void slotRefreshView(); protected Q_SLOTS: virtual void slotOptions(); protected: void updateActionsEnabled( bool on ); private Q_SLOTS: void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotContextMenuRequested(KPlato::Node *node, const QPoint& pos); void slotSplitView(); private: Project *m_project; int m_id; TaskStatusTreeView *m_view; QDomDocument m_domdoc; }; //-------------------------------------- class TaskStatusViewSettingsPanel : public QWidget, public Ui::TaskStatusViewSettingsPanel { Q_OBJECT public: explicit TaskStatusViewSettingsPanel( TaskStatusTreeView *view, QWidget *parent = 0 ); public Q_SLOTS: void slotOk(); void setDefault(); Q_SIGNALS: void changed(); private: TaskStatusTreeView *m_view; }; class TaskStatusViewSettingsDialog : public SplitItemViewSettupDialog { Q_OBJECT public: explicit TaskStatusViewSettingsDialog( ViewBase *view, TaskStatusTreeView *treeview, QWidget *parent = 0 ); }; } //namespace KPlato #endif diff --git a/src/libs/ui/kptviewbase.cpp b/src/libs/ui/kptviewbase.cpp index 945906d6..d036c9c3 100644 --- a/src/libs/ui/kptviewbase.cpp +++ b/src/libs/ui/kptviewbase.cpp @@ -1,2525 +1,2682 @@ /* This file is part of the KDE project Copyright (C) 2006 -2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptviewbase.h" #include "kptitemmodelbase.h" #include "kptproject.h" #include "kptdebug.h" #include "config.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 +#include namespace KPlato { DockWidget::DockWidget( ViewBase *v, const QString &identity, const QString &title ) : QDockWidget( v ), view( v ), id( identity ), location( Qt::RightDockWidgetArea ), editor( false ), m_shown( true ) { setWindowTitle( title ); setObjectName( v->objectName() + '-' + identity ); toggleViewAction()->setObjectName( objectName() ); connect(this, &QDockWidget::dockLocationChanged, this, &DockWidget::setLocation); } void DockWidget::activate( KoMainWindow *mainWindow ) { connect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); setVisible( m_shown ); mainWindow->addDockWidget( location, this ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->addAction( toggleViewAction() ); break; } } } void DockWidget::deactivate( KoMainWindow *mainWindow ) { disconnect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); mainWindow->removeDockWidget( this ); // activation re-parents to QMainWindow, so re-parent back to view setParent( const_cast( view ) ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->removeAction( toggleViewAction() ); break; } } } void DockWidget::setShown( bool show ) { m_shown = show; setVisible( show ); } bool KPlato::DockWidget::shown() const { return m_shown; } void DockWidget::setLocation( Qt::DockWidgetArea area ) { location = area; } bool DockWidget::saveXml( QDomElement &context ) const { QDomElement e = context.ownerDocument().createElement( "docker" ); context.appendChild( e ); e.setAttribute( "id", id ); e.setAttribute( "location", QString::number(location) ); e.setAttribute( "floating", QString::number(isFloating()) ); e.setAttribute( "visible", QString::number(m_shown) ); return true; } void DockWidget::loadXml(const KoXmlElement& context) { location = static_cast( context.attribute( "location", "0" ).toInt() ); setFloating( (bool) context.attribute( "floating", "0" ).toInt() ); m_shown = context.attribute( "visible", "1" ).toInt(); } //------------------------ bool PrintingOptions::loadXml( KoXmlElement &element ) { KoXmlElement e; forEachElement( e, element ) { if ( e.tagName() == "header" ) { headerOptions.group = e.attribute( "group", "0" ).toInt(); headerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); headerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); headerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); headerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } else if ( e.tagName() == "footer" ) { footerOptions.group = e.attribute( "group", "0" ).toInt(); footerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); footerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); footerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); footerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } } return true; } void PrintingOptions::saveXml( QDomElement &element ) const { QDomElement me = element.ownerDocument().createElement( "printing-options" ); element.appendChild( me ); QDomElement h = me.ownerDocument().createElement( "header" ); me.appendChild( h ); h.setAttribute( "group", QString::number(headerOptions.group) ); h.setAttribute( "project", QString::number(headerOptions.project) ); h.setAttribute( "date", QString::number(headerOptions.date) ); h.setAttribute( "manager", QString::number(headerOptions.manager) ); h.setAttribute( "page", QString::number(headerOptions.page) ); QDomElement f = me.ownerDocument().createElement( "footer" ); me.appendChild( f ); f.setAttribute( "group", QString::number(footerOptions.group) ); f.setAttribute( "project", QString::number(footerOptions.project) ); f.setAttribute( "date", QString::number(footerOptions.date) ); f.setAttribute( "manager", QString::number(footerOptions.manager) ); f.setAttribute( "page", QString::number(footerOptions.page) ); } //---------------------- PrintingHeaderFooter::PrintingHeaderFooter( const PrintingOptions &opt, QWidget *parent ) : QWidget( parent ) { setupUi( this ); setWindowTitle( i18n("Header and Footer" )); setOptions( opt ); connect(ui_header, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footer, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); } PrintingHeaderFooter::~PrintingHeaderFooter() { //debugPlan; } void PrintingHeaderFooter::slotChanged() { debugPlan; emit changed( options() ); } void PrintingHeaderFooter::setOptions( const PrintingOptions &options ) { m_options = options; ui_header->setChecked( m_options.headerOptions.group ); ui_headerProject->setCheckState( m_options.headerOptions.project ); ui_headerDate->setCheckState( m_options.headerOptions.date ); ui_headerManager->setCheckState( m_options.headerOptions.manager ); ui_headerPage->setCheckState( m_options.headerOptions.page ); ui_footer->setChecked( m_options.footerOptions.group ); ui_footerProject->setCheckState( m_options.footerOptions.project ); ui_footerDate->setCheckState( m_options.footerOptions.date ); ui_footerManager->setCheckState( m_options.footerOptions.manager ); ui_footerPage->setCheckState( m_options.footerOptions.page ); } PrintingOptions PrintingHeaderFooter::options() const { //debugPlan; PrintingOptions opt; opt.headerOptions.group = ui_header->isChecked(); opt.headerOptions.project = ui_headerProject->checkState(); opt.headerOptions.date = ui_headerDate->checkState(); opt.headerOptions.manager = ui_headerManager->checkState(); opt.headerOptions.page = ui_headerPage->checkState(); opt.footerOptions.group = ui_footer->isChecked(); opt.footerOptions.project = ui_footerProject->checkState(); opt.footerOptions.date = ui_footerDate->checkState( ); opt.footerOptions.manager = ui_footerManager->checkState(); opt.footerOptions.page = ui_footerPage->checkState(); return opt; } PrintingDialog::PrintingDialog( ViewBase *view ) : KoPrintingDialog( view ), m_view( view ), m_widget( 0 ) { setPrinterPageLayout( view->pageLayout() ); QImage px( 100, 600, QImage::Format_Mono ); int dpm = printer().resolution() * 40; px.setDotsPerMeterX( dpm ); px.setDotsPerMeterY( dpm ); QPainter p( &px ); m_textheight = p.boundingRect( QRectF(), Qt::AlignTop, "Aj" ).height(); debugPlan<<"textheight:"<printingOptions(); } void PrintingDialog::setPrintingOptions( const PrintingOptions &opt ) { debugPlan; m_view->setPrintingOptions( opt ); emit changed( opt ); emit changed(); } void PrintingDialog::setPrinterPageLayout( const KoPageLayout &pagelayout ) { QPrinter &p = printer(); QPrinter::Orientation o; switch ( pagelayout.orientation ) { case KoPageFormat::Portrait: o = QPrinter::Portrait; break; case KoPageFormat::Landscape: o = QPrinter::Landscape; break; default: o = QPrinter::Portrait; break; } p.setOrientation( o ); p.setPaperSize( KoPageFormat::printerPageSize( pagelayout.format ) ); p.setPageMargins( pagelayout.leftMargin, pagelayout.topMargin, pagelayout.rightMargin, pagelayout.bottomMargin, QPrinter::Point ); } void PrintingDialog::startPrinting(RemovePolicy removePolicy ) { setPrinterPageLayout( m_view->pageLayout() ); // FIXME: Something resets printer().paperSize() to A4 ! KoPrintingDialog::startPrinting( removePolicy ); } QWidget *PrintingDialog::createPageLayoutWidget() const { QWidget *w = ViewBase::createPageLayoutWidget( m_view ); KoPageLayoutWidget *pw = w->findChild(); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), m_view, SLOT(setPageLayout(KoPageLayout))); connect(pw, &KoPageLayoutWidget::layoutChanged, this, &PrintingDialog::setPrinterPageLayout); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), this, SIGNAL(changed())); return w; } QList PrintingDialog::createOptionWidgets() const { //debugPlan; PrintingHeaderFooter *w = new PrintingHeaderFooter( printingOptions() ); connect(w, &PrintingHeaderFooter::changed, this, &PrintingDialog::setPrintingOptions); const_cast( this )->m_widget = w; return QList() << w; } /*QList PrintingDialog::shapesOnPage(int) { return QList(); }*/ void PrintingDialog::drawRect( QPainter &p, const QRect &r, Qt::Edges edges ) { p.save(); QPen pen = p.pen(); pen.setColor(Qt::gray); p.setPen(pen); if (edges & Qt::LeftEdge) { p.drawLine( r.topLeft(), r.bottomLeft() ); } if (edges & Qt::BottomEdge) { p.drawLine( r.bottomLeft(), r.bottomRight() ); } if (edges & Qt::TopEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } if (edges & Qt::RightEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } p.restore(); } QRect PrintingDialog::headerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.headerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.headerOptions ); return QRect( 0, 0, const_cast( this )->printer().pageRect().width(), height ); } QRect PrintingDialog::footerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.footerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.footerOptions ); QRect r = const_cast( this )->printer().pageRect(); return QRect( 0, r.height() - height, r.width(), height ); } int PrintingDialog::headerFooterHeight( const PrintingOptions::Data &options ) const { int height = 0.0; if ( options.page == Qt::Checked || options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { height += m_textheight * 1.5; } if ( options.project == Qt::Checked && options.manager == Qt::Checked && ( options.date == Qt::Checked || options.page == Qt::Checked ) ) { height *= 2.0; } debugPlan< pageRect.left()) { pageRect.setLeft(r.left()); } p.restore(); if ( options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } } if ( options.date == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Date:") ); p.restore(); if ( ( options.project == Qt::Checked && options.manager != Qt::Checked ) || ( options.project != Qt::Checked && options.manager == Qt::Checked ) ) { dateRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_1.height() ); rect_1.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else if ( options.project == Qt::Checked && options.manager == Qt::Checked ) { dateRect = p.boundingRect( rect_2, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } else { dateRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, date ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if ( rect_2.left() != rect.left() ) { p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } } } if ( options.manager == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Manager:") ); p.restore(); if ( options.project != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } else if ( options.date != Qt::Checked && options.page != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } rect_1.setRight( managerRect.left() - 2 ); p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else { managerRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_2.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } } if ( options.project == Qt::Checked ) { projectRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, projectName ); projectRect.setHeight( rect_1.height() ); p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Project:") ); if (rct.right() > projectRect.right()) { projectRect.setRight(rct.right()); } p.restore(); } if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignHCenter|Qt::AlignBottom, page ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, projectName ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignHCenter|Qt::AlignBottom, date ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, manager ); } QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Page:" ) ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Project:" ) ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Date:" ) ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Manager:" ) ); } p.restore(); } //-------------- ViewBase::ViewBase(KoPart *part, KoDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_readWrite( false ), m_proj( 0 ), m_schedulemanager( 0 ) { } ViewBase::~ViewBase() { if ( koDocument() ) { //HACK to avoid ~View to access koDocument() setDocumentDeleted(); } } void ViewBase::setProject( Project *project ) { m_proj = project; emit projectChanged( project ); } KoDocument *ViewBase::part() const { return koDocument(); } KoPageLayout ViewBase::pageLayout() const { return m_pagelayout; } void ViewBase::setPageLayout( const KoPageLayout &layout ) { m_pagelayout = layout; } bool ViewBase::isActive() const { if ( hasFocus() ) { return true; } foreach ( QWidget *v, findChildren() ) { if ( v->hasFocus() ) { return true; } } return false; } void ViewBase::updateReadWrite( bool readwrite ) { m_readWrite = readwrite; emit readWriteChanged( readwrite ); } void ViewBase::setGuiActive( bool active ) // virtual slot { //debugPlan<setWindowTitle( xi18nc( "@title:tab", "Page Layout" ) ); QHBoxLayout *lay = new QHBoxLayout(widget); KoPageLayoutWidget *w = new KoPageLayoutWidget( widget, view->pageLayout() ); w->showPageSpread( false ); lay->addWidget( w, 1 ); KoPagePreviewWidget *prev = new KoPagePreviewWidget( widget ); prev->setPageLayout( view->pageLayout() ); lay->addWidget( prev, 1 ); connect (w, &KoPageLayoutWidget::layoutChanged, prev, &KoPagePreviewWidget::setPageLayout); return widget; } /*static*/ PrintingHeaderFooter *ViewBase::createHeaderFooterWidget( ViewBase *view ) { PrintingHeaderFooter *widget = new PrintingHeaderFooter( view->printingOptions() ); widget->setWindowTitle( xi18nc( "@title:tab", "Header and Footer" ) ); widget->setOptions( view->printingOptions() ); return widget; } void ViewBase::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlan; QList lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } void ViewBase::createOptionActions(int actions) { QAction *action; action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionExpand) { action = new QAction(koIcon("arrow-down"), i18n("Expand All"), this); connect(action, &QAction::triggered, this, &ViewBase::expandAll); addContextAction(action); } if (actions & OptionExpand) { action = new QAction(koIcon("arrow-up"), i18n("Collapse All"), this); connect(action, &QAction::triggered, this, &ViewBase::collapseAll); addContextAction(action); } action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionPrint) { action = KStandardAction::create(KStandardAction::Print, mainWindow(), SLOT(slotFilePrint()), this); action->setObjectName("print"); addContextAction(action); } if (actions & OptionPrintPreview) { action = KStandardAction::create(KStandardAction::PrintPreview, mainWindow(), SLOT(slotFilePrintPreview()), this); action->setObjectName("print preview"); addContextAction(action); } if (actions & OptionPrintPdf) { action = new QAction(koIcon("application-pdf"), i18n("Print to PDF..."), this); action->setObjectName("print pdf"); connect(action, SIGNAL(triggered()), mainWindow(), SLOT(exportToPdf())); addContextAction(action); } if (actions & OptionPrintConfig) { action = new QAction(koIcon("configure"), i18n("Print Options..."), this); action->setObjectName("print options"); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionViewConfig) { action = new QAction(koIcon("configure"), i18n("Configure View..."), this); action->setObjectName("configure view"); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } } void ViewBase::slotOptionsFinished( int result ) { if ( result == QDialog::Accepted ) { emit optionsModified(); } if ( sender() ) { sender()->deleteLater(); } } bool ViewBase::loadContext( const KoXmlElement &context ) { KoXmlElement me; forEachElement( me, context ) { if ( me.tagName() == "page-layout" ) { m_pagelayout.format = KoPageFormat::formatFromString( me.attribute( "format" ) ); m_pagelayout.orientation = me.attribute( "orientation" ) == "landscape" ? KoPageFormat::Landscape : KoPageFormat::Portrait; m_pagelayout.width = me.attribute( "width", "0.0" ).toDouble(); m_pagelayout.height = me.attribute( "height", "0.0" ).toDouble(); m_pagelayout.leftMargin = me.attribute( "left-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.rightMargin = me.attribute( "right-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.topMargin = me.attribute( "top-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.bottomMargin = me.attribute( "bottom-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); } else if ( me.tagName() == "printing-options" ) { m_printingOptions.loadXml( me ); } else if ( me.tagName() == "dockers" ) { KoXmlElement e; forEachElement ( e, me ) { DockWidget *ds = findDocker( e.attribute( "id" ) ); if ( ds ) { ds->loadXml( e ); } } } } return true; } void ViewBase::saveContext( QDomElement &context ) const { QDomElement me = context.ownerDocument().createElement( "page-layout" ); context.appendChild( me ); me.setAttribute( "format", KoPageFormat::formatString( m_pagelayout.format ) ); me.setAttribute( "orientation", m_pagelayout.orientation == KoPageFormat::Portrait ? "portrait" : "landscape" ); me.setAttribute( "width", QString::number(m_pagelayout.width) ); me.setAttribute( "height",QString::number(m_pagelayout. height) ); me.setAttribute( "left-margin", QString::number(m_pagelayout.leftMargin) ); me.setAttribute( "right-margin", QString::number(m_pagelayout.rightMargin) ); me.setAttribute( "top-margin", QString::number(m_pagelayout.topMargin) ); me.setAttribute( "bottom-margin",QString::number( m_pagelayout.bottomMargin) ); m_printingOptions.saveXml( context ); if ( ! m_dockers.isEmpty() ) { QDomElement e = context.ownerDocument().createElement( "dockers" ); context.appendChild( e ); foreach ( const DockWidget *ds, m_dockers ) { ds->saveXml( e ); } } } void ViewBase::addDocker( DockWidget *ds ) { //addAction( "view_docker_list", ds->toggleViewAction() ); m_dockers << ds; } QList ViewBase::dockers() const { return m_dockers; } DockWidget* ViewBase::findDocker( const QString &id ) const { foreach ( DockWidget *ds, m_dockers ) { if ( ds->id == id ) { return ds; } } return 0; } //---------------------- TreeViewPrintingDialog::TreeViewPrintingDialog( ViewBase *view, TreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int TreeViewPrintingDialog::documentLastPage() const { int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int TreeViewPrintingDialog::firstRow( int page ) const { debugPlan<header(); int height = mh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< TreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void TreeViewPrintingDialog::printPage( int page, QPainter &painter ) { m_firstRow = firstRow( page ); QHeaderView *mh = m_tree->header(); int length = mh->length(); int height = mh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); debugPlan<printingOptions(), page, *(m_project) ); } int gap = 8; int pageHeight = pageRect.height() - height; if ( hRect.isValid() ) { pageHeight -= ( hRect.height() + gap ); } if ( fRect.isValid() ) { pageHeight -= ( fRect.height() + gap ); } int rowsPrPage = pageHeight / height; double sx = pageRect.width() > length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 ) { debugPlan<<"No data"; return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( m_firstRow, 0, QModelIndex() ); int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } ++numRows; idx = m_tree->indexBelow( idx ); } } /** * TreeViewBase is a QTreeView adapted for operation by keyboard only and as components in DoubleTreeViewBase. * Note that keyboard navigation and selection behavior may not be fully compliant with QTreeView. * If you use other settings than QAbstractItemView::ExtendedSelection and QAbstractItemView::SelectRows, * you should have a look at the implementation keyPressEvent() and updateSelection(). */ TreeViewBase::TreeViewBase( QWidget *parent ) : QTreeView( parent ), m_arrowKeyNavigation( true ), m_acceptDropsOnView( false ), - m_readWrite( false ) + m_readWrite( false ), + m_handleDrag(true) { + m_dragPixmap = koIcon("application-x-vnd.kde.plan").pixmap(32); setDefaultDropAction( Qt::MoveAction ); setItemDelegate( new ItemDelegate( this ) ); setAlternatingRowColors ( true ); header()->setContextMenuPolicy( Qt::CustomContextMenu ); connect( header(), &QWidget::customContextMenuRequested, this, &TreeViewBase::slotHeaderContextMenuRequested ); } void TreeViewBase::dropEvent( QDropEvent *e ) { debugPlan; QTreeView::dropEvent( e ); } KoPrintJob * TreeViewBase::createPrintJob( ViewBase *parent ) { TreeViewPrintingDialog *dia = new TreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void TreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; if ( model() ) { model()->setData( QModelIndex(), rw, Role::ReadWrite ); } } void TreeViewBase::createItemDelegates( ItemModelBase *model ) { for ( int c = 0; c < model->columnCount(); ++c ) { QAbstractItemDelegate *delegate = model->createDelegate( c, this ); if ( delegate ) { setItemDelegateForColumn( c, delegate ); } } } void TreeViewBase::slotHeaderContextMenuRequested( const QPoint& pos ) { //debugPlan; emit headerContextMenuRequested( header()->mapToGlobal( pos ) ); } void TreeViewBase::setColumnsHidden( const QList &lst ) { //debugPlan< xlst; foreach ( int c, lst ) { if ( c == -1 ) { // hide rest for ( int i = prev+1; i < model()->columnCount(); ++i ) { if ( ! lst.contains( i ) ) { xlst << i; } } break; } xlst << c; prev = c; } for ( int c = 0; c < model()->columnCount(); ++c ) { setColumnHidden( c, xlst.contains( c ) ); } } QModelIndex TreeViewBase::firstColumn( int row, const QModelIndex &parent ) { int s; for ( s = 0; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::lastColumn( int row, const QModelIndex &parent ) { int s; for ( s = header()->count() - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::nextColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::previousColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveLeft ); } QModelIndex TreeViewBase::firstEditable( int row, const QModelIndex &parent ) { QModelIndex index = firstColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::lastEditable( int row, const QModelIndex &parent ) { QModelIndex index = lastColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveLeft ); } // Reimplemented to fix qt bug 160083: Doesn't scroll horizontally. void TreeViewBase::scrollTo(const QModelIndex &index, ScrollHint hint) { //debugPlan<width(); int horizontalOffset = header()->offset(); int horizontalPosition = header()->sectionPosition(index.column()); int cellWidth = header()->sectionSize(index.column()); if (hint == PositionAtCenter) { horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2)); } else { if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition); else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth); } } void TreeViewBase::focusInEvent(QFocusEvent *event) { //debugPlan<reason(); QAbstractScrollArea::focusInEvent(event); //NOTE: not QTreeView if ( event->reason() == Qt::MouseFocusReason ) { return; } QModelIndex curr = currentIndex(); if ( ! curr.isValid() || ! isIndexHidden( curr ) ) { return; } QModelIndex idx = curr; for ( int s = 0; s < header()->count(); ++s) { idx = model()->index( curr.row(), header()->logicalIndex( s ), curr.parent() ); if ( ! isIndexHidden( idx ) ) { selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); scrollTo( idx ); break; } } } /*! \reimp */ void TreeViewBase::keyPressEvent(QKeyEvent *event) { //debugPlan<key()<<","<key()) { case Qt::Key_Right: { QModelIndex nxt = moveCursor( MoveRight, Qt::NoModifier ); if ( nxt.isValid() ) { selectionModel()->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } else { emit moveAfterLastColumn( current ); } event->accept(); return; break; } case Qt::Key_Left: { QModelIndex prv = moveCursor( MoveLeft, Qt::NoModifier ); if ( prv.isValid() ) { selectionModel()->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } else { emit moveBeforeFirstColumn( current ); } event->accept(); return; break; } case Qt::Key_Down: { QModelIndex i = moveCursor( MoveDown, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } case Qt::Key_Up: { QModelIndex i = moveCursor( MoveUp, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } default: break; } } QTreeView::keyPressEvent(event); } void TreeViewBase::updateSelection( const QModelIndex &oldidx, const QModelIndex &newidx, QKeyEvent *event ) { if ( newidx == oldidx || ! newidx.isValid() ) { return; } if ( !hasFocus() && QApplication::focusWidget() == indexWidget(oldidx) ) { setFocus(); } QItemSelectionModel::SelectionFlags command; // NoUpdate on Key movement and Ctrl Qt::KeyboardModifiers modifiers = static_cast(event)->modifiers(); switch (static_cast(event)->key()) { case Qt::Key_Backtab: modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab Q_FALLTHROUGH(); case Qt::Key_Down: case Qt::Key_Up: case Qt::Key_Left: case Qt::Key_Right: if (modifiers & Qt::ControlModifier) command = QItemSelectionModel::NoUpdate; else if (modifiers & Qt::ShiftModifier) command = QItemSelectionModel::Select | selectionBehaviorFlags(); else command = QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags(); break; default: break; } selectionModel()->setCurrentIndex( newidx, command ); } void TreeViewBase::mousePressEvent(QMouseEvent *event) { // If the mouse is pressed outside any item, the current item should be/remain selected QPoint pos = event->pos(); QModelIndex index = indexAt(pos); debugPlan<pos(); if ( ! index.isValid() ) { index = selectionModel()->currentIndex(); if ( index.isValid() && ! selectionModel()->isSelected( index ) ) { pos = visualRect( index ).center(); QMouseEvent e( event->type(), pos, mapToGlobal( pos ), event->button(), event->buttons(), event->modifiers() ); QTreeView::mousePressEvent( &e ); event->setAccepted( e.isAccepted() ); debugPlan<( sender() ); if ( delegate == 0 ) { warnPlan<<"Not a KPlato::ItemDelegate, try standard treatment"<endEditHint(); // Close editor, do nothing else QTreeView::closeEditor( editor, QAbstractItemDelegate::NoHint ); QModelIndex index; switch ( endHint ) { case Delegate::EditLeftItem: index = moveToEditable( currentIndex(), MoveLeft ); break; case Delegate::EditRightItem: index = moveToEditable( currentIndex(), MoveRight ); break; case Delegate::EditDownItem: index = moveToEditable( currentIndex(), MoveDown ); break; case Delegate::EditUpItem: index = moveToEditable( currentIndex(), MoveUp ); break; default: //debugPlan<<"Standard treatment"<setCurrentIndex(persistent, flags); // currentChanged signal would have already started editing if (!(editTriggers() & QAbstractItemView::CurrentChanged)) { edit(persistent); } } } QModelIndex TreeViewBase::moveToEditable( const QModelIndex &index, CursorAction cursorAction ) { QModelIndex ix = index; do { ix = moveCursor( ix, cursorAction ); } while ( ix.isValid() && ! ( model()->flags( ix ) & Qt::ItemIsEditable ) ); //debugPlan<= model()->columnCount(ix.parent()) ) { //debugPlan<columnCount(ix.parent())<index( ix.row(), col, ix.parent() ); } // else Here we could go to the top return ix; } case MoveUp: { // TODO: span // Fetch the index above current. // This should be the previous non-hidden row, same column as current, // that has a column in current.column() ix = indexAbove( current ); while ( ix.isValid() && col >= model()->columnCount(ix.parent()) ) { ix = indexAbove( ix ); } if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MovePrevious: case MoveLeft: { for ( int s = header()->visualIndex( col ) - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MoveNext: case MoveRight: { for ( int s = header()->visualIndex( col ) + 1; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MovePageUp: case MovePageDown: { ix = QTreeView::moveCursor( cursorAction, modifiers ); // Now we are at the correct row, so move to correct column if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MoveHome: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to first row } else { //stay at this row ix = current; } for ( int s = 0; s < header()->count(); ++s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), header()->logicalIndex( s ), ix.parent() ); break; } } return ix; } case MoveEnd: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to last row } else { //stay at this row ix = current; } for ( int s = header()->count() - 1; s >= 0; --s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), logicalIndex, ix.parent() ); break; } } return ix; } default: break; } return ix; } void TreeViewBase::contextMenuEvent ( QContextMenuEvent *event ) { debugPlan<selectedRows(); emit contextMenuRequested( indexAt(event->pos()), event->globalPos(), selectionModel()->selectedRows() ); } void TreeViewBase::slotCurrentChanged( const QModelIndex ¤t, const QModelIndex & ) { if ( current.isValid() ) { scrollTo( current ); } } void TreeViewBase::setModel( QAbstractItemModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } setReadWrite( m_readWrite ); } void TreeViewBase::setSelectionModel( QItemSelectionModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setSelectionModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } } void TreeViewBase::setStretchLastSection( bool mode ) { header()->setStretchLastSection( mode ); } void TreeViewBase::mapToSection( int col, int section ) { header()->moveSection( header()->visualIndex( col ), section ); } int TreeViewBase::section( int col ) const { return header()->visualIndex( col ); } void TreeViewBase::dragMoveEvent(QDragMoveEvent *event) { //debugPlan; if (dragDropMode() == InternalMove && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) { //debugPlan<<"Internal:"<isAccepted(); return; } QTreeView::dragMoveEvent( event ); if ( dropIndicatorPosition() == QAbstractItemView::OnViewport ) { if ( ! m_acceptDropsOnView ) { event->ignore(); } debugPlan<<"On viewport:"<isAccepted(); } else { QModelIndex index = indexAt( event->pos() ); if ( index.isValid() ) { emit dropAllowed( index, dropIndicatorPosition(), event ); } else { event->ignore(); debugPlan<<"Invalid index:"<isAccepted(); } } if ( event->isAccepted() ) { if ( viewport()->cursor().shape() == Qt::ForbiddenCursor ) { viewport()->unsetCursor(); } } else if ( viewport()->cursor().shape() != Qt::ForbiddenCursor ) { viewport()->setCursor( Qt::ForbiddenCursor ); } debugPlan<isAccepted()<cursor().shape(); } QModelIndex TreeViewBase::firstVisibleIndex( const QModelIndex &idx ) const { int count = model()->columnCount(); for ( int c = 0; c < count; ++c ) { if ( ! isColumnHidden( c ) ) { return model()->index( idx.row(), c, model()->parent( idx ) ); } } return QModelIndex(); } bool TreeViewBase::loadContext( const QMetaEnum &map, const KoXmlElement &element, bool expand ) { //debugPlan<setStretchLastSection( (bool)( element.attribute( "stretch-last-column", "1" ).toInt() ) ); KoXmlElement e = element.namedItem( "columns" ).toElement(); if ( ! e.isNull() ) { if ( ! map.isValid() ) { // try numbers debugPlan<<"invalid map"; for ( int i = model()->columnCount() - 1; i >= 0; --i ) { QString s = e.attribute( QString( "column-%1" ).arg( i ), "" ); if ( s == "hidden" ) { hideColumn( i ); } else if ( s == "shown" ) { showColumn( i ); } else debugPlan<columnCount() - 1; i >= 0; --i ) { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( e.hasAttribute( s.arg( i ) ) ) { int index = e.attribute( s.arg( i ), "-1" ).toInt(); if ( index >= 0 && index < h->count() ) { header()->moveSection( h->visualIndex( index ), i ); } } } } else { QMap m; // QMap for ( int i = 0; i < h->count(); ++i ) { QString n = e.attribute( s.arg( i ) ); if ( n.isEmpty() ) { continue; } int col = map.keyToValue( n.toUtf8() ); if ( col >= 0 && col < h->count() ) { m.insert( i, col ); } } for ( QMap::const_iterator it = m.constBegin(); it != m.constEnd(); ++it ) { QString n = e.attribute( s.arg( it.key() ) ); int current = h->visualIndex( it.value() ); header()->moveSection( current, it.key() ); } } } if (expand) { loadExpanded(element); } return true; } void TreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element, bool expand ) const { //debugPlan<stretchLastSection()) ); QDomElement e = element.ownerDocument().createElement( "columns" ); element.appendChild( e ); for ( int i = 0; i < model()->columnCount(); ++i ) { bool h = isColumnHidden( i ); if ( ! map.isValid() ) { debugPlan<<"invalid map"; e.setAttribute( QString( "column-%1" ).arg( i ), h ? "hidden" : "shown" ); } else { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( ! isColumnHidden( h->logicalIndex( i ) ) ) { if ( ! map.isValid() ) { e.setAttribute( QString( "section-%1" ).arg( i ), h->logicalIndex( i ) ); } else { QString n = map.key( h->logicalIndex( i ) ); if ( ! n.isEmpty() ) { e.setAttribute( QString( "section-%1" ).arg( i ), n ); } } } } if (expand) { QDomElement expanded = element.ownerDocument().createElement("expanded"); element.appendChild(expanded); saveExpanded(expanded); } } ItemModelBase *TreeViewBase::itemModel() const { QAbstractItemModel *m = model(); QAbstractProxyModel *p = qobject_cast( m ); while ( p ) { m = p->sourceModel(); p = qobject_cast( m ); } return qobject_cast( m ); } void TreeViewBase::expandRecursive(const QModelIndex &idx, bool xpand) { int rowCount = model()->rowCount(idx); if (rowCount == 0) { return; } xpand ? expand(idx) : collapse(idx); for (int r = 0; r < rowCount; ++r) { QModelIndex i = model()->index(r, 0, idx); Q_ASSERT(i.isValid()); expandRecursive(i, xpand); } } void TreeViewBase::slotExpand() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // expandAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), idx.column(), idx.parent()); } expandRecursive(idx, true); } void TreeViewBase::slotCollapse() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // collapseAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), 0, idx.parent()); } expandRecursive(idx, false); } void TreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_contextMenuIndex = idx; } void TreeViewBase::loadExpanded(const KoXmlElement &element) { // we get here on loadContext() m_loadContextDoc.clear(); KoXmlElement expanded = element.namedItem("expanded").toElement(); if (expanded.isNull()) { return; } KoXml::asQDomElement(m_loadContextDoc, expanded); // FIXME: // if data is dependent on schedule manger // we cannot do anything until schedulemanger is set, // so we wait a bit and hope everything is ok QTimer::singleShot(500, this, &TreeViewBase::doContextExpanded); } void TreeViewBase::expandRecursivly(QDomElement element, const QModelIndex &parent) { if (element.isNull()) { return; } for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName() != "item") { continue; } int childRow = e.attribute("row", "-1").toInt(); if (childRow > -1) { QModelIndex idx = model()->index(childRow, 0, parent); if (idx.isValid()) { setExpanded(idx, true); expandRecursivly(e, idx); } } } } void TreeViewBase::doExpand(QDomDocument &doc) { // we get here on setScheduleManager() m_expandDoc = doc; QTimer::singleShot(0, this, &TreeViewBase::doExpanded); } void TreeViewBase::doContextExpanded() { expandRecursivly(m_loadContextDoc.documentElement()); } void TreeViewBase::doExpanded() { expandRecursivly(m_expandDoc.documentElement()); } void TreeViewBase::saveExpanded(QDomElement &element, const QModelIndex &parent) const { for (int r = 0; r < model()->rowCount(parent); ++r) { QModelIndex idx = model()->index(r, 0, parent); if (isExpanded(idx)) { QDomElement e = element.ownerDocument().createElement("item"); e.setAttribute("row", r); element.appendChild(e); saveExpanded(e, idx); } } } +void TreeViewBase::setHandleDrag(bool state) +{ + m_handleDrag = state; +} + +void TreeViewBase::startDrag(Qt::DropActions supportedActions) +{ + Qt::DropAction defaultDropAction = Qt::IgnoreAction; + if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) { + defaultDropAction = this->defaultDropAction(); + } else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) { + defaultDropAction = Qt::CopyAction; + } + if (m_handleDrag) { + QModelIndexList indexes = selectionModel()->selectedRows(); + if (!indexes.isEmpty()) { + QMimeData *data = model()->mimeData(indexes); + if (!data) { + debugPlan<<"No mimedata"; + return; + } + QDrag *drag = new QDrag(this); + drag->setPixmap(m_dragPixmap); + drag->setMimeData(data); + drag->exec(supportedActions, defaultDropAction); + } + } else { + static_cast(parent())->handleDrag(supportedActions, defaultDropAction); + } +} + +QList TreeViewBase::visualColumns() const +{ + if (!isVisible()) { + return QList(); + } + QMap columns; + for (int i = 0; i < model()->columnCount(); ++i) { + if (!isColumnHidden(i)) { + columns.insert(header()->visualIndex(i), i); + } + } + return columns.values(); +} + +void TreeViewBase::setDragPixmap(const QPixmap &pixmap) +{ + m_dragPixmap = pixmap; +} + +QPixmap TreeViewBase::dragPixmap() const +{ + return m_dragPixmap; +} + +QModelIndexList TreeViewBase::selectedIndexes() const +{ + QModelIndexList viewSelected; + QModelIndexList modelSelected; + if (selectionModel()) + modelSelected = selectionModel()->selectedIndexes(); + for (int i = 0; i < modelSelected.count(); ++i) { + // check that neither the parents nor the index is hidden before we add + QModelIndex index = modelSelected.at(i); + while (index.isValid() && !isIndexHidden(index)) { + int column = index.column(); + index = index.parent(); + if (index.isValid() && column != index.column()) { + index = index.sibling(index.row(), column); + } + } + if (index.isValid()) + continue; + viewSelected.append(modelSelected.at(i)); + } + return viewSelected; +} + //---------------------- DoubleTreeViewPrintingDialog::DoubleTreeViewPrintingDialog( ViewBase *view, DoubleTreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int DoubleTreeViewPrintingDialog::documentLastPage() const { debugPlan<pageLayout().format ); int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int DoubleTreeViewPrintingDialog::firstRow( int page ) const { debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; debugPlan<<"rowsPrPage"< 0 ); int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->masterView()->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<masterView()->indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< DoubleTreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void DoubleTreeViewPrintingDialog::printPage( int page, QPainter &painter ) { debugPlan<pageLayout() ); qreal t, l, b, r; printer().getPageMargins( &l, &t, &r, &b, QPrinter::Point ); debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int length = mh->length() + sh->length(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); Q_ASSERT( model != 0 ); debugPlan< length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } if ( ! sh->isSectionHidden( i ) ) { QRect r = QRect( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 || model->rowCount() == 0 ) { debugPlan<<"No data"; painter.restore(); return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( 0, 0 ); for ( int r = 0; r < m_firstRow && idx.isValid(); ++r ) { idx = m_tree->masterView()->indexBelow( idx ); } int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) && sh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } if ( ! sh->isSectionHidden( i ) ) { QRect r( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } } ++numRows; idx = m_tree->masterView()->indexBelow( idx ); } painter.restore(); } /** * DoubleTreeViewBase is a QSplitter containing two treeviews. * This makes it possible to keep columns visible in one view when scrolling the other view horizontally. */ DoubleTreeViewBase::DoubleTreeViewBase( bool /*mode*/, QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_readWrite( false ), m_mode( false ) { init(); } DoubleTreeViewBase::DoubleTreeViewBase( QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_mode( false ) { init(); } DoubleTreeViewBase::~DoubleTreeViewBase() { } KoPrintJob *DoubleTreeViewBase::createPrintJob( ViewBase *parent ) { DoubleTreeViewPrintingDialog *dia = new DoubleTreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void DoubleTreeViewBase::slotExpand() { m_leftview->slotExpand(); } void DoubleTreeViewBase::slotCollapse() { m_leftview->slotCollapse(); } void DoubleTreeViewBase::setParentsExpanded( const QModelIndex &idx, bool expanded ) { //debugPlan<isExpanded( idx )<isExpanded( idx ); QModelIndex p = model()->parent( idx ); QList lst; while ( p.isValid() ) { lst << p; p = model()->parent( p ); } while ( ! lst.isEmpty() ) { p = lst.takeLast(); m_leftview->setExpanded( p, expanded ); m_rightview->setExpanded( m_rightview->firstVisibleIndex( p ), expanded ); //HACK: qt can't handle that column 0 is hidden! //debugPlan<isExpanded( p )<isExpanded( p ); } } void DoubleTreeViewBase::init() { setOrientation( Qt::Horizontal ); setHandleWidth( 3 ); m_leftview = new TreeViewBase(this); m_leftview->setObjectName("Left view"); + m_leftview->setHandleDrag(false); addWidget( m_leftview ); setStretchFactor( 0, 1 ); m_rightview = new TreeViewBase(this); m_rightview->setObjectName("Right view"); + m_rightview->setHandleDrag(false); addWidget( m_rightview ); setStretchFactor( 1, 1 ); m_leftview->setTreePosition(-1); // always visual index 0 connect( m_leftview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_leftview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotLeftHeaderContextMenuRequested ); connect( m_rightview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_rightview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotRightHeaderContextMenuRequested ); connect( m_leftview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_rightview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_rightview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_leftview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_leftview, &TreeViewBase::moveAfterLastColumn, this, &DoubleTreeViewBase::slotToRightView ); connect( m_rightview, &TreeViewBase::moveBeforeFirstColumn, this, &DoubleTreeViewBase::slotToLeftView ); connect( m_leftview, &TreeViewBase::editAfterLastColumn, this, &DoubleTreeViewBase::slotEditToRightView ); connect( m_rightview, &TreeViewBase::editBeforeFirstColumn, this, &DoubleTreeViewBase::slotEditToLeftView ); connect( m_leftview, &QTreeView::expanded, m_rightview, &QTreeView::expand ); connect( m_leftview, &QTreeView::collapsed, m_rightview, &QTreeView::collapse ); connect( m_rightview, &QTreeView::expanded, m_leftview, &QTreeView::expand ); connect( m_rightview, &QTreeView::collapsed, m_leftview, &QTreeView::collapse ); connect( m_leftview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); connect( m_rightview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); m_actionSplitView = new QAction(koIcon("view-split-left-right"), QString(), this); setViewSplitMode( true ); connect( m_leftview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotLeftSortIndicatorChanged ); connect( m_rightview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotRightSortIndicatorChanged ); } void DoubleTreeViewBase::slotLeftSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_leftview->header()->setSortIndicatorShown( true ); // sorting controlled by left treeview, turn right off m_rightview->header()->setSortIndicatorShown( false ); } void DoubleTreeViewBase::slotRightSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_rightview->header()->setSortIndicatorShown( true ); // sorting controlled by right treeview, turn left off m_leftview->header()->setSortIndicatorShown( false ); } QList DoubleTreeViewBase::expandColumnList( const QList &lst ) const { QList mlst = lst; if ( ! mlst.isEmpty() ) { int v = 0; if ( mlst.last() == -1 && mlst.count() > 1 ) { v = mlst[ mlst.count() - 2 ] + 1; mlst.removeLast(); } for ( int c = v; c < model()->columnCount(); ++c ) { mlst << c; } } return mlst; } void DoubleTreeViewBase::hideColumns( TreeViewBase *view, const QList &list ) { view->setColumnsHidden( list ); } void DoubleTreeViewBase::hideColumns( const QList &masterList, const QList &slaveList ) { m_leftview->setColumnsHidden( masterList ); m_rightview->setColumnsHidden( slaveList ); if ( m_rightview->isHidden() ) { QList mlst = expandColumnList( masterList ); QList slst = expandColumnList( slaveList ); QList lst; for ( int c = 0; c < model()->columnCount(); ++c ) { // only hide columns hidden in *both* views //debugPlan<= 0) && (slst.indexOf( c ) >= 0) ) { lst << c; } } //debugPlan<setColumnsHidden( lst ); } else { setStretchFactors(); } } void DoubleTreeViewBase::slotToRightView( const QModelIndex &index ) { //debugPlan<firstColumn( index.row(), model()->parent( index ) ); m_rightview->setFocus(); if ( nxt.isValid() ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotToLeftView( const QModelIndex &index ) { //debugPlan<lastColumn( index.row(), model()->parent( index ) ); m_leftview->setFocus(); if ( prv.isValid() ) { m_selectionmodel->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotEditToRightView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_rightview->setFocus(); QModelIndex nxt = m_rightview->firstEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_rightview->edit( nxt ); } else { slotToRightView( index ); } } void DoubleTreeViewBase::slotEditToLeftView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_leftview->setFocus(); QModelIndex nxt = m_leftview->lastEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_leftview->edit( nxt ); } else { slotToLeftView( index ); } } void DoubleTreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; m_leftview->setReadWrite( rw ); m_rightview->setReadWrite( rw ); } void DoubleTreeViewBase::closePersistentEditor( const QModelIndex &index ) { m_leftview->closePersistentEditor( index ); m_rightview->closePersistentEditor( index ); } void DoubleTreeViewBase::setModel( QAbstractItemModel *model ) { m_leftview->setModel( model ); m_rightview->setModel( model ); if ( m_selectionmodel ) { disconnect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); disconnect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); } m_selectionmodel = m_leftview->selectionModel(); m_rightview->setSelectionModel( m_selectionmodel ); connect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); connect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); setReadWrite( m_readWrite ); } QAbstractItemModel *DoubleTreeViewBase::model() const { return m_leftview->model(); } void DoubleTreeViewBase::slotSelectionChanged( const QItemSelection &sel, const QItemSelection & ) { emit selectionChanged( sel.indexes() ); } void DoubleTreeViewBase::setSelectionModel( QItemSelectionModel *model ) { m_leftview->setSelectionModel( model ); m_rightview->setSelectionModel( model ); } void DoubleTreeViewBase::setSelectionMode( QAbstractItemView::SelectionMode mode ) { m_leftview->setSelectionMode( mode ); m_rightview->setSelectionMode( mode ); } void DoubleTreeViewBase::setSelectionBehavior( QAbstractItemView::SelectionBehavior mode ) { m_leftview->setSelectionBehavior( mode ); m_rightview->setSelectionBehavior( mode ); } void DoubleTreeViewBase::setItemDelegateForColumn( int col, QAbstractItemDelegate * delegate ) { m_leftview->setItemDelegateForColumn( col, delegate ); m_rightview->setItemDelegateForColumn( col, delegate ); } void DoubleTreeViewBase::createItemDelegates( ItemModelBase *model ) { m_leftview->createItemDelegates( model ); m_rightview->createItemDelegates( model ); } void DoubleTreeViewBase::setEditTriggers( QAbstractItemView::EditTriggers mode ) { m_leftview->setEditTriggers( mode ); m_rightview->setEditTriggers( mode ); } QAbstractItemView::EditTriggers DoubleTreeViewBase::editTriggers() const { return m_leftview->editTriggers(); } void DoubleTreeViewBase::setStretchLastSection( bool mode ) { m_rightview->header()->setStretchLastSection( mode ); if ( m_rightview->isHidden() ) { m_leftview->header()->setStretchLastSection( mode ); } } void DoubleTreeViewBase::edit( const QModelIndex &index ) { if ( ! m_leftview->isColumnHidden( index.column() ) ) { m_leftview->edit( index ); } else if ( ! m_rightview->isHidden() && ! m_rightview->isColumnHidden( index.column() ) ) { m_rightview->edit( index ); } } void DoubleTreeViewBase::setDragDropMode( QAbstractItemView::DragDropMode mode ) { m_leftview->setDragDropMode( mode ); m_rightview->setDragDropMode( mode ); } void DoubleTreeViewBase::setDragDropOverwriteMode( bool mode ) { m_leftview->setDragDropOverwriteMode( mode ); m_rightview->setDragDropOverwriteMode( mode ); } void DoubleTreeViewBase::setDropIndicatorShown( bool mode ) { m_leftview->setDropIndicatorShown( mode ); m_rightview->setDropIndicatorShown( mode ); } void DoubleTreeViewBase::setDragEnabled ( bool mode ) { m_leftview->setDragEnabled( mode ); m_rightview->setDragEnabled( mode ); } void DoubleTreeViewBase::setAcceptDrops( bool mode ) { m_leftview->setAcceptDrops( mode ); m_rightview->setAcceptDrops( mode ); } void DoubleTreeViewBase::setAcceptDropsOnView( bool mode ) { m_leftview->setAcceptDropsOnView( mode ); m_rightview->setAcceptDropsOnView( mode ); } void DoubleTreeViewBase::setDefaultDropAction( Qt::DropAction action ) { m_leftview->setDefaultDropAction( action ); m_rightview->setDefaultDropAction( action ); } void DoubleTreeViewBase::slotRightHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit slaveHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::slotLeftHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit masterHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::setStretchFactors() { int lc = m_leftview->header()->count() - m_leftview->header()->hiddenSectionCount(); int rc = m_rightview->header()->count() - m_rightview->header()->hiddenSectionCount(); setStretchFactor( indexOf( m_rightview ), qMax( 1, qMin( 4, rc / qMax( 1, lc ) ) ) ); //debugPlan<loadContext(map, slave, false); } KoXmlElement master = element.namedItem("master").toElement(); if (!master.isNull()) { m_leftview->loadContext(map, master); } return true; } void DoubleTreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element ) const { QDomElement master = element.ownerDocument().createElement( "master" ); element.appendChild(master); m_leftview->saveContext(map, master); QDomElement slave = element.ownerDocument().createElement( "slave" ); element.appendChild(slave); if (m_rightview->isHidden()) { slave.setAttribute("hidden", "true"); } m_rightview->saveContext(map, slave, false); } void DoubleTreeViewBase::setViewSplitMode( bool split ) { if ( split ) { m_actionSplitView->setText( i18n( "Unsplit View" ) ); m_actionSplitView->setIcon(koIcon("view-close")); } else { m_actionSplitView->setText( i18n( "Split View" ) ); m_actionSplitView->setIcon(koIcon("view-split-left-right")); } if ( m_mode == split ) { return; } m_mode = split; if ( split ) { m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); if ( model() ) { m_rightview->setColumnHidden( 0, true ); m_leftview->resizeColumnToContents( 0 ); for ( int c = 1; c < m_rightview->model()->columnCount(); ++c ) { if ( m_leftview->isColumnHidden( c ) ) { m_rightview->setColumnHidden( c, true ); } else { m_rightview->setColumnHidden( c, false ); m_rightview->mapToSection( c, m_leftview->section( c ) ); m_leftview->setColumnHidden( c, true ); m_rightview->resizeColumnToContents( c ); } } } m_rightview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); m_rightview->show(); } else { m_rightview->hide(); if ( model() ) { int offset = m_rightview->isColumnHidden( 0 ) ? 1 : 0; for ( int c = 0; c < model()->columnCount(); ++c ) { if ( ! m_rightview->isColumnHidden( c ) ) { m_leftview->setColumnHidden( c, false ); m_leftview->mapToSection( c, m_rightview->section( c ) + offset ); m_leftview->resizeColumnToContents( c ); } } } m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } } void DoubleTreeViewBase::setRootIsDecorated ( bool show ) { m_leftview->setRootIsDecorated( show ); m_rightview->setRootIsDecorated( show ); } QModelIndex DoubleTreeViewBase::indexAt( const QPoint &pos ) const { QModelIndex idx = m_leftview->indexAt( pos ); if ( ! idx.isValid() ) { idx = m_rightview->indexAt( pos ); } return idx; } void DoubleTreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_leftview->setContextMenuIndex(idx); m_rightview->setContextMenuIndex(idx); } +void sort(QTreeView *view, QModelIndexList &list) +{ + QModelIndexList i; i << list.takeFirst(); + for (QModelIndex idx = view->indexAbove(i.first()); idx.isValid() && !list.isEmpty(); idx = view->indexAbove(idx)) { + if (list.contains(idx)) { + i.prepend(idx); + list.removeOne(idx); + } + } + for (QModelIndex idx = view->indexBelow(i.last()); idx.isValid() && !list.isEmpty(); idx = view->indexBelow(idx)) { + if (list.contains(idx)) { + i.append(idx); + list.removeOne(idx); + } + } + list = i; +} + +QMimeData *DoubleTreeViewBase::mimeData() const +{ + QModelIndexList rows = selectionModel()->selectedRows(); + sort(m_leftview, rows); + if (rows.isEmpty()) { + debugPlan<<"No rows selected"; + return 0; + } + QList columns;; + columns = m_leftview->visualColumns() + m_rightview->visualColumns(); + QModelIndexList indexes; + for (int r = 0; r < rows.count(); ++r) { + int row = rows.at(r).row(); + const QModelIndex &parent = rows.at(r).parent(); + for (int i = 0; i < columns.count(); ++i) { + indexes << model()->index(row, columns.at(i), parent); + } + } + return model()->mimeData(indexes); +} + +void DoubleTreeViewBase::handleDrag(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction) +{ + QMimeData *data = mimeData(); + if (!data) { + debugPlan<<"No mimedata"; + return; + } + QDrag *drag = new QDrag(this); + drag->setPixmap(m_leftview->dragPixmap()); + drag->setMimeData(data); + Qt::DropAction a = drag->exec(supportedActions, defaultDropAction); +} + +void DoubleTreeViewBase::setDragPixmap(const QPixmap &pixmap) +{ + m_leftview->setDragPixmap(pixmap); +} + +QPixmap DoubleTreeViewBase::dragPixmap() const +{ + return m_leftview->dragPixmap(); +} + +void DoubleTreeViewBase::editCopy() +{ + QMimeData *data = mimeData(); + if (!data) { + debugPlan<<"No mimedata"; + return; + } + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setMimeData(data); +} + } // namespace KPlato diff --git a/src/libs/ui/kptviewbase.h b/src/libs/ui/kptviewbase.h index 9a286802..c31269df 100644 --- a/src/libs/ui/kptviewbase.h +++ b/src/libs/ui/kptviewbase.h @@ -1,683 +1,705 @@ /* This file is part of the KDE project Copyright (C) 2006 -2010 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPTVIEWBASE_H #define KPTVIEWBASE_H #include "planui_export.h" #include "kptitemmodelbase.h" #include "ui_kptprintingheaderfooter.h" #include #include #include #include //#include #include #include #include #include #include #include class QMetaEnum; class QAbstractItemModel; class QDomElement; class QModelIndex; class KoDocument; class KoPrintJob; class KoPart; /// The main namespace namespace KPlato { class Project; class Node; class Resource; class ResourceGroup; class Relation; class Calendar; class ViewBase; class TreeViewBase; class DoubleTreeViewBase; //------------------ class PLANUI_EXPORT DockWidget : public QDockWidget { Q_OBJECT public: DockWidget( ViewBase *v, const QString &identity, const QString &title ); void activate( KoMainWindow *mainWindow ); void deactivate( KoMainWindow *mainWindow ); bool shown() const; bool saveXml( QDomElement &context ) const; void loadXml( const KoXmlElement &context ); const ViewBase *view; /// The view this docker belongs to QString id; /// Docker identity Qt::DockWidgetArea location; /// The area the docker should go when visible bool editor; /// Editor dockers will not be shown in read only mode public Q_SLOTS: void setShown( bool show ); void setLocation( Qt::DockWidgetArea area ); private: bool m_shown; /// The dockers visibility when the view is active }; //------------------ class PLANUI_EXPORT PrintingOptions { public: PrintingOptions() { headerOptions.group = true; headerOptions.project = Qt::Checked; headerOptions.date = Qt::Checked; headerOptions.manager = Qt::Checked; headerOptions.page = Qt::Checked; footerOptions.group = false; footerOptions.project = Qt::Checked; footerOptions.date = Qt::Checked; footerOptions.manager = Qt::Checked; footerOptions.page = Qt::Checked; } ~PrintingOptions() {} bool loadXml( KoXmlElement &element ); void saveXml( QDomElement &element ) const; struct Data { bool group; Qt::CheckState project; Qt::CheckState date; Qt::CheckState manager; Qt::CheckState page; }; struct Data headerOptions; struct Data footerOptions; }; //------------------ class PLANUI_EXPORT PrintingHeaderFooter : public QWidget, public Ui::PrintingHeaderFooter { Q_OBJECT public: explicit PrintingHeaderFooter( const PrintingOptions &opt, QWidget *parent = 0 ); ~PrintingHeaderFooter(); void setOptions( const PrintingOptions &options ); PrintingOptions options() const; Q_SIGNALS: void changed(const KPlato::PrintingOptions&); protected Q_SLOTS: void slotChanged(); private: PrintingOptions m_options; }; //------------------ class PLANUI_EXPORT PrintingDialog : public KoPrintingDialog { Q_OBJECT public: explicit PrintingDialog(ViewBase *view); ~PrintingDialog(); QList createOptionWidgets() const override; // virtual QList shapesOnPage(int); QRect headerRect() const; QRect footerRect() const; void paintHeaderFooter( QPainter &p, const PrintingOptions &options, int pageNumber, const Project &project ); PrintingOptions printingOptions() const; QWidget *createPageLayoutWidget() const; QAbstractPrintDialog::PrintDialogOptions printDialogOptions() const override; Q_SIGNALS: void changed(const KPlato::PrintingOptions &opt); void changed(); public Q_SLOTS: void setPrintingOptions(const KPlato::PrintingOptions &opt); void setPrinterPageLayout( const KoPageLayout &pagelayout ); void startPrinting(KoPrintJob::RemovePolicy removePolicy = DoNotDelete) override; protected: virtual void paint( QPainter &p, const PrintingOptions::Data &options, const QRect &rect, int pageNumber, const Project &project ); int headerFooterHeight( const PrintingOptions::Data &options ) const; void drawRect( QPainter &p, const QRect &r, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge ); protected: ViewBase *m_view; PrintingHeaderFooter *m_widget; int m_textheight; }; class PLANUI_EXPORT ViewActionLists { public: ViewActionLists() : actionOptions( 0 ) {} virtual ~ViewActionLists() {} /// Returns the list of action lists that shall be plugged/unplugged virtual QStringList actionListNames() const { return m_actionListMap.keys(); } /// Returns the list of actions associated with the action list name virtual QList actionList( const QString &name ) const { return m_actionListMap[name]; } /// Add an action to the specified action list void addAction( const QString &list, QAction *action ) { m_actionListMap[list].append( action ); } virtual QList viewlistActionList() const { return m_viewlistActionList; } void addViewlistAction( QAction *action ) { m_viewlistActionList.append( action ); } QList contextActionList() const { return m_contextActionList; } void addContextAction( QAction *action ) { m_contextActionList.append( action ); } protected: /// List of all menu/toolbar actions (used for plug/unplug) QMap > m_actionListMap; /// List of actions that will be shown in the viewlist context menu QList m_viewlistActionList; /// List of actions that will be shown in the views header context menu QList m_contextActionList; // View options context menu QAction *actionOptions; }; /** ViewBase is the baseclass of all sub-views to View. */ class PLANUI_EXPORT ViewBase : public KoView, public ViewActionLists { Q_OBJECT public: enum OptionTypes { OptionExpand = 1, OptionCollapse = 2, OptionPrint = 4, OptionPrintPreview = 8, OptionPrintPdf = 16, OptionPrintConfig = 32, OptionViewConfig = 64, OptionAll = 0xffff }; /// Constructor ViewBase(KoPart *part, KoDocument *doc, QWidget *parent); /// Destructor virtual ~ViewBase(); /// Return the part (document) this view handles KoDocument *part() const; /// Return the page layout used for printing this view virtual KoPageLayout pageLayout() const; /// Return the type of view this is (class name) QString viewType() const { return metaObject()->className(); } /// Returns true if this view or any child widget has focus bool isActive() const; /// Set the project this view shall handle. virtual void setProject( Project *project ); /// Return the project virtual Project *project() const { return m_proj; } /// Return the schedule manager virtual ScheduleManager *scheduleManager() const { return m_schedulemanager; } /// Draw data from current part / project virtual void draw() {} /// Draw data from project. virtual void draw(Project &/*project*/) {} /// Draw changed data from project. virtual void drawChanges(Project &project) { draw(project); } /// Set readWrite mode virtual void updateReadWrite( bool ); bool isReadWrite() const { return m_readWrite; } /// Reimplement if your view handles nodes virtual Node* currentNode() const { return 0; } /// Reimplement if your view handles resources virtual Resource* currentResource() const { return 0; } /// Reimplement if your view handles resource groups virtual ResourceGroup* currentResourceGroup() const { return 0; } /// Reimplement if your view handles calendars virtual Calendar* currentCalendar() const { return 0; } /// Reimplement if your view handles relations virtual Relation *currentRelation() const { return 0; } /// Reimplement if your view handles zoom // virtual KoZoomController *zoomController() const { return 0; } /// Loads context info (printer settings) into this view. virtual bool loadContext( const KoXmlElement &context ); /// Save context info (printer settings) from this view. virtual void saveContext( QDomElement &context ) const; virtual KoPrintJob *createPrintJob(); PrintingOptions printingOptions() const { return m_printingOptions; } static QWidget *createPageLayoutWidget( ViewBase *view ); static PrintingHeaderFooter *createHeaderFooterWidget( ViewBase *view ); void addAction( const QString &list, QAction *action ) { ViewActionLists::addAction( list, action ); } virtual void createDockers() {} void addDocker( DockWidget *ds ); QList dockers() const; DockWidget *findDocker( const QString &id ) const; public Q_SLOTS: void setPrintingOptions(const KPlato::PrintingOptions &opt) { m_printingOptions = opt; } /// Activate/deactivate the gui virtual void setGuiActive( bool activate ); virtual void setScheduleManager(KPlato::ScheduleManager *sm) { m_schedulemanager = sm; } void slotUpdateReadWrite( bool ); virtual void slotHeaderContextMenuRequested( const QPoint &pos ); virtual void slotEditCopy() {} virtual void slotEditCut() {} virtual void slotEditPaste() {} virtual void slotRefreshView() {} void setPageLayout( const KoPageLayout &layout ); Q_SIGNALS: /// Emitted when the gui has been activated or deactivated void guiActivated(KPlato::ViewBase*, bool); /// Request for a context menu popup void requestPopupMenu( const QString&, const QPoint & ); /// Emitted when options are modified void optionsModified(); void projectChanged(KPlato::Project *project); void readWriteChanged( bool ); void expandAll(); void collapseAll(); void openDocument(const QUrl &url); protected Q_SLOTS: virtual void slotOptions() {} virtual void slotOptionsFinished( int result ); protected: void createOptionActions(int actions); bool m_readWrite; PrintingOptions m_printingOptions; Project *m_proj; ScheduleManager *m_schedulemanager; KoPageLayout m_pagelayout; QList m_dockers; }; //------------------ class PLANUI_EXPORT TreeViewPrintingDialog : public PrintingDialog { Q_OBJECT public: TreeViewPrintingDialog( ViewBase *view, TreeViewBase *treeview, Project *project = 0 ); ~TreeViewPrintingDialog() {} virtual int documentFirstPage() const { return 1; } virtual int documentLastPage() const; QList createOptionWidgets() const; protected: virtual void printPage( int pageNumber, QPainter &painter ); int firstRow( int page ) const; private: TreeViewBase *m_tree; Project *m_project; int m_firstRow; }; //----------------- class PLANUI_EXPORT TreeViewBase : public QTreeView { Q_OBJECT public: explicit TreeViewBase( QWidget *parent = 0 ); void setReadWrite( bool rw ); virtual void createItemDelegates( ItemModelBase *model ); void setArrowKeyNavigation( bool on ) { m_arrowKeyNavigation = on; } bool arrowKeyNavigation() const { return m_arrowKeyNavigation; } /// Move move to first visual QModelIndex firstColumn( int row, const QModelIndex &parent ); /// Move move to last visual QModelIndex lastColumn( int row, const QModelIndex &parent ); /// Move from @p current to next item QModelIndex nextColumn( const QModelIndex ¤t ); /// Move from @p current to next item QModelIndex previousColumn( const QModelIndex ¤t ); /// Move to first editable index in @p row with @p parent QModelIndex firstEditable( int row, const QModelIndex &parent ); /// Move to last editable index in @p row with @p parent QModelIndex lastEditable( int row, const QModelIndex &parent ); void setAcceptDropsOnView( bool mode ) { m_acceptDropsOnView = mode; } virtual void setModel( QAbstractItemModel *model ); virtual void setSelectionModel( QItemSelectionModel *model ); void setStretchLastSection( bool ); void mapToSection( int column, int section ); int section( int col ) const; void setColumnsHidden( const QList &list ); /// Loads context info into this view. Reimplement. virtual bool loadContext(const QMetaEnum &map, const KoXmlElement &element, bool expand = true); /// Save context info from this view. Reimplement. virtual void saveContext(const QMetaEnum &map, QDomElement &context , bool expand = true) const; /** Reimplemented to fix qt bug 160083: Doesn't scroll horizontally. Scroll the contents of the tree view until the given model item \a index is visible. The \a hint parameter specifies more precisely where the item should be located after the operation. If any of the parents of the model item are collapsed, they will be expanded to ensure that the model item is visible. */ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible); void setDefaultColumns( const QList &lst ) { m_defaultColumns = lst; } QList defaultColumns() const { return m_defaultColumns; } KoPrintJob *createPrintJob( ViewBase *parent ); QModelIndex firstVisibleIndex( const QModelIndex &idx ) const; ItemModelBase *itemModel() const; void setContextMenuIndex(const QModelIndex &idx); void loadExpanded(const KoXmlElement &element); void saveExpanded(QDomElement &element, const QModelIndex &parent = QModelIndex()) const; void expandRecursivly(QDomElement element, const QModelIndex &parent = QModelIndex()); void doExpand(QDomDocument &doc); + void setHandleDrag(bool state); + QList visualColumns() const; + void setDragPixmap(const QPixmap &pixmap); + QPixmap dragPixmap() const; + + QModelIndexList selection() const { return selectedIndexes(); } public Q_SLOTS: void slotExpand(); void slotCollapse(); Q_SIGNALS: /// Context menu requested from viewport at global position @p pos void contextMenuRequested( const QModelIndex&, const QPoint &pos, const QModelIndexList& ); /// Context menu requested from header at global position @p pos void headerContextMenuRequested( const QPoint &pos ); void moveAfterLastColumn( const QModelIndex & ); void moveBeforeFirstColumn( const QModelIndex & ); void editAfterLastColumn( const QModelIndex & ); void editBeforeFirstColumn( const QModelIndex & ); void dropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); protected: + /// Re-implemented to cater for hidden column 0 + QModelIndexList selectedIndexes() const; void keyPressEvent(QKeyEvent *event); void mousePressEvent( QMouseEvent *event ); virtual void focusInEvent(QFocusEvent *event); + /** Reimplemented from QTreeView to make tab/backtab in editor work reasonably well. Move the cursor in the way described by \a cursorAction, *not* using the information provided by the button \a modifiers. */ QModelIndex moveCursor( CursorAction cursorAction, Qt::KeyboardModifiers modifiers ); /// Move cursor from @p index in direction @p cursorAction. @p modifiers is not used. QModelIndex moveCursor( const QModelIndex &index, CursorAction cursorAction, Qt::KeyboardModifiers = Qt::NoModifier ); /// Move from @p index to next editable item, in direction @p cursorAction. QModelIndex moveToEditable( const QModelIndex &index, CursorAction cursorAction ); void contextMenuEvent ( QContextMenuEvent * event ); void dragMoveEvent(QDragMoveEvent *event); void dropEvent( QDropEvent *e ); void updateSelection( const QModelIndex &oldidx, const QModelIndex &newidx, QKeyEvent *event ); void expandRecursive(const QModelIndex &parent, bool xpand); //Copied from QAbstractItemView inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const { switch (selectionBehavior()) { case QAbstractItemView::SelectRows: return QItemSelectionModel::Rows; case QAbstractItemView::SelectColumns: return QItemSelectionModel::Columns; case QAbstractItemView::SelectItems: default: return QItemSelectionModel::NoUpdate; } } + void startDrag(Qt::DropActions supportedActions); + protected Q_SLOTS: /// Close the @p editor, using sender()->endEditHint(). /// Use @p hint if sender is not of type ItemDelegate. virtual void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); virtual void slotCurrentChanged ( const QModelIndex & current, const QModelIndex & previous ); void slotHeaderContextMenuRequested( const QPoint& ); void doContextExpanded(); void doExpanded(); protected: bool m_arrowKeyNavigation; bool m_acceptDropsOnView; QList m_hideList; bool m_readWrite; QList m_defaultColumns; QPersistentModelIndex m_contextMenuIndex; QDomDocument m_loadContextDoc; QDomDocument m_expandDoc; + + bool m_handleDrag; + QPixmap m_dragPixmap; }; //------------------ class PLANUI_EXPORT DoubleTreeViewPrintingDialog : public PrintingDialog { Q_OBJECT public: DoubleTreeViewPrintingDialog( ViewBase *view, DoubleTreeViewBase *treeview, Project *project ); ~DoubleTreeViewPrintingDialog() {} virtual int documentFirstPage() const { return 1; } virtual int documentLastPage() const; QList createOptionWidgets() const; protected: virtual void printPage( int pageNumber, QPainter &painter ); int firstRow( int page ) const; private: DoubleTreeViewBase *m_tree; Project *m_project; int m_firstRow; }; class PLANUI_EXPORT DoubleTreeViewBase : public QSplitter { Q_OBJECT public: explicit DoubleTreeViewBase( QWidget *parent ); DoubleTreeViewBase( bool mode, QWidget *parent ); ~DoubleTreeViewBase(); void setReadWrite( bool rw ); void closePersistentEditor( const QModelIndex &index ); void setModel( QAbstractItemModel *model ); QAbstractItemModel *model() const; void setArrowKeyNavigation( bool on ) { m_arrowKeyNavigation = on; } bool arrowKeyNavigation() const { return m_arrowKeyNavigation; } QItemSelectionModel *selectionModel() const { return m_selectionmodel; } void setSelectionModel( QItemSelectionModel *model ); void setSelectionMode( QAbstractItemView::SelectionMode mode ); void setSelectionBehavior( QAbstractItemView::SelectionBehavior mode ); virtual void createItemDelegates( ItemModelBase *model ); void setItemDelegateForColumn( int col, QAbstractItemDelegate * delegate ); void setEditTriggers ( QAbstractItemView::EditTriggers ); QAbstractItemView::EditTriggers editTriggers() const; void setAcceptDrops( bool ); void setAcceptDropsOnView( bool ); void setDropIndicatorShown( bool ); void setDragDropMode( QAbstractItemView::DragDropMode mode ); void setDragDropOverwriteMode( bool mode ); void setDragEnabled ( bool mode ); void setDefaultDropAction( Qt::DropAction action ); void setStretchLastSection( bool ); /// Hide columns in the @p hideList, show all other columns. /// If the hideList.last() == -1, the rest of the columns are hidden. void hideColumns( TreeViewBase *view, const QList &hideList ); void hideColumns( const QList &masterList, const QList &slaveList = QList() ); void hideColumn( int col ) { m_leftview->hideColumn( col ); if ( m_rightview ) m_rightview->hideColumn( col ); } void showColumn( int col ) { if ( col == 0 || m_rightview == 0 ) m_leftview->showColumn( col ); else m_rightview->showColumn( col ); } bool isColumnHidden( int col ) const { return m_rightview ? m_rightview->isColumnHidden( col ) : m_leftview->isColumnHidden( col ); } TreeViewBase *masterView() const { return m_leftview; } TreeViewBase *slaveView() const { return m_rightview; } /// Loads context info into this view. Reimplement. virtual bool loadContext( const QMetaEnum &map, const KoXmlElement &element ); /// Save context info from this view. Reimplement. virtual void saveContext( const QMetaEnum &map, QDomElement &context ) const; void setViewSplitMode( bool split ); bool isViewSplit() const { return m_mode; } QAction *actionSplitView() const { return m_actionSplitView; } void setRootIsDecorated ( bool show ); KoPrintJob *createPrintJob( ViewBase *parent ); void setStretchFactors(); QModelIndex indexAt( const QPoint &pos ) const; void setParentsExpanded( const QModelIndex &idx, bool expanded ); void setSortingEnabled( bool on ) { m_leftview->setSortingEnabled( on ); m_rightview->setSortingEnabled( on ); } void sortByColumn( int col, Qt::SortOrder order = Qt::AscendingOrder ) { if ( ! m_leftview->isColumnHidden( col ) || ! m_rightview->isVisible() || m_rightview->isColumnHidden( col ) ) { m_leftview->sortByColumn( col, order ); } else { m_rightview->sortByColumn( col, order ); } } void setContextMenuIndex(const QModelIndex &idx); + void handleDrag(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction); + void setDragPixmap(const QPixmap &pixmap); + QPixmap dragPixmap() const; + + void editCopy(); + + QMimeData *mimeData() const; + Q_SIGNALS: /// Context menu requested from the viewport, pointer over @p index at global position @p pos void contextMenuRequested( const QModelIndex &index, const QPoint& pos, const QModelIndexList& ); /// Context menu requested from master- or slave header at global position @p pos void headerContextMenuRequested( const QPoint &pos ); /// Context menu requested from master header at global position @p pos void masterHeaderContextMenuRequested( const QPoint &pos ); /// Context menu requested from slave header at global position @p pos void slaveHeaderContextMenuRequested( const QPoint &pos ); void currentChanged ( const QModelIndex & current, const QModelIndex & previous ); void selectionChanged( const QModelIndexList& ); void dropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ); public Q_SLOTS: void edit( const QModelIndex &index ); void slotExpand(); void slotCollapse(); protected Q_SLOTS: void slotSelectionChanged( const QItemSelection &sel, const QItemSelection & ); void slotToRightView( const QModelIndex &index ); void slotToLeftView( const QModelIndex &index ); void slotEditToRightView( const QModelIndex &index ); void slotEditToLeftView( const QModelIndex &index ); void slotRightHeaderContextMenuRequested( const QPoint &pos ); void slotLeftHeaderContextMenuRequested( const QPoint &pos ); void slotLeftSortIndicatorChanged( int logicalIndex, Qt::SortOrder order ); void slotRightSortIndicatorChanged( int logicalIndex, Qt::SortOrder order ); protected: void init(); QList expandColumnList( const QList &lst ) const; protected: TreeViewBase *m_leftview; TreeViewBase *m_rightview; QItemSelectionModel *m_selectionmodel; bool m_arrowKeyNavigation; bool m_readWrite; bool m_mode; QAction *m_actionSplitView; }; } // namespace KPlato #endif