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 | |
43 | namespace KPlato |
44 | { |
45 | |
46 | //-------------------------------------- |
47 | |
48 | ResourceAllocationModel::ResourceAllocationModel( QObject *parent ) |
49 | : QObject( parent ), |
50 | m_project( 0 ), |
51 | m_task( 0 ) |
52 | { |
53 | } |
54 | |
55 | ResourceAllocationModel::~ResourceAllocationModel() |
56 | { |
57 | } |
58 | |
59 | const QMetaEnum ResourceAllocationModel::columnMap() const |
60 | { |
61 | return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties" ) ); |
62 | } |
63 | |
64 | void ResourceAllocationModel::setProject( Project *project ) |
65 | { |
66 | m_project = project; |
67 | } |
68 | |
69 | void ResourceAllocationModel::setTask( Task *task ) |
70 | { |
71 | m_task = task; |
72 | } |
73 | |
74 | int ResourceAllocationModel::propertyCount() const |
75 | { |
76 | return columnMap().keyCount(); |
77 | } |
78 | |
79 | QVariant 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 | |
94 | QVariant 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 | |
110 | QVariant 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 | |
130 | QVariant 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 | |
151 | QVariant 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 | |
193 | QVariant 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 | |
218 | QVariant 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 | |
238 | QVariant 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 | |
267 | QVariant 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 | |
284 | QVariant 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 | |
303 | QVariant 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 | |
329 | QVariant ResourceAllocationModel::( 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 | |
370 | ResourceAllocationItemModel::ResourceAllocationItemModel( QObject *parent ) |
371 | : ItemModelBase( parent ) |
372 | { |
373 | } |
374 | |
375 | ResourceAllocationItemModel::~ResourceAllocationItemModel() |
376 | { |
377 | } |
378 | |
379 | void ResourceAllocationItemModel::slotResourceToBeInserted( const ResourceGroup *group, int row ) |
380 | { |
381 | //kDebug(planDbg())<<group->name()<<","<<row; |
382 | beginInsertRows( index( group ), row, row ); |
383 | } |
384 | |
385 | void 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 | |
392 | void 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 | |
399 | void ResourceAllocationItemModel::slotResourceRemoved( const Resource */*resource */) |
400 | { |
401 | //kDebug(planDbg())<<resource->name(); |
402 | endRemoveRows(); |
403 | } |
404 | |
405 | void ResourceAllocationItemModel::slotResourceGroupToBeInserted( const ResourceGroup */*group*/, int row ) |
406 | { |
407 | //kDebug(planDbg())<<group->name(); |
408 | beginInsertRows( QModelIndex(), row, row ); |
409 | } |
410 | |
411 | void ResourceAllocationItemModel::slotResourceGroupInserted( const ResourceGroup */*group */) |
412 | { |
413 | //kDebug(planDbg())<<group->name(); |
414 | endInsertRows(); |
415 | } |
416 | |
417 | void ResourceAllocationItemModel::slotResourceGroupToBeRemoved( const ResourceGroup *group ) |
418 | { |
419 | //kDebug(planDbg())<<group->name(); |
420 | int row = index( group ).row(); |
421 | beginRemoveRows( QModelIndex(), row, row ); |
422 | } |
423 | |
424 | void ResourceAllocationItemModel::slotResourceGroupRemoved( const ResourceGroup */*group */) |
425 | { |
426 | //kDebug(planDbg())<<group->name(); |
427 | endRemoveRows(); |
428 | } |
429 | |
430 | void 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 | |
478 | void 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 | |
497 | void 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 | |
523 | bool 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 | |
540 | Qt::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 | |
575 | QModelIndex 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 | |
592 | QModelIndex 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 | |
614 | QModelIndex 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 | |
629 | QModelIndex 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 | |
640 | int ResourceAllocationItemModel::columnCount( const QModelIndex &/*parent*/ ) const |
641 | { |
642 | return m_model.propertyCount(); |
643 | } |
644 | |
645 | int 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 | |
661 | QVariant 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 | |
698 | int 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 | |
709 | QVariant 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 | |
755 | bool 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 | |
788 | bool 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 | |
802 | QVariant 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 | |
820 | QVariant 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 | |
874 | bool 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 | |
892 | QVariant 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 | |
908 | QVariant 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 | |
951 | bool 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 | |
995 | QVariant ResourceAllocationItemModel::( 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 | |
1012 | QAbstractItemDelegate *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 | |
1022 | QObject *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 | |
1032 | void 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 | |
1042 | void 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 | |
1051 | Resource *ResourceAllocationItemModel::resource( const QModelIndex &idx ) const |
1052 | { |
1053 | return qobject_cast<Resource*>( object( idx ) ); |
1054 | } |
1055 | |
1056 | void 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 | |
1066 | QList<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 | |