1/* This file is part of the KDE project
2 Copyright (C) 2007, 2012 Dag Andersen <danders@get2net.dk>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17* Boston, MA 02110-1301, USA.
18*/
19
20#include "kptrelationmodel.h"
21
22#include "kptglobal.h"
23#include "kptcommonstrings.h"
24#include "kptcommand.h"
25#include "kptduration.h"
26#include "kptproject.h"
27#include "kptnode.h"
28#include "kptrelation.h"
29#include "kptdebug.h"
30
31#include <QAbstractItemModel>
32#include <QModelIndex>
33#include <QWidget>
34
35#include <kaction.h>
36#include <kglobal.h>
37#include <klocale.h>
38#include <ktoggleaction.h>
39#include <kactionmenu.h>
40#include <kstandardaction.h>
41#include <kstandardshortcut.h>
42#include <kaccelgen.h>
43#include <kactioncollection.h>
44
45#include <kdganttglobal.h>
46
47
48namespace KPlato
49{
50
51QVariant RelationModel::parentName( const Relation *r, int role ) const
52{
53 //kDebug(planDbg())<<r<<", "<<role<<endl;
54 switch ( role ) {
55 case Qt::DisplayRole:
56 case Qt::ToolTipRole:
57 case Qt::EditRole:
58 return r->parent()->name();
59 case Qt::TextAlignmentRole:
60 case Qt::StatusTipRole:
61 case Qt::WhatsThisRole:
62 return QVariant();
63 }
64 return QVariant();
65}
66
67QVariant RelationModel::childName( const Relation *r, int role ) const
68{
69 //kDebug(planDbg())<<r<<", "<<role<<endl;
70 switch ( role ) {
71 case Qt::DisplayRole:
72 case Qt::ToolTipRole:
73 case Qt::EditRole:
74 return r->child()->name();
75 case Qt::TextAlignmentRole:
76 case Qt::StatusTipRole:
77 case Qt::WhatsThisRole:
78 return QVariant();
79 }
80 return QVariant();
81}
82
83QVariant RelationModel::type( const Relation *r, int role ) const
84{
85 //kDebug(planDbg())<<r<<", "<<role<<endl;
86 switch ( role ) {
87 case Qt::DisplayRole:
88 case Qt::ToolTipRole:
89 return r->typeToString( true );
90 case Role::EnumList:
91 return r->typeList( true );
92 case Qt::EditRole:
93 case Role::EnumListValue:
94 return (int)r->type();
95 case Qt::TextAlignmentRole:
96 return Qt::AlignCenter;
97 case Qt::StatusTipRole:
98 case Qt::WhatsThisRole:
99 return QVariant();
100 }
101 return QVariant();
102}
103
104QVariant RelationModel::lag( const Relation *r, int role ) const
105{
106 switch ( role ) {
107 case Qt::DisplayRole:
108 case Qt::ToolTipRole: {
109 Duration::Unit unit = Duration::Unit_h;
110 return QVariant(KGlobal::locale()->formatNumber( r->lag().toDouble( unit ), 1 ) + Duration::unitToString( unit, true ));
111 }
112 case Qt::EditRole:
113 return r->lag().toDouble( Duration::Unit_h );
114 case Role::DurationUnit:
115 return static_cast<int>( Duration::Unit_h );
116 case Qt::StatusTipRole:
117 case Qt::WhatsThisRole:
118 return QVariant();
119 }
120 return QVariant();
121}
122
123QVariant RelationModel::data( const Relation *r, int property, int role ) const
124{
125 QVariant result;
126 switch ( property ) {
127 case 0: result = parentName( r, role ); break;
128 case 1: result = childName( r, role ); break;
129 case 2: result = type( r, role ); break;
130 case 3: result = lag( r, role ); break;
131 default:
132 //kDebug(planDbg())<<"Invalid property number: "<<property<<endl;;
133 return result;
134 }
135 return result;
136}
137
138int RelationModel::propertyCount()
139{
140 return 4;
141}
142
143QVariant RelationModel::headerData( int section, int role )
144{
145 if ( role == Qt::DisplayRole ) {
146 switch ( section ) {
147 case 0: return i18n( "Parent" );
148 case 1: return i18n( "Child" );
149 case 2: return i18n( "Type" );
150 case 3: return i18n( "Lag" );
151 default: return QVariant();
152 }
153 }
154 if ( role == Qt::ToolTipRole ) {
155 switch ( section ) {
156 case 0: return ToolTip::relationParent();
157 case 1: return ToolTip::relationChild();
158 case 2: return ToolTip::relationType();
159 case 3: return ToolTip::relationLag();
160 default: return QVariant();
161 }
162 }
163 return QVariant();
164}
165
166//----------------------------
167RelationItemModel::RelationItemModel( QObject *parent )
168 : ItemModelBase( parent ),
169 m_node( 0 ),
170 m_removedRelation( 0 )
171{
172}
173
174RelationItemModel::~RelationItemModel()
175{
176}
177
178void RelationItemModel::slotRelationToBeAdded( Relation *relation, int, int )
179{
180 kDebug(planDbg());
181 if ( m_node == 0 || m_node != relation->child() ) {
182 return;
183 }
184 // relations always appended
185 int row = rowCount();
186 beginInsertRows( QModelIndex(), row, row );
187}
188
189void RelationItemModel::slotRelationAdded( Relation *relation )
190{
191 kDebug(planDbg());
192 if ( m_node == 0 || m_node != relation->child() ) {
193 return;
194 }
195 endInsertRows();
196}
197
198void RelationItemModel::slotRelationToBeRemoved( Relation *relation )
199{
200 if ( m_node == 0 || ! m_node->dependParentNodes().contains( relation ) ) {
201 return;
202 }
203 m_removedRelation = relation;
204 int row = m_node->dependParentNodes().indexOf( relation );
205 kDebug(planDbg())<<row;
206 beginRemoveRows( QModelIndex(), row, row );
207}
208
209void RelationItemModel::slotRelationRemoved( Relation *relation )
210{
211 kDebug(planDbg());
212 if ( m_removedRelation != relation ) {
213 return;
214 }
215 m_removedRelation = 0;
216 endRemoveRows();
217}
218
219void RelationItemModel::slotRelationModified( Relation *relation )
220{
221 kDebug(planDbg());
222 if ( m_node == 0 || ! m_node->dependParentNodes().contains( relation ) ) {
223 return;
224 }
225 int row = m_node->dependParentNodes().indexOf( relation );
226 emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount()-1 ) );
227}
228
229void RelationItemModel::slotNodeToBeRemoved( Node *node )
230{
231 if ( node != m_node ) {
232 return;
233 }
234 setNode( 0 );
235}
236
237void RelationItemModel::slotNodeRemoved( Node *node )
238{
239 Q_UNUSED(node);
240}
241
242void RelationItemModel::slotLayoutChanged()
243{
244 //kDebug(planDbg())<<node->name()<<endl;
245 emit layoutAboutToBeChanged();
246 emit layoutChanged();
247}
248
249void RelationItemModel::setProject( Project *project )
250{
251 if ( m_project ) {
252 disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
253 disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
254
255 disconnect( m_project, SIGNAL(relationToBeAdded(Relation*,int,int)), this, SLOT(slotRelationToBeAdded(Relation*,int,int)) );
256 disconnect( m_project, SIGNAL(relationAdded(Relation*)), this, SLOT(slotRelationAdded(Relation*)) );
257
258 disconnect( m_project, SIGNAL(relationToBeRemoved(Relation*)), this, SLOT(slotRelationToBeRemoved(Relation*)) );
259 disconnect( m_project, SIGNAL(relationRemoved(Relation*)), this, SLOT(slotRelationRemoved(Relation*)) );
260
261 disconnect( m_project, SIGNAL(relationModified(Relation*)), this, SLOT(slotRelationModified(Relation*)) );
262 }
263 m_project = project;
264 if ( project ) {
265 connect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
266 connect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
267
268 connect( m_project, SIGNAL(relationToBeAdded(Relation*,int,int)), this, SLOT(slotRelationToBeAdded(Relation*,int,int)) );
269 connect( m_project, SIGNAL(relationAdded(Relation*)), this, SLOT(slotRelationAdded(Relation*)) );
270
271 connect( m_project, SIGNAL(relationToBeRemoved(Relation*)), this, SLOT(slotRelationToBeRemoved(Relation*)) );
272 connect( m_project, SIGNAL(relationRemoved(Relation*)), this, SLOT(slotRelationRemoved(Relation*)) );
273
274 connect( m_project, SIGNAL(relationModified(Relation*)), this, SLOT(slotRelationModified(Relation*)) );
275 }
276 reset();
277}
278
279void RelationItemModel::setNode( Node *node )
280{
281 m_node = node;
282 reset();
283}
284
285Qt::ItemFlags RelationItemModel::flags( const QModelIndex &index ) const
286{
287 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
288 if ( !index.isValid() ) {
289 if ( m_readWrite ) {
290 flags |= Qt::ItemIsDropEnabled;
291 }
292 return flags;
293 }
294 if ( m_readWrite ) {
295 flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
296 switch ( index.column() ) {
297 case 2: // type
298 flags |= Qt::ItemIsEditable;
299 break;
300 case 3: // lag
301 flags |= Qt::ItemIsEditable;
302 break;
303 default:
304 flags &= ~Qt::ItemIsEditable;
305 break;
306 }
307 }
308 return flags;
309}
310
311
312QModelIndex RelationItemModel::parent( const QModelIndex &/*index*/ ) const
313{
314 return QModelIndex(); // flat model
315}
316
317QModelIndex RelationItemModel::index( int row, int column, const QModelIndex &parent ) const
318{
319 if ( m_project == 0 ) {
320 return QModelIndex();
321 }
322 if ( parent.isValid() ) {
323 return QModelIndex(); // flat model
324 }
325 return createIndex( row, column );
326}
327
328bool RelationItemModel::setType( Relation *r, const QVariant &value, int role )
329{
330 switch ( role ) {
331 case Qt::EditRole:
332 Relation::Type v = Relation::Type( value.toInt() );
333 //kDebug(planDbg())<<v<<r->type();
334 if ( v == r->type() ) {
335 return false;
336 }
337 emit executeCommand( new ModifyRelationTypeCmd( r, v, kundo2_i18n("Modify relation type") ) );
338 return true;
339 }
340 return false;
341}
342
343bool RelationItemModel::setLag( Relation *r, const QVariant &value, int role )
344{
345 switch ( role ) {
346 case Qt::EditRole: {
347 Duration::Unit unit = static_cast<Duration::Unit>( value.toList()[1].toInt() );
348 Duration d( value.toList()[0].toDouble(), unit );
349 kDebug(planDbg())<<value.toList()[0].toDouble()<<","<<unit<<" ->"<<d.toString();
350 if ( d == r->lag() ) {
351 return false;
352 }
353 emit executeCommand( new ModifyRelationLagCmd( r, d, kundo2_i18n( "Modify relation time lag" ) ) );
354 return true;
355 }
356 default:
357 break;
358 }
359 return false;
360}
361
362QVariant RelationItemModel::data( const QModelIndex &index, int role ) const
363{
364 if ( role == Qt::TextAlignmentRole ) {
365 return headerData( index.column(), Qt::Horizontal, role );
366 }
367
368 QVariant result;
369 Relation *r = relation( index );
370 if ( r != 0 ) {
371 result = m_relationmodel.data( r, index.column(), role );
372 }
373 if ( result.isValid() ) {
374 if ( role == Qt::DisplayRole && result.type() == QVariant::String && result.toString().isEmpty()) {
375 // HACK to show focus in empty cells
376 result = ' ';
377 }
378 return result;
379 }
380 return result;
381}
382
383bool RelationItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
384{
385 if ( ! index.isValid() ) {
386 return ItemModelBase::setData( index, value, role );
387 }
388 if ( ( flags(index) & Qt::ItemIsEditable ) == 0 || role != Qt::EditRole ) {
389 return false;
390 }
391 Relation *r = relation( index );
392 switch (index.column()) {
393 case 0: return false;
394 case 1: return false;
395 case 2: return setType( r, value, role );
396 case 3: return setLag( r, value, role );
397 default:
398 qWarning("data: invalid display value column %d", index.column());
399 return false;
400 }
401 return false;
402}
403
404QVariant RelationItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
405{
406 if ( orientation == Qt::Horizontal ) {
407 if ( role == Qt::DisplayRole ) {
408 return m_relationmodel.headerData( section, role );
409 } else if ( role == Qt::TextAlignmentRole ) {
410 switch (section) {
411 case 2: return Qt::AlignCenter;
412 case 3: return Qt::AlignRight;
413 default: return QVariant();
414 }
415 }
416 }
417 if ( role == Qt::ToolTipRole ) {
418 return RelationModel::headerData( section, role );
419 }
420 return ItemModelBase::headerData(section, orientation, role);
421}
422
423QAbstractItemDelegate *RelationItemModel::createDelegate( int column, QWidget *parent ) const
424{
425 switch ( column ) {
426 case 2: return new EnumDelegate( parent );
427 case 3: return new DurationSpinBoxDelegate( parent );
428 default: return 0;
429 }
430 return 0;
431}
432
433int RelationItemModel::columnCount( const QModelIndex &/*parent*/ ) const
434{
435 return m_relationmodel.propertyCount();
436}
437
438int RelationItemModel::rowCount( const QModelIndex &parent ) const
439{
440 if ( m_project == 0 || m_node == 0 || parent.isValid() ) {
441 return 0;
442 }
443 return m_node->numDependParentNodes();
444}
445
446Relation *RelationItemModel::relation( const QModelIndex &index ) const
447{
448 if ( ! index.isValid() || m_node == 0 ) {
449 return 0;
450 }
451 return m_node->dependParentNodes().value( index.row() );
452}
453
454void RelationItemModel::slotNodeChanged( Node *node )
455{
456 Q_UNUSED(node);
457 reset();
458}
459
460
461} //namespace KPlato
462
463#include "kptrelationmodel.moc"
464