1 | /* This file is part of the KDE project |
2 | Copyright (C) 2007, 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 "kptaccountsmodel.h" |
21 | |
22 | #include "kptglobal.h" |
23 | #include "kptcommonstrings.h" |
24 | #include "kptcommand.h" |
25 | #include "kptduration.h" |
26 | #include "kptnode.h" |
27 | #include "kptproject.h" |
28 | #include "kpttask.h" |
29 | #include "kptaccount.h" |
30 | #include "kptdatetime.h" |
31 | #include "kptschedule.h" |
32 | #include "kptdebug.h" |
33 | |
34 | #include <KoIcon.h> |
35 | |
36 | #include <QList> |
37 | #include <QObject> |
38 | |
39 | #include <kcalendarsystem.h> |
40 | #include <kdeversion.h> |
41 | #include <kglobal.h> |
42 | #include <klocale.h> |
43 | |
44 | namespace KPlato |
45 | { |
46 | |
47 | //-------------------------------------- |
48 | AccountModel::AccountModel() |
49 | : QObject(), |
50 | m_project( 0 ) |
51 | { |
52 | } |
53 | |
54 | const QMetaEnum AccountModel::columnMap() const |
55 | { |
56 | return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties" ) ); |
57 | } |
58 | |
59 | int AccountModel::propertyCount() const |
60 | { |
61 | return columnMap().keyCount(); |
62 | } |
63 | |
64 | QVariant AccountModel::data( const Account *a, int property, int role ) const |
65 | { |
66 | QVariant result; |
67 | if ( a == 0 ) { |
68 | return QVariant(); |
69 | } |
70 | switch ( property ) { |
71 | case AccountModel::Name: result = name( a, role ); break; |
72 | case AccountModel::Description: result = description( a, role ); break; |
73 | default: |
74 | kDebug(planDbg())<<"data: invalid display value column" <<property; |
75 | return QVariant(); |
76 | } |
77 | return result; |
78 | } |
79 | |
80 | QVariant AccountModel::name( const Account *a, int role ) const |
81 | { |
82 | //kDebug(planDbg())<<a->name()<<","<<role; |
83 | switch ( role ) { |
84 | case Qt::DisplayRole: |
85 | case Qt::EditRole: |
86 | return a->name(); |
87 | case Qt::ToolTipRole: |
88 | if ( a->isDefaultAccount() ) { |
89 | return i18nc( "1=account name" , "%1 (Default account)" , a->name() ); |
90 | } |
91 | return a->name(); |
92 | case Qt::StatusTipRole: |
93 | case Qt::WhatsThisRole: |
94 | return QVariant(); |
95 | case Qt::CheckStateRole: |
96 | if ( a->isDefaultAccount() ) { |
97 | return m_project && m_project->isBaselined() ? Qt::PartiallyChecked : Qt::Checked; |
98 | } |
99 | return m_project && m_project->isBaselined() ? QVariant() : Qt::Unchecked; |
100 | case Qt::DecorationRole: |
101 | if ( a->isBaselined() ) { |
102 | return koIcon("view-time-schedule-baselined" ); |
103 | } |
104 | break; |
105 | } |
106 | return QVariant(); |
107 | } |
108 | |
109 | QVariant AccountModel::description( const Account *a, int role ) const |
110 | { |
111 | //kDebug(planDbg())<<res->name()<<","<<role; |
112 | switch ( role ) { |
113 | case Qt::DisplayRole: |
114 | case Qt::EditRole: |
115 | case Qt::ToolTipRole: |
116 | return a->description(); |
117 | break; |
118 | case Qt::StatusTipRole: |
119 | case Qt::WhatsThisRole: |
120 | return QVariant(); |
121 | } |
122 | return QVariant(); |
123 | } |
124 | |
125 | QVariant AccountModel::( int property, int role ) const |
126 | { |
127 | if ( role == Qt::DisplayRole ) { |
128 | switch ( property ) { |
129 | case AccountModel::Name: return i18n( "Name" ); |
130 | case AccountModel::Description: return i18n( "Description" ); |
131 | default: return QVariant(); |
132 | } |
133 | } |
134 | if ( role == Qt::TextAlignmentRole ) { |
135 | return QVariant(); |
136 | } |
137 | if ( role == Qt::ToolTipRole ) { |
138 | switch ( property ) { |
139 | case AccountModel::Name: return ToolTip::accountName(); |
140 | case AccountModel::Description: return ToolTip::accountDescription(); |
141 | default: return QVariant(); |
142 | } |
143 | } |
144 | return QVariant(); |
145 | } |
146 | |
147 | //---------------------------------------- |
148 | AccountItemModel::AccountItemModel( QObject *parent ) |
149 | : ItemModelBase( parent ), |
150 | m_account( 0 ) |
151 | { |
152 | } |
153 | |
154 | AccountItemModel::~AccountItemModel() |
155 | { |
156 | } |
157 | |
158 | const QMetaEnum AccountItemModel::columnMap() const |
159 | { |
160 | return m_model.columnMap(); |
161 | } |
162 | |
163 | void AccountItemModel::slotAccountToBeInserted( const Account *parent, int row ) |
164 | { |
165 | //kDebug(planDbg())<<parent->name(); |
166 | Q_ASSERT( m_account == 0 ); |
167 | m_account = const_cast<Account*>(parent); |
168 | beginInsertRows( index( parent ), row, row ); |
169 | } |
170 | |
171 | void AccountItemModel::slotAccountInserted( const Account *account ) |
172 | { |
173 | //kDebug(planDbg())<<account->name(); |
174 | Q_ASSERT( account->parent() == m_account ); Q_UNUSED( account ); |
175 | endInsertRows(); |
176 | m_account = 0; |
177 | } |
178 | |
179 | void AccountItemModel::slotAccountToBeRemoved( const Account *account ) |
180 | { |
181 | //kDebug(planDbg())<<account->name(); |
182 | Q_ASSERT( m_account == 0 ); |
183 | m_account = const_cast<Account*>(account); |
184 | int row = index( account ).row(); |
185 | beginRemoveRows( index( account->parent() ), row, row ); |
186 | } |
187 | |
188 | void AccountItemModel::slotAccountRemoved( const Account *account ) |
189 | { |
190 | //kDebug(planDbg())<<account->name(); |
191 | Q_ASSERT( account == m_account ); Q_UNUSED( account ); |
192 | endRemoveRows(); |
193 | m_account = 0; |
194 | } |
195 | |
196 | void AccountItemModel::setProject( Project *project ) |
197 | { |
198 | if ( m_project ) { |
199 | Accounts *acc = &( m_project->accounts() ); |
200 | disconnect( acc , SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) ); |
201 | |
202 | disconnect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) ); |
203 | disconnect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) ); |
204 | |
205 | disconnect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) ); |
206 | disconnect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) ); |
207 | } |
208 | m_project = project; |
209 | m_model.m_project = project; |
210 | if ( project ) { |
211 | Accounts *acc = &( project->accounts() ); |
212 | kDebug(planDbg())<<acc; |
213 | connect( acc, SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) ); |
214 | |
215 | connect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) ); |
216 | connect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) ); |
217 | |
218 | connect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) ); |
219 | connect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) ); |
220 | } |
221 | } |
222 | |
223 | Qt::ItemFlags AccountItemModel::flags( const QModelIndex &index ) const |
224 | { |
225 | Qt::ItemFlags flags = ItemModelBase::flags( index ); |
226 | if ( ! m_readWrite ) { |
227 | return flags &= ~Qt::ItemIsEditable; |
228 | } |
229 | if ( ! index.isValid() || ! m_project ) { |
230 | return flags; |
231 | } |
232 | Account *a = account( index ); |
233 | if ( a ) { |
234 | switch ( index.column() ) { |
235 | case AccountModel::Name: { |
236 | if ( ! a->isBaselined() ) { |
237 | flags |= Qt::ItemIsEditable; |
238 | flags |= Qt::ItemIsUserCheckable; |
239 | } |
240 | break; |
241 | } |
242 | default: flags |= Qt::ItemIsEditable; break; |
243 | } |
244 | } |
245 | return flags; |
246 | } |
247 | |
248 | |
249 | QModelIndex AccountItemModel::parent( const QModelIndex &index ) const |
250 | { |
251 | if ( !index.isValid() || m_project == 0 ) { |
252 | return QModelIndex(); |
253 | } |
254 | //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column(); |
255 | Account *a = account( index ); |
256 | if ( a == 0 ) { |
257 | return QModelIndex(); |
258 | } |
259 | Account *par = a->parent(); |
260 | if ( par ) { |
261 | a = par->parent(); |
262 | int row = -1; |
263 | if ( a ) { |
264 | row = a->accountList().indexOf( par ); |
265 | } else { |
266 | row = m_project->accounts().accountList().indexOf( par ); |
267 | } |
268 | //kDebug(planDbg())<<par->name()<<":"<<row; |
269 | return createIndex( row, 0, par ); |
270 | } |
271 | return QModelIndex(); |
272 | } |
273 | |
274 | QModelIndex AccountItemModel::index( int row, int column, const QModelIndex &parent ) const |
275 | { |
276 | if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) { |
277 | return QModelIndex(); |
278 | } |
279 | Account *par = account( parent ); |
280 | if ( par == 0 ) { |
281 | if ( row < m_project->accounts().accountList().count() ) { |
282 | return createIndex( row, column, m_project->accounts().accountList().at( row ) ); |
283 | } |
284 | } else if ( row < par->accountList().count() ) { |
285 | return createIndex( row, column, par->accountList().at( row ) ); |
286 | } |
287 | return QModelIndex(); |
288 | } |
289 | |
290 | QModelIndex AccountItemModel::index( const Account *account, int column ) const |
291 | { |
292 | Account *a = const_cast<Account*>(account); |
293 | if ( m_project == 0 || account == 0 ) { |
294 | return QModelIndex(); |
295 | } |
296 | int row = -1; |
297 | Account *par = a->parent(); |
298 | if ( par == 0 ) { |
299 | row = m_project->accounts().accountList().indexOf( a ); |
300 | } else { |
301 | row = par->accountList().indexOf( a ); |
302 | } |
303 | if ( row == -1 ) { |
304 | return QModelIndex(); |
305 | } |
306 | return createIndex( row, column, a ); |
307 | |
308 | } |
309 | |
310 | int AccountItemModel::columnCount( const QModelIndex & ) const |
311 | { |
312 | return m_model.propertyCount(); |
313 | } |
314 | |
315 | int AccountItemModel::rowCount( const QModelIndex &parent ) const |
316 | { |
317 | if ( m_project == 0 ) { |
318 | return 0; |
319 | } |
320 | Account *par = account( parent ); |
321 | if ( par == 0 ) { |
322 | return m_project->accounts().accountList().count(); |
323 | } |
324 | return par->accountList().count(); |
325 | } |
326 | |
327 | bool AccountItemModel::setName( Account *a, const QVariant &value, int role ) |
328 | { |
329 | switch ( role ) { |
330 | case Qt::EditRole: |
331 | if ( value.toString() != a->name() ) { |
332 | emit executeCommand( new RenameAccountCmd( a, value.toString(), kundo2_i18n( "Modify account name" ) ) ); |
333 | } |
334 | return true; |
335 | case Qt::CheckStateRole: { |
336 | switch ( value.toInt() ) { |
337 | case Qt::Unchecked: |
338 | if ( a->isDefaultAccount() ) { |
339 | emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), a, 0, kundo2_i18n( "De-select as default account" ) ) ); |
340 | return true; |
341 | } |
342 | break; |
343 | case Qt::Checked: |
344 | if ( ! a->isDefaultAccount() ) { |
345 | emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), m_project->accounts().defaultAccount(), a, kundo2_i18n( "Select as default account" ) ) ); |
346 | return true; |
347 | } |
348 | break; |
349 | default: break; |
350 | } |
351 | } |
352 | default: break; |
353 | } |
354 | return false; |
355 | } |
356 | |
357 | bool AccountItemModel::setDescription( Account *a, const QVariant &value, int role ) |
358 | { |
359 | switch ( role ) { |
360 | case Qt::EditRole: |
361 | if ( value.toString() != a->description() ) { |
362 | emit executeCommand( new ModifyAccountDescriptionCmd( a, value.toString(), kundo2_i18n( "Modify account description" ) ) ); |
363 | } |
364 | return true; |
365 | } |
366 | return false; |
367 | } |
368 | |
369 | QVariant AccountItemModel::data( const QModelIndex &index, int role ) const |
370 | { |
371 | QVariant result; |
372 | Account *a = account( index ); |
373 | if ( a == 0 ) { |
374 | return QVariant(); |
375 | } |
376 | result = m_model.data( a, index.column(), role ); |
377 | return result; |
378 | } |
379 | |
380 | bool AccountItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) |
381 | { |
382 | if ( ! index.isValid() ) { |
383 | return ItemModelBase::setData( index, value, role ); |
384 | } |
385 | if ( ( flags( index ) &( Qt::ItemIsEditable | Qt::CheckStateRole ) ) == 0 ) { |
386 | Q_ASSERT( true ); |
387 | return false; |
388 | } |
389 | Account *a = account( index ); |
390 | kDebug(planDbg())<<a->name()<<value<<role; |
391 | switch (index.column()) { |
392 | case AccountModel::Name: return setName( a, value, role ); |
393 | case AccountModel::Description: return setDescription( a, value, role ); |
394 | default: |
395 | qWarning("data: invalid display value column %d" , index.column()); |
396 | return false; |
397 | } |
398 | return false; |
399 | } |
400 | |
401 | QVariant AccountItemModel::( int section, Qt::Orientation orientation, int role ) const |
402 | { |
403 | if ( orientation == Qt::Horizontal ) { |
404 | return m_model.headerData( section, role ); |
405 | } |
406 | return ItemModelBase::headerData(section, orientation, role); |
407 | } |
408 | |
409 | Account *AccountItemModel::account( const QModelIndex &index ) const |
410 | { |
411 | return static_cast<Account*>( index.internalPointer() ); |
412 | } |
413 | |
414 | void AccountItemModel::slotAccountChanged( Account *account ) |
415 | { |
416 | Account *par = account->parent(); |
417 | if ( par ) { |
418 | int row = par->accountList().indexOf( account ); |
419 | emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) ); |
420 | } else { |
421 | int row = m_project->accounts().accountList().indexOf( account ); |
422 | emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) ); |
423 | } |
424 | } |
425 | |
426 | QModelIndex AccountItemModel::insertAccount( Account *account, Account *parent, int index ) |
427 | { |
428 | kDebug(planDbg()); |
429 | if ( account->name().isEmpty() || m_project->accounts().findAccount( account->name() ) ) { |
430 | QString s = parent == 0 ? account->name() : parent->name(); |
431 | account->setName( m_project->accounts().uniqueId( s ) ); |
432 | //m_project->accounts().insertId( account ); |
433 | } |
434 | emit executeCommand( new AddAccountCmd( *m_project, account, parent, index, kundo2_i18n( "Add account" ) ) ); |
435 | int row = -1; |
436 | if ( parent ) { |
437 | row = parent->accountList().indexOf( account ); |
438 | } else { |
439 | row = m_project->accounts().accountList().indexOf( account ); |
440 | } |
441 | if ( row != -1 ) { |
442 | //kDebug(planDbg())<<"Inserted:"<<account->name(); |
443 | return createIndex( row, 0, account ); |
444 | } |
445 | kDebug(planDbg())<<"Can't find" <<account->name(); |
446 | return QModelIndex(); |
447 | } |
448 | |
449 | void AccountItemModel::removeAccounts( QList<Account*> lst ) |
450 | { |
451 | MacroCommand *cmd = 0; |
452 | KUndo2MagicString c = kundo2_i18np( "Delete Account" , "Delete %1 Accounts" , lst.count() ); |
453 | while ( ! lst.isEmpty() ) { |
454 | bool del = true; |
455 | Account *acc = lst.takeFirst(); |
456 | foreach ( Account *a, lst ) { |
457 | if ( acc->isChildOf( a ) ) { |
458 | del = false; // acc will be deleted when a is deleted |
459 | break; |
460 | } |
461 | } |
462 | if ( del ) { |
463 | if ( cmd == 0 ) cmd = new MacroCommand( c ); |
464 | cmd->addCommand( new RemoveAccountCmd( *m_project, acc ) ); |
465 | } |
466 | } |
467 | if ( cmd ) |
468 | emit executeCommand( cmd ); |
469 | } |
470 | |
471 | //---------------------------------------- |
472 | CostBreakdownItemModel::CostBreakdownItemModel( QObject *parent ) |
473 | : ItemModelBase( parent ), |
474 | m_manager( 0 ), |
475 | m_cumulative( false ), |
476 | m_periodtype( Period_Day ), |
477 | m_startmode( StartMode_Project ), |
478 | m_endmode( EndMode_Project ), |
479 | m_showmode( ShowMode_Both ) |
480 | { |
481 | m_format = QString( "%1 [%2]" ); |
482 | } |
483 | |
484 | CostBreakdownItemModel::~CostBreakdownItemModel() |
485 | { |
486 | } |
487 | |
488 | void CostBreakdownItemModel::slotAccountToBeInserted( const Account *parent, int row ) |
489 | { |
490 | //kDebug(planDbg())<<parent->name(); |
491 | beginInsertRows( index( parent ), row, row ); |
492 | } |
493 | |
494 | void CostBreakdownItemModel::slotAccountInserted( const Account *account ) |
495 | { |
496 | Q_UNUSED(account); |
497 | //kDebug(planDbg())<<account->name(); |
498 | endInsertRows(); |
499 | } |
500 | |
501 | void CostBreakdownItemModel::slotAccountToBeRemoved( const Account *account ) |
502 | { |
503 | |
504 | //kDebug(planDbg())<<account->name(); |
505 | int row = index( account ).row(); |
506 | beginRemoveRows( index( account->parent() ), row, row ); |
507 | } |
508 | |
509 | void CostBreakdownItemModel::slotAccountRemoved( const Account *account ) |
510 | { |
511 | Q_UNUSED(account); |
512 | //kDebug(planDbg())<<account->name(); |
513 | endRemoveRows(); |
514 | } |
515 | |
516 | void CostBreakdownItemModel::slotDataChanged() |
517 | { |
518 | fetchData(); |
519 | foreach ( Account *a, m_plannedCostMap.keys() ) { |
520 | QModelIndex idx1 = index( a ); |
521 | QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) ); |
522 | //kDebug(planDbg())<<a->name()<<idx1<<idx2; |
523 | emit dataChanged( idx1, idx2 ); |
524 | } |
525 | } |
526 | |
527 | void CostBreakdownItemModel::setProject( Project *project ) |
528 | { |
529 | if ( m_project ) { |
530 | Accounts *acc = &( m_project->accounts() ); |
531 | disconnect( acc , SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) ); |
532 | |
533 | disconnect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) ); |
534 | disconnect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) ); |
535 | |
536 | disconnect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) ); |
537 | disconnect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) ); |
538 | |
539 | disconnect( m_project , SIGNAL(nodeChanged(Node*)), this, SLOT(slotDataChanged()) ); |
540 | disconnect( m_project , SIGNAL(nodeAdded(Node*)), this, SLOT(slotDataChanged()) ); |
541 | disconnect( m_project , SIGNAL(nodeRemoved(Node*)), this, SLOT(slotDataChanged()) ); |
542 | |
543 | disconnect( m_project , SIGNAL(resourceChanged(Resource*)), this, SLOT(slotDataChanged()) ); |
544 | disconnect( m_project , SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotDataChanged()) ); |
545 | disconnect( m_project , SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotDataChanged()) ); |
546 | } |
547 | m_project = project; |
548 | if ( project ) { |
549 | Accounts *acc = &( project->accounts() ); |
550 | kDebug(planDbg())<<acc; |
551 | connect( acc, SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) ); |
552 | |
553 | connect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) ); |
554 | connect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) ); |
555 | |
556 | connect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) ); |
557 | connect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) ); |
558 | |
559 | connect( m_project , SIGNAL(nodeChanged(Node*)), this, SLOT(slotDataChanged()) ); |
560 | connect( m_project , SIGNAL(nodeAdded(Node*)), this, SLOT(slotDataChanged()) ); |
561 | connect( m_project , SIGNAL(nodeRemoved(Node*)), this, SLOT(slotDataChanged()) ); |
562 | |
563 | connect( m_project , SIGNAL(resourceChanged(Resource*)), this, SLOT(slotDataChanged()) ); |
564 | connect( m_project , SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotDataChanged()) ); |
565 | connect( m_project , SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotDataChanged()) ); |
566 | } |
567 | } |
568 | |
569 | void CostBreakdownItemModel::setScheduleManager( ScheduleManager *sm ) |
570 | { |
571 | kDebug(planDbg())<<m_project<<m_manager<<sm; |
572 | if ( m_manager != sm ) { |
573 | m_manager = sm; |
574 | fetchData(); |
575 | reset(); |
576 | } |
577 | } |
578 | |
579 | long CostBreakdownItemModel::id() const |
580 | { |
581 | return m_manager == 0 ? -1 : m_manager->scheduleId(); |
582 | } |
583 | |
584 | EffortCostMap CostBreakdownItemModel::fetchPlannedCost( Account *account ) |
585 | { |
586 | EffortCostMap ec; |
587 | ec = account->plannedCost( id() ); |
588 | m_plannedCostMap.insert( account, ec ); |
589 | QDate s = ec.startDate(); |
590 | if ( ! m_plannedStart.isValid() || s < m_plannedStart ) { |
591 | m_plannedStart = s; |
592 | } |
593 | QDate e = ec.endDate(); |
594 | if ( ! m_plannedEnd.isValid() || e > m_plannedEnd ) { |
595 | m_plannedEnd = e; |
596 | } |
597 | return ec; |
598 | } |
599 | |
600 | EffortCostMap CostBreakdownItemModel::fetchActualCost( Account *account ) |
601 | { |
602 | kDebug(planDbg())<<account->name(); |
603 | EffortCostMap ec; |
604 | ec = account->actualCost( id() ); |
605 | m_actualCostMap.insert( account, ec ); |
606 | QDate s = ec.startDate(); |
607 | if ( ! m_actualStart.isValid() || s < m_actualStart ) { |
608 | m_actualStart = s; |
609 | } |
610 | QDate e = ec.endDate(); |
611 | if ( ! m_actualEnd.isValid() || e > m_actualEnd ) { |
612 | m_actualEnd = e; |
613 | } |
614 | kDebug(planDbg())<<account->name()<<ec.totalEffort().toDouble(Duration::Unit_h)<<ec.totalCost(); |
615 | return ec; |
616 | } |
617 | |
618 | void CostBreakdownItemModel::fetchData() |
619 | { |
620 | //kDebug(planDbg())<<m_start<<m_end; |
621 | m_plannedCostMap.clear(); |
622 | m_plannedStart = m_plannedEnd = QDate(); |
623 | m_actualStart = m_actualEnd = QDate(); |
624 | if ( m_project == 0 || m_manager == 0 ) { |
625 | return; |
626 | } |
627 | foreach ( Account *a, m_project->accounts().allAccounts() ) { |
628 | fetchPlannedCost( a ); |
629 | fetchActualCost( a ); |
630 | } |
631 | } |
632 | |
633 | QModelIndex CostBreakdownItemModel::parent( const QModelIndex &index ) const |
634 | { |
635 | if ( !index.isValid() || m_project == 0 ) { |
636 | return QModelIndex(); |
637 | } |
638 | //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column(); |
639 | Account *a = account( index ); |
640 | if ( a == 0 ) { |
641 | return QModelIndex(); |
642 | } |
643 | Account *par = a->parent(); |
644 | if ( par ) { |
645 | a = par->parent(); |
646 | int row = -1; |
647 | if ( a ) { |
648 | row = a->accountList().indexOf( par ); |
649 | } else { |
650 | row = m_project->accounts().accountList().indexOf( par ); |
651 | } |
652 | //kDebug(planDbg())<<par->name()<<":"<<row; |
653 | return createIndex( row, 0, par ); |
654 | } |
655 | return QModelIndex(); |
656 | } |
657 | |
658 | QModelIndex CostBreakdownItemModel::index( int row, int column, const QModelIndex &parent ) const |
659 | { |
660 | if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) { |
661 | return QModelIndex(); |
662 | } |
663 | Account *par = account( parent ); |
664 | if ( par == 0 ) { |
665 | if ( row < m_project->accounts().accountList().count() ) { |
666 | return createIndex( row, column, m_project->accounts().accountList().at( row ) ); |
667 | } |
668 | } else if ( row < par->accountList().count() ) { |
669 | return createIndex( row, column, par->accountList().at( row ) ); |
670 | } |
671 | return QModelIndex(); |
672 | } |
673 | |
674 | QModelIndex CostBreakdownItemModel::index( const Account *account ) const |
675 | { |
676 | Account *a = const_cast<Account*>(account); |
677 | if ( m_project == 0 || account == 0 ) { |
678 | return QModelIndex(); |
679 | } |
680 | int row = -1; |
681 | Account *par = a->parent(); |
682 | if ( par == 0 ) { |
683 | row = m_project->accounts().accountList().indexOf( a ); |
684 | } else { |
685 | row = par->accountList().indexOf( a ); |
686 | } |
687 | if ( row == -1 ) { |
688 | return QModelIndex(); |
689 | } |
690 | return createIndex( row, 0, a ); |
691 | |
692 | } |
693 | |
694 | int CostBreakdownItemModel::columnCount( const QModelIndex & ) const |
695 | { |
696 | int c = 3; |
697 | if ( startDate().isValid() && endDate().isValid() ) { |
698 | switch ( m_periodtype ) { |
699 | case Period_Day: { |
700 | c += startDate().daysTo( endDate()) + 1; |
701 | break; |
702 | } |
703 | case Period_Week: { |
704 | // ISO Week numbering always uses Monday as first day of week |
705 | const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber) |
706 | ? Qt::Monday |
707 | : KGlobal::locale()->weekStartDay(); |
708 | |
709 | int days = firstWeekDay - startDate().dayOfWeek(); |
710 | if ( days > 0 ) { |
711 | days -= 7; |
712 | } |
713 | QDate start = startDate().addDays( days ); |
714 | c += (start.daysTo( endDate() ) / 7) + 1; |
715 | break; |
716 | } |
717 | case Period_Month: { |
718 | int days = startDate().daysInMonth() - startDate().day() + 1; |
719 | for ( QDate d = startDate(); d < endDate(); d = d.addDays( days ) ) { |
720 | ++c; |
721 | days = qMin( d.daysTo( endDate() ), d.daysInMonth() ); |
722 | } |
723 | break; |
724 | } |
725 | } |
726 | } |
727 | return c; |
728 | } |
729 | |
730 | int CostBreakdownItemModel::rowCount( const QModelIndex &parent ) const |
731 | { |
732 | if ( m_project == 0 ) { |
733 | return 0; |
734 | } |
735 | Account *par = account( parent ); |
736 | if ( par == 0 ) { |
737 | return m_project->accounts().accountList().count(); |
738 | } |
739 | return par->accountList().count(); |
740 | } |
741 | |
742 | QString CostBreakdownItemModel::formatMoney( double cost1, double cost2 ) const |
743 | { |
744 | if ( m_showmode == ShowMode_Planned ) { |
745 | return m_project->locale()->formatMoney( cost1, "" , 0 ); |
746 | } |
747 | if ( m_showmode == ShowMode_Actual ) { |
748 | return m_project->locale()->formatMoney( cost2, "" , 0 ); |
749 | } |
750 | if ( m_showmode == ShowMode_Both ) { |
751 | return QString(m_format).arg( m_project->locale()->formatMoney( cost2, "" , 0 ) ).arg( m_project->locale()->formatMoney( cost1, "" , 0 ) ); |
752 | } |
753 | if ( m_showmode == ShowMode_Deviation ) { |
754 | return m_project->locale()->formatMoney( cost1 - cost2, "" , 0 ); |
755 | } |
756 | return "" ; |
757 | } |
758 | |
759 | QVariant CostBreakdownItemModel::data( const QModelIndex &index, int role ) const |
760 | { |
761 | QVariant result; |
762 | Account *a = account( index ); |
763 | if ( a == 0 ) { |
764 | return QVariant(); |
765 | } |
766 | if ( role == Qt::DisplayRole ) { |
767 | switch ( index.column() ) { |
768 | case 0: return a->name(); |
769 | case 1: return a->description(); |
770 | case 2: { |
771 | return formatMoney( m_plannedCostMap.value( a ).totalCost(), m_actualCostMap.value( a ).totalCost() ); |
772 | } |
773 | default: { |
774 | int col = index.column() - 3; |
775 | EffortCostMap pc = m_plannedCostMap.value( a ); |
776 | EffortCostMap ac = m_actualCostMap.value( a ); |
777 | switch ( m_periodtype ) { |
778 | case Period_Day: { |
779 | double planned = 0.0; |
780 | if ( m_cumulative ) { |
781 | planned = pc.costTo( startDate().addDays( col ) ); |
782 | } else { |
783 | planned = pc.costOnDate( startDate().addDays( col ) ); |
784 | } |
785 | double actual = 0.0; |
786 | if ( m_cumulative ) { |
787 | actual = ac.costTo( startDate().addDays( col ) ); |
788 | } else { |
789 | actual = ac.costOnDate( startDate().addDays( col ) ); |
790 | } |
791 | return formatMoney( planned, actual ); |
792 | } |
793 | case Period_Week: { |
794 | // ISO Week numbering always uses Monday as first day of week |
795 | const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber) |
796 | ? Qt::Monday |
797 | : KGlobal::locale()->weekStartDay(); |
798 | |
799 | int days = firstWeekDay - startDate().dayOfWeek(); |
800 | if ( days > 0 ) { |
801 | days -= 7; ; |
802 | } |
803 | QDate start = startDate().addDays( days ); |
804 | int week = col; |
805 | double planned = 0.0; |
806 | if ( m_cumulative ) { |
807 | planned = pc.costTo( start.addDays( ++week * 7 ) ); |
808 | } else { |
809 | planned = week == 0 ? pc.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : pc.cost( start.addDays( week * 7 ) ); |
810 | } |
811 | double actual = 0.0; |
812 | if ( m_cumulative ) { |
813 | actual = ac.costTo( start.addDays( ++week * 7 ) ); |
814 | } else { |
815 | actual = week == 0 ? ac.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : ac.cost( start.addDays( week * 7 ) ); |
816 | } |
817 | return formatMoney( planned, actual ); |
818 | } |
819 | case Period_Month: { |
820 | int days = startDate().daysInMonth() - startDate().day() + 1; |
821 | QDate start = startDate(); |
822 | for ( int i = 0; i < col; ++i ) { |
823 | start = start.addDays( days ); |
824 | days = start.daysInMonth(); |
825 | } |
826 | int planned = 0.0; |
827 | if ( m_cumulative ) { |
828 | planned = pc.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); |
829 | } else { |
830 | planned = pc.cost( start, start.daysInMonth() - start.day() + 1); |
831 | } |
832 | int actual = 0.0; |
833 | if ( m_cumulative ) { |
834 | actual = ac.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); |
835 | } else { |
836 | actual = ac.cost( start, start.daysInMonth() - start.day() + 1); |
837 | } |
838 | return formatMoney( planned, actual ); |
839 | } |
840 | default: |
841 | return 0.0; |
842 | break; |
843 | } |
844 | } |
845 | } |
846 | } else if ( role == Qt::ToolTipRole ) { |
847 | switch ( index.column() ) { |
848 | case 0: return a->name(); |
849 | case 1: return a->description(); |
850 | case 2: { |
851 | double act = m_actualCostMap.value( a ).totalCost(); |
852 | double pl = m_plannedCostMap.value( a ).totalCost(); |
853 | return i18n( "Actual total cost: %1, planned total cost: %2" , m_project->locale()->formatMoney( act, "" , 0 ), m_project->locale()->formatMoney( pl, "" , 0 ) ); |
854 | } |
855 | default: break; |
856 | } |
857 | } else if ( role == Qt::TextAlignmentRole ) { |
858 | return headerData( index.column(), Qt::Horizontal, role ); |
859 | } else { |
860 | switch ( index.column() ) { |
861 | case 0: |
862 | case 1: return QVariant(); |
863 | default: { |
864 | return cost( a, index.column() - 3, role ); |
865 | } |
866 | } |
867 | } |
868 | return QVariant(); |
869 | } |
870 | |
871 | QVariant CostBreakdownItemModel::cost( const Account *a, int offset, int role ) const |
872 | { |
873 | EffortCostMap costmap; |
874 | if ( role == Role::Planned ) { |
875 | costmap = m_plannedCostMap.value( const_cast<Account*>( a ) ); |
876 | } else if ( role == Role::Actual ) { |
877 | costmap = m_actualCostMap.value( const_cast<Account*>( a ) ); |
878 | } else { |
879 | return QVariant(); |
880 | } |
881 | double cost = 0.0; |
882 | switch ( m_periodtype ) { |
883 | case Period_Day: { |
884 | if ( m_cumulative ) { |
885 | cost = costmap.costTo( startDate().addDays( offset ) ); |
886 | } else { |
887 | cost = costmap.costOnDate( startDate().addDays( offset ) ); |
888 | } |
889 | break; |
890 | } |
891 | case Period_Week: { |
892 | // ISO Week numbering always uses Monday as first day of week |
893 | const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber) |
894 | ? Qt::Monday |
895 | : KGlobal::locale()->weekStartDay(); |
896 | |
897 | int days = firstWeekDay - startDate().dayOfWeek(); |
898 | if ( days > 0 ) { |
899 | days -= 7; ; |
900 | } |
901 | QDate start = startDate().addDays( days ); |
902 | int week = offset; |
903 | if ( m_cumulative ) { |
904 | cost = costmap.costTo( start.addDays( ++week * 7 ) ); |
905 | } else { |
906 | cost = week == 0 ? costmap.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : costmap.cost( start.addDays( week * 7 ) ); |
907 | } |
908 | break; |
909 | } |
910 | case Period_Month: { |
911 | int days = startDate().daysInMonth() - startDate().day() + 1; |
912 | QDate start = startDate(); |
913 | for ( int i = 0; i < offset; ++i ) { |
914 | start = start.addDays( days ); |
915 | days = start.daysInMonth(); |
916 | } |
917 | if ( m_cumulative ) { |
918 | cost = costmap.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) ); |
919 | } else { |
920 | cost = costmap.cost( start, start.daysInMonth() - start.day() + 1); |
921 | } |
922 | break; |
923 | } |
924 | default: |
925 | break; |
926 | } |
927 | return cost; |
928 | } |
929 | |
930 | int CostBreakdownItemModel::periodType() const |
931 | { |
932 | return m_periodtype; |
933 | } |
934 | |
935 | void CostBreakdownItemModel::setPeriodType( int period ) |
936 | { |
937 | if ( m_periodtype != period ) { |
938 | m_periodtype = period; |
939 | reset(); |
940 | } |
941 | } |
942 | |
943 | int CostBreakdownItemModel::startMode() const |
944 | { |
945 | return m_startmode; |
946 | } |
947 | |
948 | void CostBreakdownItemModel::setStartMode( int mode ) |
949 | { |
950 | m_startmode = mode; |
951 | reset(); |
952 | } |
953 | |
954 | int CostBreakdownItemModel::endMode() const |
955 | { |
956 | return m_endmode; |
957 | } |
958 | |
959 | void CostBreakdownItemModel::setEndMode( int mode ) |
960 | { |
961 | m_endmode = mode; |
962 | reset(); |
963 | } |
964 | |
965 | QDate CostBreakdownItemModel::startDate() const |
966 | { |
967 | if ( m_project == 0 || m_manager == 0 ) { |
968 | return m_start; |
969 | } |
970 | switch ( m_startmode ) { |
971 | case StartMode_Project: { |
972 | QDate d = m_project->startTime( id() ).date(); |
973 | if ( m_plannedStart.isValid() && m_plannedStart < d ) { |
974 | d = m_plannedStart; |
975 | } |
976 | if ( m_actualStart.isValid() && m_actualStart < d ) { |
977 | d = m_actualStart; |
978 | } |
979 | return d; |
980 | } |
981 | default: break; |
982 | } |
983 | return m_start; |
984 | } |
985 | |
986 | |
987 | void CostBreakdownItemModel::setStartDate( const QDate &date ) |
988 | { |
989 | m_start = date; |
990 | reset(); |
991 | } |
992 | |
993 | QDate CostBreakdownItemModel::endDate() const |
994 | { |
995 | if ( m_project == 0 || m_manager == 0 ) { |
996 | return m_end; |
997 | } |
998 | switch ( m_endmode ) { |
999 | case EndMode_Project: { |
1000 | QDate d = m_project->endTime( id() ).date(); |
1001 | if ( m_plannedEnd.isValid() && m_plannedEnd > d ) { |
1002 | d = m_plannedEnd; |
1003 | } |
1004 | if ( m_actualEnd.isValid() && m_actualEnd > d ) { |
1005 | d = m_actualEnd; |
1006 | } |
1007 | return d; |
1008 | } |
1009 | case EndMode_CurrentDate: return QDate::currentDate(); |
1010 | default: break; |
1011 | } |
1012 | return m_end; |
1013 | } |
1014 | |
1015 | void CostBreakdownItemModel::setEndDate( const QDate &date ) |
1016 | { |
1017 | m_end = date; |
1018 | reset(); |
1019 | } |
1020 | |
1021 | bool CostBreakdownItemModel::cumulative() const |
1022 | { |
1023 | return m_cumulative; |
1024 | } |
1025 | |
1026 | void CostBreakdownItemModel::setCumulative( bool on ) |
1027 | { |
1028 | m_cumulative = on; |
1029 | reset(); |
1030 | } |
1031 | |
1032 | int CostBreakdownItemModel::showMode() const |
1033 | { |
1034 | return m_showmode; |
1035 | } |
1036 | void CostBreakdownItemModel::setShowMode( int show ) |
1037 | { |
1038 | m_showmode = show; |
1039 | } |
1040 | |
1041 | QVariant CostBreakdownItemModel::( int section, Qt::Orientation orientation, int role ) const |
1042 | { |
1043 | if ( orientation == Qt::Horizontal ) { |
1044 | if ( role == Qt::DisplayRole ) { |
1045 | if ( section == 0 ) { |
1046 | return i18n( "Name" ); |
1047 | } |
1048 | if ( section == 1 ) { |
1049 | return i18n( "Description" ); |
1050 | } |
1051 | if ( section == 2 ) { |
1052 | return i18n( "Total" ); |
1053 | } |
1054 | int col = section - 3; |
1055 | switch ( m_periodtype ) { |
1056 | case Period_Day: { |
1057 | return startDate().addDays( col ).toString( Qt::ISODate ); |
1058 | } |
1059 | case Period_Week: { |
1060 | const KCalendarSystem * calendar = KGlobal::locale()->calendar(); |
1061 | #if KDE_IS_VERSION(4,7,0) |
1062 | return calendar->week(startDate().addDays( ( col ) * 7 )); |
1063 | #else |
1064 | return calendar->weekNumber(startDate().addDays( ( col ) * 7 )); |
1065 | #endif |
1066 | } |
1067 | case Period_Month: { |
1068 | int days = startDate().daysInMonth() - startDate().day() + 1; |
1069 | QDate start = startDate(); |
1070 | for ( int i = 0; i < col; ++i ) { |
1071 | start = start.addDays( days ); |
1072 | days = start.daysInMonth(); |
1073 | } |
1074 | return QDate::shortMonthName( start.month() ); |
1075 | } |
1076 | default: |
1077 | return section; |
1078 | break; |
1079 | } |
1080 | return QVariant(); |
1081 | } |
1082 | if ( role == Qt::EditRole ) { |
1083 | if ( section == 0 ) { |
1084 | return "Name" ; |
1085 | } |
1086 | if ( section == 1 ) { |
1087 | return "Description" ; |
1088 | } |
1089 | if ( section == 2 ) { |
1090 | return "Total" ; |
1091 | } |
1092 | int col = section - 3; |
1093 | switch ( m_periodtype ) { |
1094 | case Period_Day: { |
1095 | return startDate().addDays( col ); |
1096 | } |
1097 | case Period_Week: { |
1098 | const KCalendarSystem * calendar = KGlobal::locale()->calendar(); |
1099 | #if KDE_IS_VERSION(4,7,0) |
1100 | return calendar->week(startDate().addDays( ( col ) * 7 )); |
1101 | #else |
1102 | return calendar->weekNumber(startDate().addDays( ( col ) * 7 )); |
1103 | #endif |
1104 | } |
1105 | case Period_Month: { |
1106 | int days = startDate().daysInMonth() - startDate().day() + 1; |
1107 | QDate start = startDate(); |
1108 | for ( int i = 0; i < col; ++i ) { |
1109 | start = start.addDays( days ); |
1110 | days = start.daysInMonth(); |
1111 | } |
1112 | return start.month(); |
1113 | } |
1114 | default: |
1115 | return section; |
1116 | break; |
1117 | } |
1118 | return QVariant(); |
1119 | } |
1120 | if ( role == Qt::ToolTipRole ) { |
1121 | switch ( section ) { |
1122 | case 0: return ToolTip::accountName(); |
1123 | case 1: return ToolTip::accountDescription(); |
1124 | case 2: return i18n( "The total cost for the account shown as: Actual cost [ Planned cost ]" ); |
1125 | default: return QVariant(); |
1126 | } |
1127 | } |
1128 | if ( role == Qt::TextAlignmentRole ) { |
1129 | switch ( section ) { |
1130 | case 0: return QVariant(); |
1131 | case 1: return QVariant(); |
1132 | default: return (int)(Qt::AlignRight|Qt::AlignVCenter); |
1133 | } |
1134 | return QVariant(); |
1135 | } |
1136 | } |
1137 | return ItemModelBase::headerData(section, orientation, role); |
1138 | } |
1139 | |
1140 | Account *CostBreakdownItemModel::account( const QModelIndex &index ) const |
1141 | { |
1142 | return static_cast<Account*>( index.internalPointer() ); |
1143 | } |
1144 | |
1145 | void CostBreakdownItemModel::slotAccountChanged( Account *account ) |
1146 | { |
1147 | Q_UNUSED(account); |
1148 | fetchData(); |
1149 | foreach ( Account *a, m_plannedCostMap.keys() ) { |
1150 | QModelIndex idx1 = index( a ); |
1151 | QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) ); |
1152 | //kDebug(planDbg())<<a->name()<<idx1<<idx2; |
1153 | emit dataChanged( idx1, idx2 ); |
1154 | } |
1155 | } |
1156 | |
1157 | |
1158 | } // namespace KPlato |
1159 | |
1160 | #include "kptaccountsmodel.moc" |
1161 | |