1/* This file is part of the KDE project
2 Copyright (C) 2009, 2012 Dag Andersen danders@get2net>
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 "kptresourceallocationmodel.h"
21
22#include "kptcommonstrings.h"
23#include "kptcommand.h"
24#include "kptitemmodelbase.h"
25#include "kptcalendar.h"
26#include "kptduration.h"
27#include "kptnode.h"
28#include "kptproject.h"
29#include "kpttask.h"
30#include "kptresource.h"
31#include "kptdatetime.h"
32#include "kptdebug.h"
33
34#include <QMimeData>
35#include <QObject>
36#include <QStringList>
37
38#include <kaction.h>
39#include <kglobal.h>
40#include <klocale.h>
41#include <kactioncollection.h>
42
43namespace KPlato
44{
45
46//--------------------------------------
47
48ResourceAllocationModel::ResourceAllocationModel( QObject *parent )
49 : QObject( parent ),
50 m_project( 0 ),
51 m_task( 0 )
52{
53}
54
55ResourceAllocationModel::~ResourceAllocationModel()
56{
57}
58
59const QMetaEnum ResourceAllocationModel::columnMap() const
60{
61 return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") );
62}
63
64void ResourceAllocationModel::setProject( Project *project )
65{
66 m_project = project;
67}
68
69void ResourceAllocationModel::setTask( Task *task )
70{
71 m_task = task;
72}
73
74int ResourceAllocationModel::propertyCount() const
75{
76 return columnMap().keyCount();
77}
78
79QVariant ResourceAllocationModel::name( const Resource *res, int role ) const
80{
81 //kDebug(planDbg())<<res->name()<<","<<role;
82 switch ( role ) {
83 case Qt::DisplayRole:
84 case Qt::EditRole:
85 case Qt::ToolTipRole:
86 return res->name();
87 case Qt::StatusTipRole:
88 case Qt::WhatsThisRole:
89 return QVariant();
90 }
91 return QVariant();
92}
93
94QVariant ResourceAllocationModel::name( const ResourceGroup *res, int role ) const
95{
96 //kDebug(planDbg())<<res->name()<<","<<role;
97 switch ( role ) {
98 case Qt::DisplayRole:
99 case Qt::EditRole:
100 case Qt::ToolTipRole:
101 return res->name();
102 break;
103 case Qt::StatusTipRole:
104 case Qt::WhatsThisRole:
105 return QVariant();
106 }
107 return QVariant();
108}
109
110QVariant ResourceAllocationModel::type( const Resource *res, int role ) const
111{
112 switch ( role ) {
113 case Qt::DisplayRole:
114 case Qt::EditRole:
115 case Qt::ToolTipRole:
116 return res->typeToString( true );
117 case Role::EnumList:
118 return res->typeToStringList( true );
119 case Role::EnumListValue:
120 return (int)res->type();
121 case Qt::TextAlignmentRole:
122 return Qt::AlignCenter;
123 case Qt::StatusTipRole:
124 case Qt::WhatsThisRole:
125 return QVariant();
126 }
127 return QVariant();
128}
129
130QVariant ResourceAllocationModel::type( const ResourceGroup *res, int role ) const
131{
132 switch ( role ) {
133 case Qt::DisplayRole:
134 case Qt::EditRole:
135 case Qt::ToolTipRole:
136 return res->typeToString( true );
137 case Role::EnumList:
138 return res->typeToStringList( true );
139 case Role::EnumListValue:
140 return (int)res->type();
141 case Qt::TextAlignmentRole:
142 return Qt::AlignCenter;
143 case Qt::StatusTipRole:
144 case Qt::WhatsThisRole:
145 return QVariant();
146 }
147 return QVariant();
148}
149
150
151QVariant ResourceAllocationModel::allocation( const ResourceGroup *group, const Resource *res, int role ) const
152{
153 if ( m_project == 0 || m_task == 0 ) {
154 return QVariant();
155 }
156 const ResourceGroupRequest *rg = m_task->requests().find( group );
157 const ResourceRequest *rr = 0;
158 if ( rg ) {
159 rr = rg->find( res );
160 }
161 switch ( role ) {
162 case Qt::DisplayRole: {
163 int units = rr ? rr->units() : 0;
164 // xgettext: no-c-format
165 return i18nc( "<value>%", "%1%", units );
166 }
167 case Qt::EditRole:
168 return rr ? rr->units() : 0;;
169 case Qt::ToolTipRole: {
170 int units = rr ? rr->units() : 0;
171 if ( units == 0 ) {
172 return i18nc( "@info:tooltip", "Not allocated" );
173 }
174 // xgettext: no-c-format
175 return i18nc( "@info:tooltip", "Allocated units: %1%", units );
176 }
177 case Qt::TextAlignmentRole:
178 return Qt::AlignCenter;
179 case Qt::StatusTipRole:
180 case Qt::WhatsThisRole:
181 return QVariant();
182 case Role::Minimum:
183 return 0;
184 case Role::Maximum:
185 return 100;
186 case Qt::CheckStateRole:
187 return Qt::Unchecked;
188
189 }
190 return QVariant();
191}
192
193QVariant ResourceAllocationModel::allocation( const ResourceGroup *res, int role ) const
194{
195 if ( m_project == 0 || m_task == 0 ) {
196 return QVariant();
197 }
198 const ResourceGroupRequest *req = m_task->requests().find( res );
199 switch ( role ) {
200 case Qt::DisplayRole:
201 case Qt::EditRole:
202 return req ? req->units() : 0;
203 case Qt::ToolTipRole:
204 return QVariant();
205 case Qt::TextAlignmentRole:
206 return Qt::AlignCenter;
207 case Qt::StatusTipRole:
208 case Qt::WhatsThisRole:
209 return QVariant();
210 case Role::Minimum:
211 return 0;
212 case Role::Maximum:
213 return res->numResources();
214 }
215 return QVariant();
216}
217
218QVariant ResourceAllocationModel::maximum( const Resource *res, int role ) const
219{
220 switch ( role ) {
221 case Qt::DisplayRole:
222 // xgettext: no-c-format
223 return i18nc( "<value>%", "%1%", res->units() );
224 case Qt::EditRole:
225 return res->units();
226 case Qt::ToolTipRole:
227 // xgettext: no-c-format
228 return i18n( "Maximum units available: %1%", res->units() );
229 case Qt::TextAlignmentRole:
230 return Qt::AlignCenter;
231 case Qt::StatusTipRole:
232 case Qt::WhatsThisRole:
233 return QVariant();
234 }
235 return QVariant();
236}
237
238QVariant ResourceAllocationModel::required( const Resource *res, int role ) const
239{
240 switch ( role ) {
241 case Qt::DisplayRole: {
242 QStringList lst;
243 foreach ( Resource *r, res->requiredResources() ) {
244 lst << r->name();
245 }
246 return lst.join( "," );
247 }
248 case Qt::EditRole:
249 return QVariant();//Not used
250 case Qt::ToolTipRole:
251 return QVariant();
252 case Qt::TextAlignmentRole:
253 return Qt::AlignCenter;
254 case Qt::StatusTipRole:
255 return QVariant();
256 case Qt::WhatsThisRole:
257 return i18nc( "@info:whatsthis", "<title>Required Resources</title>"
258 "<para>A working resource can be assigned to one or more required resources."
259 " A required resource is a material resource that the working resource depends on"
260 " in order to do the work.</para>"
261 "<para>To be able to use a material resource as a required resource, the material resource"
262 " must be part of a group of type <emphasis>Material</emphasis>.</para>" );
263 }
264 return QVariant();
265}
266
267QVariant ResourceAllocationModel::maximum( const ResourceGroup *res, int role ) const
268{
269 switch ( role ) {
270 case Qt::DisplayRole:
271 case Qt::EditRole:
272 return res->numResources();
273 case Qt::ToolTipRole:
274 return i18np( "There is %1 resource available in this group", "There are %1 resources available in this group", res->numResources() );
275 case Qt::TextAlignmentRole:
276 return Qt::AlignCenter;
277 case Qt::StatusTipRole:
278 case Qt::WhatsThisRole:
279 return QVariant();
280 }
281 return QVariant();
282}
283
284QVariant ResourceAllocationModel::data( const ResourceGroup *group, const Resource *resource, int property, int role ) const
285{
286 QVariant result;
287 if ( resource == 0 ) {
288 return result;
289 }
290 switch ( property ) {
291 case RequestName: result = name( resource, role ); break;
292 case RequestType: result = type( resource, role ); break;
293 case RequestAllocation: result = allocation( group, resource, role ); break;
294 case RequestMaximum: result = maximum( resource, role ); break;
295 case RequestRequired: result = required( resource, role ); break;
296 default:
297 kDebug(planDbg())<<"data: invalid display value: property="<<property;
298 break;
299 }
300 return result;
301}
302
303QVariant ResourceAllocationModel::data( const ResourceGroup *group, int property, int role ) const
304{
305 QVariant result;
306 if ( group == 0 ) {
307 return result;
308 }
309 switch ( property ) {
310 case RequestName: result = name( group, role ); break;
311 case RequestType: result = type( group, role ); break;
312 case RequestAllocation: result = allocation( group, role ); break;
313 case RequestMaximum: result = maximum( group, role ); break;
314 default:
315 if ( role == Qt::DisplayRole ) {
316 if ( property < propertyCount() ) {
317 result = QString();
318 } else {
319 kDebug(planDbg())<<"data: invalid display value column"<<property;;
320 return QVariant();
321 }
322 }
323 break;
324 }
325 return result;
326}
327
328
329QVariant ResourceAllocationModel::headerData( int section, int role )
330{
331 if ( role == Qt::DisplayRole ) {
332 switch ( section ) {
333 case RequestName: return i18n( "Name" );
334 case RequestType: return i18n( "Type" );
335 case RequestAllocation: return i18n( "Allocation" );
336 case RequestMaximum: return i18nc( "@title:column", "Available" );
337 case RequestRequired: return i18nc( "@title:column", "Required Resources" );
338 default: return QVariant();
339 }
340 } else if ( role == Qt::TextAlignmentRole ) {
341 switch (section) {
342 case 0: return QVariant();
343 default: return Qt::AlignCenter;
344 }
345 } else if ( role == Qt::ToolTipRole ) {
346 switch ( section ) {
347 case RequestName: return ToolTip::resourceName();
348 case RequestType: return ToolTip::resourceType();
349 case RequestAllocation: return i18n( "Resource allocation" );
350 case RequestMaximum: return i18nc( "@info:tootip", "Available resources or resource units" );
351 case RequestRequired: return i18nc( "@info:tootip", "Required material resources" );
352 default: return QVariant();
353 }
354 } else if ( role == Qt::WhatsThisRole ) {
355 switch ( section ) {
356 case RequestRequired:
357 return i18nc( "@info:whatsthis", "<title>Required Resources</title>"
358 "<para>A working resource can be assigned to one or more required resources."
359 " A required resource is a material resource that the working resource depends on"
360 " in order to do the work.</para>"
361 "<para>To be able to use a material resource as a required resource, the material resource"
362 " must be part of a group of type Material.</para>" );
363 default: return QVariant();
364 }
365 }
366 return QVariant();
367}
368//--------------------------------------
369
370ResourceAllocationItemModel::ResourceAllocationItemModel( QObject *parent )
371 : ItemModelBase( parent )
372{
373}
374
375ResourceAllocationItemModel::~ResourceAllocationItemModel()
376{
377}
378
379void ResourceAllocationItemModel::slotResourceToBeInserted( const ResourceGroup *group, int row )
380{
381 //kDebug(planDbg())<<group->name()<<","<<row;
382 beginInsertRows( index( group ), row, row );
383}
384
385void ResourceAllocationItemModel::slotResourceInserted( const Resource */*resource */)
386{
387 //kDebug(planDbg())<<resource->name();
388 endInsertRows();
389 emit layoutChanged(); //HACK to make the right view react! Bug in qt?
390}
391
392void ResourceAllocationItemModel::slotResourceToBeRemoved( const Resource *resource )
393{
394 //kDebug(planDbg())<<resource->name();
395 int row = index( resource ).row();
396 beginRemoveRows( index( resource->parentGroup() ), row, row );
397}
398
399void ResourceAllocationItemModel::slotResourceRemoved( const Resource */*resource */)
400{
401 //kDebug(planDbg())<<resource->name();
402 endRemoveRows();
403}
404
405void ResourceAllocationItemModel::slotResourceGroupToBeInserted( const ResourceGroup */*group*/, int row )
406{
407 //kDebug(planDbg())<<group->name();
408 beginInsertRows( QModelIndex(), row, row );
409}
410
411void ResourceAllocationItemModel::slotResourceGroupInserted( const ResourceGroup */*group */)
412{
413 //kDebug(planDbg())<<group->name();
414 endInsertRows();
415}
416
417void ResourceAllocationItemModel::slotResourceGroupToBeRemoved( const ResourceGroup *group )
418{
419 //kDebug(planDbg())<<group->name();
420 int row = index( group ).row();
421 beginRemoveRows( QModelIndex(), row, row );
422}
423
424void ResourceAllocationItemModel::slotResourceGroupRemoved( const ResourceGroup */*group */)
425{
426 //kDebug(planDbg())<<group->name();
427 endRemoveRows();
428}
429
430void ResourceAllocationItemModel::setProject( Project *project )
431{
432 if ( m_project ) {
433 disconnect( m_project, SIGNAL(resourceChanged(Resource*)), this, SLOT(slotResourceChanged(Resource*)) );
434 disconnect( m_project, SIGNAL(resourceGroupChanged(ResourceGroup*)), this, SLOT(slotResourceGroupChanged(ResourceGroup*)) );
435
436 disconnect( m_project, SIGNAL(resourceGroupToBeAdded(const ResourceGroup*,int)), this, SLOT(slotResourceGroupToBeInserted(const ResourceGroup*,int)) );
437
438 disconnect( m_project, SIGNAL(resourceGroupToBeRemoved(const ResourceGroup*)), this, SLOT(slotResourceGroupToBeRemoved(const ResourceGroup*)) );
439
440 disconnect( m_project, SIGNAL(resourceToBeAdded(const ResourceGroup*,int)), this, SLOT(slotResourceToBeInserted(const ResourceGroup*,int)) );
441
442 disconnect( m_project, SIGNAL(resourceToBeRemoved(const Resource*)), this, SLOT(slotResourceToBeRemoved(const Resource*)) );
443
444 disconnect( m_project, SIGNAL(resourceGroupAdded(const ResourceGroup*)), this, SLOT(slotResourceGroupInserted(const ResourceGroup*)) );
445
446 disconnect( m_project, SIGNAL(resourceGroupRemoved(const ResourceGroup*)), this, SLOT(slotResourceGroupRemoved(const ResourceGroup*)) );
447
448 disconnect( m_project, SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotResourceInserted(const Resource*)) );
449
450 disconnect( m_project, SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotResourceRemoved(const Resource*)) );
451
452 }
453 m_project = project;
454 if ( m_project ) {
455 connect( m_project, SIGNAL(resourceChanged(Resource*)), this, SLOT(slotResourceChanged(Resource*)) );
456 connect( m_project, SIGNAL(resourceGroupChanged(ResourceGroup*)), this, SLOT(slotResourceGroupChanged(ResourceGroup*)) );
457
458 connect( m_project, SIGNAL(resourceGroupToBeAdded(const ResourceGroup*,int)), this, SLOT(slotResourceGroupToBeInserted(const ResourceGroup*,int)) );
459
460 connect( m_project, SIGNAL(resourceGroupToBeRemoved(const ResourceGroup*)), this, SLOT(slotResourceGroupToBeRemoved(const ResourceGroup*)) );
461
462 connect( m_project, SIGNAL(resourceToBeAdded(const ResourceGroup*,int)), this, SLOT(slotResourceToBeInserted(const ResourceGroup*,int)) );
463
464 connect( m_project, SIGNAL(resourceToBeRemoved(const Resource*)), this, SLOT(slotResourceToBeRemoved(const Resource*)) );
465
466 connect( m_project, SIGNAL(resourceGroupAdded(const ResourceGroup*)), this, SLOT(slotResourceGroupInserted(const ResourceGroup*)) );
467
468 connect( m_project, SIGNAL(resourceGroupRemoved(const ResourceGroup*)), this, SLOT(slotResourceGroupRemoved(const ResourceGroup*)) );
469
470 connect( m_project, SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotResourceInserted(const Resource*)) );
471
472 connect( m_project, SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotResourceRemoved(const Resource*)) );
473
474 }
475 m_model.setProject( m_project );
476}
477
478void ResourceAllocationItemModel::setTask( Task *task )
479{
480 if ( task == m_model.task() ) {
481 return;
482 }
483 if ( m_model.task() == 0 ) {
484 filldata( task );
485 m_model.setTask( task );
486 reset();
487 return;
488 }
489 if ( task ) {
490 emit layoutAboutToBeChanged();
491 filldata( task );
492 m_model.setTask( task );
493 emit layoutChanged();
494 }
495}
496
497void ResourceAllocationItemModel::filldata( Task *task )
498{
499 qDeleteAll( m_resourceCache );
500 m_resourceCache.clear();
501 qDeleteAll( m_groupCache );
502 m_groupCache.clear();
503 m_requiredChecked.clear();
504 if ( m_project && task ) {
505 foreach ( const ResourceGroup *g, m_project->resourceGroups() ) {
506 const ResourceGroupRequest *gr = task->requests().find( g );
507 if ( gr ) {
508 m_groupCache[ g ] = new ResourceGroupRequest( *gr );
509 }
510 }
511 foreach ( const Resource *r, m_project->resourceList() ) {
512 const ResourceRequest *rr = task->requests().find( r );
513 if ( rr ) {
514 m_resourceCache[ r ] = new ResourceRequest( *rr );
515 if ( ! m_resourceCache[ r ]->requiredResources().isEmpty() ) {
516 m_requiredChecked[ r ] = Qt::Checked;
517 }
518 }
519 }
520 }
521}
522
523bool ResourceAllocationItemModel::hasMaterialResources() const
524{
525 if ( ! m_project ) {
526 return false;
527 }
528 foreach ( const ResourceGroup *g, m_project->resourceGroups() ) {
529 if ( g->type() == ResourceGroup::Type_Material ) {
530 foreach ( const Resource *r, g->resources() ) {
531 if ( r->type() == Resource::Type_Material ) {
532 return true;
533 }
534 }
535 }
536 }
537 return false;
538}
539
540Qt::ItemFlags ResourceAllocationItemModel::flags( const QModelIndex &index ) const
541{
542 Qt::ItemFlags flags = ItemModelBase::flags( index );
543 if ( !m_readWrite ) {
544 //kDebug(planDbg())<<"read only"<<flags;
545 return flags &= ~Qt::ItemIsEditable;
546 }
547 if ( !index.isValid() ) {
548 //kDebug(planDbg())<<"invalid"<<flags;
549 return flags;
550 }
551 switch ( index.column() ) {
552 case ResourceAllocationModel::RequestAllocation:
553 flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
554 break;
555 case ResourceAllocationModel::RequestRequired: {
556 Resource *r = resource( index );
557 if ( r && r->type() != Resource::Type_Work ) {
558 flags &= ~( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
559 } else if ( m_resourceCache.contains( r ) && m_resourceCache[ r ]->units() > 0 ) {
560 flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
561 if ( ! hasMaterialResources() ) {
562 flags &= ~Qt::ItemIsEnabled;
563 }
564 }
565 break;
566 }
567 default:
568 flags &= ~Qt::ItemIsEditable;
569 break;
570 }
571 return flags;
572}
573
574
575QModelIndex ResourceAllocationItemModel::parent( const QModelIndex &index ) const
576{
577 if ( !index.isValid() || m_project == 0 ) {
578 return QModelIndex();
579 }
580 //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column();
581
582 Resource *r = qobject_cast<Resource*>( object( index ) );
583 if ( r && r->parentGroup() ) {
584 // only resources have parent
585 int row = m_project->indexOf( r->parentGroup() );
586 return createIndex( row, 0, r->parentGroup() );
587 }
588
589 return QModelIndex();
590}
591
592QModelIndex ResourceAllocationItemModel::index( int row, int column, const QModelIndex &parent ) const
593{
594 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
595 return QModelIndex();
596 }
597 if ( ! parent.isValid() ) {
598 if ( row < m_project->numResourceGroups() ) {
599 return createIndex( row, column, m_project->resourceGroupAt( row ) );
600 }
601 return QModelIndex();
602 }
603 QObject *p = object( parent );
604 ResourceGroup *g = qobject_cast<ResourceGroup*>( p );
605 if ( g ) {
606 if ( row < g->numResources() ) {
607 return createIndex( row, column, g->resourceAt( row ) );
608 }
609 return QModelIndex();
610 }
611 return QModelIndex();
612}
613
614QModelIndex ResourceAllocationItemModel::index( const Resource *resource ) const
615{
616 if ( m_project == 0 || resource == 0 ) {
617 return QModelIndex();
618 }
619 Resource *r = const_cast<Resource*>(resource);
620 int row = -1;
621 ResourceGroup *par = r->parentGroup();
622 if ( par ) {
623 row = par->indexOf( r );
624 return createIndex( row, 0, r );
625 }
626 return QModelIndex();
627}
628
629QModelIndex ResourceAllocationItemModel::index( const ResourceGroup *group ) const
630{
631 if ( m_project == 0 || group == 0 ) {
632 return QModelIndex();
633 }
634 ResourceGroup *g = const_cast<ResourceGroup*>(group);
635 int row = m_project->indexOf( g );
636 return createIndex( row, 0, g );
637
638}
639
640int ResourceAllocationItemModel::columnCount( const QModelIndex &/*parent*/ ) const
641{
642 return m_model.propertyCount();
643}
644
645int ResourceAllocationItemModel::rowCount( const QModelIndex &parent ) const
646{
647 if ( m_project == 0 || m_model.task() == 0 ) {
648 return 0;
649 }
650 if ( ! parent.isValid() ) {
651 return m_project->numResourceGroups();
652 }
653 QObject *p = object( parent );
654 ResourceGroup *g = qobject_cast<ResourceGroup*>( p );
655 if ( g ) {
656 return g->numResources();
657 }
658 return 0;
659}
660
661QVariant ResourceAllocationItemModel::allocation( const ResourceGroup *group, const Resource *res, int role ) const
662{
663 if ( m_model.task() == 0 ) {
664 return QVariant();
665 }
666 if ( ! m_resourceCache.contains( res ) ) {
667 if ( role == Qt::EditRole ) {
668 ResourceRequest *req = m_model.task()->requests().find( res );
669 if ( req == 0 ) {
670 req = new ResourceRequest( const_cast<Resource*>( res ), 0 );
671 }
672 const_cast<ResourceAllocationItemModel*>( this )->m_resourceCache.insert( res, req );
673 return req->units();
674 }
675 return m_model.allocation( group, res, role );
676 }
677 switch ( role ) {
678 case Qt::DisplayRole: {
679 // xgettext: no-c-format
680 return i18nc( "<value>%", "%1%", m_resourceCache[ res ]->units() );
681 }
682 case Qt::EditRole:
683 return m_resourceCache[ res ]->units();
684 case Qt::ToolTipRole: {
685 if ( res->units() == 0 ) {
686 return i18nc( "@info:tooltip", "Not allocated" );
687 }
688 return i18nc( "@info:tooltip", "%1 allocated out of %2 available", allocation( group, res, Qt::DisplayRole ).toString(), m_model.maximum( res, Qt::DisplayRole ).toString() );
689 }
690 case Qt::CheckStateRole:
691 return m_resourceCache[ res ]->units() == 0 ? Qt::Unchecked : Qt::Checked;
692 default:
693 return m_model.allocation( group, res, role );
694 }
695 return QVariant();
696}
697
698int ResourceAllocationItemModel::requestedResources( const ResourceGroup *res ) const
699{
700 int c = 0;
701 foreach ( const Resource *r, res->resources() ) {
702 if ( m_resourceCache.contains( r ) && m_resourceCache[ r ]->units() > 0 ) {
703 ++c;
704 }
705 }
706 return c;
707}
708
709QVariant ResourceAllocationItemModel::allocation( const ResourceGroup *res, int role ) const
710{
711 if ( m_model.task() == 0 ) {
712 return QVariant();
713 }
714 if ( ! m_groupCache.contains( res ) ) {
715 return m_model.allocation( res, role );
716 }
717 switch ( role ) {
718 case Qt::DisplayRole:
719 return QString(" %1 (%2)" )
720 .arg( qMax( m_groupCache[ res ]->units(), allocation( res, Role::Minimum ).toInt() ) )
721 .arg(requestedResources( res ) );
722 case Qt::EditRole:
723 return qMax( m_groupCache[ res ]->units(), allocation( res, Role::Minimum ).toInt() );
724 case Qt::ToolTipRole: {
725 QString s1 = i18ncp( "@info:tooltip",
726 "%1 resource requested for dynamic allocation",
727 "%1 resources requested for dynamic allocation",
728 allocation( res, Qt::EditRole ).toInt() );
729 QString s2 = i18ncp( "@info:tooltip",
730 "%1 resource allocated",
731 "%1 resources allocated",
732 requestedResources( res ) );
733
734 return i18nc( "@info:tooltip", "%1<nl/>%2", s1, s2 );
735 }
736 case Qt::WhatsThisRole: {
737 return i18nc( "@info:whatsthis",
738 "<title>Group allocations</title>"
739 "<para>You can allocate a number of resources from a group and let"
740 " the scheduler select from the available resources at the time of scheduling.</para>"
741 " These dynamically allocated resources will be in addition to any resource you have allocated specifically." );
742 }
743 case Role::Minimum: {
744 return 0;
745 }
746 case Role::Maximum: {
747 return res->numResources() - requestedResources( res );
748 }
749 default:
750 return m_model.allocation( res, role );
751 }
752 return QVariant();
753}
754
755bool ResourceAllocationItemModel::setAllocation( Resource *res, const QVariant &value, int role )
756{
757 switch ( role ) {
758 case Qt::EditRole: {
759 m_resourceCache[ res ]->setUnits( value.toInt() );
760 QModelIndex idx = index( res->parentGroup() );
761 emit dataChanged( index( idx.row(), 0, QModelIndex() ), index( idx.row(), columnCount() - 1, QModelIndex() ) );
762 return true;
763 }
764 case Qt::CheckStateRole: {
765 if ( ! m_resourceCache.contains( res ) ) {
766 m_resourceCache[ res ] = new ResourceRequest( res, 0 );
767 }
768 if ( m_resourceCache[ res ]->units() == 0 ) {
769 m_resourceCache[ res ]->setUnits( 100 );
770 ResourceGroup *g = res->parentGroup();
771 if ( m_groupCache.contains( g ) ) {
772 ResourceGroupRequest *gr = m_groupCache[ g ];
773 if ( gr->units() + requestedResources( g ) > g->numResources() ) {
774 gr->setUnits( gr->units() - 1 );
775 }
776 }
777 } else {
778 m_resourceCache[ res ]->setUnits( 0 );
779 }
780 QModelIndex idx = index( res->parentGroup() );
781 emit dataChanged( index( idx.row(), 0, QModelIndex() ), index( idx.row(), columnCount() - 1, QModelIndex() ) );
782 return true;
783 }
784 }
785 return false;
786}
787
788bool ResourceAllocationItemModel::setAllocation( ResourceGroup *res, const QVariant &value, int role )
789{
790 switch ( role ) {
791 case Qt::EditRole:
792 if ( ! m_groupCache.contains( res ) ) {
793 m_groupCache[ res ] = new ResourceGroupRequest( res, 0 );
794 }
795 m_groupCache[ res ]->setUnits( value.toInt() );
796 emit dataChanged( index( res ), index( res ) );
797 return true;
798 }
799 return false;
800}
801
802QVariant ResourceAllocationItemModel::maximum( const ResourceGroup *res, int role ) const
803{
804 switch ( role ) {
805 case Qt::DisplayRole: {
806 int c = res->numResources() - requestedResources( res );
807 if ( m_groupCache.contains( res ) ) {
808 c -= m_groupCache[ res ]->units();
809 }
810 return i18nc( "1: free resources, 2: number of resources", "%1 of %2", c, res->numResources() );
811 }
812 case Qt::ToolTipRole:
813 return i18ncp( "@info:tooltip", "There is %1 resource available in this group", "There are %1 resources available in this group", res->numResources() );
814 default:
815 return m_model.maximum( res, role );
816 }
817 return QVariant();
818}
819
820QVariant ResourceAllocationItemModel::required( const QModelIndex &idx, int role ) const
821{
822 if ( m_model.task() == 0 ) {
823 return QVariant();
824 }
825 Resource *res = resource( idx );
826 if ( res == 0 ) {
827 return QVariant();
828 }
829 switch ( role ) {
830 case Qt::DisplayRole: {
831 if ( res->type() == Resource::Type_Work ) {
832 QStringList lst;
833 if ( m_requiredChecked[ res ] ) {
834 foreach ( const Resource *r, required( idx ) ) {
835 lst << r->name();
836 }
837 }
838 return lst.isEmpty() ? i18n( "None" ) : lst.join( "," );
839 }
840 break;
841 }
842 case Qt::EditRole: break;
843 case Qt::ToolTipRole:
844 switch ( res->type() ) {
845 case Resource::Type_Work: {
846 if ( ! hasMaterialResources() ) {
847 return i18nc( "@info:tooltip", "No material resources available" );
848 }
849 QStringList lst;
850 if ( m_requiredChecked[ res ] ) {
851 foreach ( const Resource *r, required( idx ) ) {
852 lst << r->name();
853 }
854 }
855 return lst.isEmpty() ? i18nc( "@info:tooltip", "No required resources" ) : lst.join( "\n" );
856 }
857 case Resource::Type_Material:
858 return i18nc( "@info:tooltip", "Material resources cannot have required resources" );
859 case Resource::Type_Team:
860 return i18nc( "@info:tooltip", "Team resources cannot have required resources" );
861 }
862 break;
863 case Qt::CheckStateRole:
864 if ( res->type() == Resource::Type_Work ) {
865 return m_requiredChecked[ res ];
866 }
867 break;
868 default:
869 return m_model.required( res, role );
870 }
871 return QVariant();
872}
873
874bool ResourceAllocationItemModel::setRequired( const QModelIndex &idx, const QVariant &value, int role )
875{
876 Resource *res = resource( idx );
877 if ( res == 0 ) {
878 return false;
879 }
880 switch ( role ) {
881 case Qt::CheckStateRole:
882 m_requiredChecked[ res ] = value.toInt();
883 if ( value.toInt() == Qt::Unchecked ) {
884 m_resourceCache[ res ]->setRequiredResources( QList<Resource*>() );
885 }
886 emit dataChanged( idx, idx );
887 return true;
888 }
889 return false;
890}
891
892QVariant ResourceAllocationItemModel::notUsed( const ResourceGroup *, int role ) const
893{
894 switch ( role ) {
895 case Qt::DisplayRole:
896 return QString(" ");
897 case Qt::TextAlignmentRole:
898 return Qt::AlignCenter;
899 case Qt::EditRole:
900 case Qt::ToolTipRole:
901 case Qt::StatusTipRole:
902 case Qt::WhatsThisRole:
903 return QVariant();
904 }
905 return QVariant();
906}
907
908QVariant ResourceAllocationItemModel::data( const QModelIndex &index, int role ) const
909{
910 QVariant result;
911 QObject *obj = object( index );
912 if ( obj == 0 ) {
913 return QVariant();
914 }
915 if ( role == Qt::TextAlignmentRole ) {
916 // use same alignment as in header (headers always horizontal)
917 return headerData( index.column(), Qt::Horizontal, role );
918 }
919 Resource *r = qobject_cast<Resource*>( obj );
920 if ( r ) {
921 if ( index.column() == ResourceAllocationModel::RequestAllocation ) {
922 return allocation( r->parentGroup(), r, role );
923 }
924 if ( index.column() == ResourceAllocationModel::RequestRequired ) {
925 return required( index, role );
926 }
927 result = m_model.data( r->parentGroup(), r, index.column(), role );
928 } else {
929 ResourceGroup *g = qobject_cast<ResourceGroup*>( obj );
930 if ( g ) {
931 switch ( index.column() ) {
932 case ResourceAllocationModel::RequestAllocation:
933 result = allocation( g, role );
934 break;
935 case ResourceAllocationModel::RequestMaximum:
936 result = maximum( g, role );
937 break;
938 default:
939 result = m_model.data( g, index.column(), role );
940 break;
941 }
942 }
943 }
944 if ( role == Qt::DisplayRole && ! result.isValid() ) {
945 // HACK to show focus in empty cells
946 result = ' ';
947 }
948 return result;
949}
950
951bool ResourceAllocationItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
952{
953 if ( ! index.isValid() ) {
954 return ItemModelBase::setData( index, value, role );
955 }
956 if ( ( flags( index ) & Qt::ItemIsEditable ) == 0 ) {
957 return false;
958 }
959 QObject *obj = object( index );
960 Resource *r = qobject_cast<Resource*>( obj );
961 if ( r ) {
962 switch (index.column()) {
963 case ResourceAllocationModel::RequestAllocation:
964 if ( setAllocation( r, value, role ) ) {
965 emit dataChanged( index, index );
966 QModelIndex idx = this->index( index.row(), ResourceAllocationModel::RequestAllocation, parent( parent( index ) ) );
967 emit dataChanged( idx, idx );
968 return true;
969 }
970 return false;
971 case ResourceAllocationModel::RequestRequired:
972 return setRequired( index, value, role );
973 default:
974 //qWarning("data: invalid display value column %d", index.column());
975 return false;
976 }
977 }
978 ResourceGroup *g = qobject_cast<ResourceGroup*>( obj );
979 if ( g ) {
980 switch (index.column()) {
981 case ResourceAllocationModel::RequestAllocation:
982 if ( setAllocation( g, value, role ) ) {
983 emit dataChanged( index, index );
984 return true;
985 }
986 return false;
987 default:
988 //qWarning("data: invalid display value column %d", index.column());
989 return false;
990 }
991 }
992 return false;
993}
994
995QVariant ResourceAllocationItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
996{
997 if ( orientation == Qt::Horizontal ) {
998 if ( role == Qt::DisplayRole ) {
999 return m_model.headerData( section, role );
1000 }
1001 if ( role == Qt::TextAlignmentRole ) {
1002 switch (section) {
1003 case 0: return QVariant();
1004 default: return Qt::AlignCenter;
1005 }
1006 return Qt::AlignCenter;
1007 }
1008 }
1009 return m_model.headerData( section, role );
1010}
1011
1012QAbstractItemDelegate *ResourceAllocationItemModel::createDelegate( int col, QWidget *parent ) const
1013{
1014 switch ( col ) {
1015 case ResourceAllocationModel::RequestAllocation: return new SpinBoxDelegate( parent );
1016 case ResourceAllocationModel::RequestRequired: return new RequieredResourceDelegate( parent );
1017 default: break;
1018 }
1019 return 0;
1020}
1021
1022QObject *ResourceAllocationItemModel::object( const QModelIndex &index ) const
1023{
1024 QObject *o = 0;
1025 if ( index.isValid() ) {
1026 o = static_cast<QObject*>( index.internalPointer() );
1027 Q_ASSERT( o );
1028 }
1029 return o;
1030}
1031
1032void ResourceAllocationItemModel::slotResourceChanged( Resource *res )
1033{
1034 ResourceGroup *g = res->parentGroup();
1035 if ( g ) {
1036 int row = g->indexOf( res );
1037 emit dataChanged( createIndex( row, 0, res ), createIndex( row, columnCount() - 1, res ) );
1038 return;
1039 }
1040}
1041
1042void ResourceAllocationItemModel::slotResourceGroupChanged( ResourceGroup *res )
1043{
1044 Project *p = res->project();
1045 if ( p ) {
1046 int row = p->resourceGroups().indexOf( res );
1047 emit dataChanged( createIndex( row, 0, res ), createIndex( row, columnCount() - 1, res ) );
1048 }
1049}
1050
1051Resource *ResourceAllocationItemModel::resource( const QModelIndex &idx ) const
1052{
1053 return qobject_cast<Resource*>( object( idx ) );
1054}
1055
1056void ResourceAllocationItemModel::setRequired( const QModelIndex &idx, const QList<Resource*> &lst )
1057{
1058 Resource *r = resource( idx );
1059 Q_ASSERT( r );
1060 if ( m_resourceCache.contains( r ) ) {
1061 m_resourceCache[ r ]->setRequiredResources( lst );
1062 emit dataChanged( idx, idx );
1063 }
1064}
1065
1066QList<Resource*> ResourceAllocationItemModel::required( const QModelIndex &idx ) const
1067{
1068 Resource *r = resource( idx );
1069 Q_ASSERT( r );
1070 if ( m_resourceCache.contains( r ) ) {
1071 ResourceRequest* request = m_resourceCache[ r ];
1072 return request->requiredResources();
1073 }
1074 return r->requiredResources();
1075}
1076
1077} // namespace KPlato
1078
1079#include "kptresourceallocationmodel.moc"
1080