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 "kptcalendarmodel.h"
21
22#include "kptglobal.h"
23#include "kptcommonstrings.h"
24#include "kptcommand.h"
25#include "kptitemmodelbase.h"
26#include "kptcalendar.h"
27#include "kptduration.h"
28#include "kptnode.h"
29#include "kptproject.h"
30#include "kpttask.h"
31#include "kptdatetime.h"
32#include "kcalendar/kdatetable.h"
33#include "kptdebug.h"
34
35#include <QMimeData>
36#include <QList>
37#include <QObject>
38#include <QPainter>
39
40#include <kglobal.h>
41#include <klocale.h>
42#include <kcalendarsystem.h>
43#include <ksystemtimezone.h>
44#include <ktimezone.h>
45#include <kdeversion.h>
46
47
48namespace KPlato
49{
50
51
52//-----------------------------------------
53CalendarDayItemModelBase::CalendarDayItemModelBase( QObject *parent )
54 : ItemModelBase( parent ),
55 m_calendar( 0 )
56{
57}
58
59CalendarDayItemModelBase::~CalendarDayItemModelBase()
60{
61}
62
63void CalendarDayItemModelBase::slotCalendarToBeRemoved( const Calendar *calendar )
64{
65 if ( calendar && calendar == m_calendar ) {
66 setCalendar( 0 );
67 }
68}
69
70
71void CalendarDayItemModelBase::setCalendar( Calendar *calendar )
72{
73 m_calendar = calendar;
74}
75
76void CalendarDayItemModelBase::setProject( Project *project )
77{
78 setCalendar( 0 );
79 if ( m_project ) {
80 disconnect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) );
81 }
82 m_project = project;
83 if ( project ) {
84 connect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) );
85 }
86 reset();
87}
88
89
90//-------------------------------------
91CalendarItemModel::CalendarItemModel( QObject *parent )
92 : ItemModelBase( parent ),
93 m_calendar( 0 )
94{
95}
96
97CalendarItemModel::~CalendarItemModel()
98{
99}
100
101const QMetaEnum CalendarItemModel::columnMap() const
102{
103 return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") );
104}
105
106void CalendarItemModel::slotCalendarToBeInserted( const Calendar *parent, int row )
107{
108 //kDebug(planDbg())<<(parent?parent->name():"Top level")<<","<<row;
109 Q_ASSERT( m_calendar == 0 );
110 m_calendar = const_cast<Calendar *>(parent);
111 beginInsertRows( index( parent ), row, row );
112}
113
114void CalendarItemModel::slotCalendarInserted( const Calendar *calendar )
115{
116 //kDebug(planDbg())<<calendar->name();
117 Q_ASSERT( calendar->parentCal() == m_calendar );
118#ifdef NDEBUG
119 Q_UNUSED(calendar)
120#endif
121
122 endInsertRows();
123 m_calendar = 0;
124}
125
126void CalendarItemModel::slotCalendarToBeRemoved( const Calendar *calendar )
127{
128 //kDebug(planDbg())<<calendar->name();
129 int row = index( calendar ).row();
130 beginRemoveRows( index( calendar->parentCal() ), row, row );
131}
132
133void CalendarItemModel::slotCalendarRemoved( const Calendar * )
134{
135 //kDebug(planDbg())<<calendar->name();
136 endRemoveRows();
137}
138
139void CalendarItemModel::setProject( Project *project )
140{
141 if ( m_project ) {
142 disconnect( m_project , SIGNAL(calendarChanged(Calendar*)), this, SLOT(slotCalendarChanged(Calendar*)) );
143
144 disconnect( m_project, SIGNAL(calendarAdded(const Calendar*)), this, SLOT(slotCalendarInserted(const Calendar*)) );
145 disconnect( m_project, SIGNAL(calendarToBeAdded(const Calendar*,int)), this, SLOT(slotCalendarToBeInserted(const Calendar*,int)) );
146
147 disconnect( m_project, SIGNAL(calendarRemoved(const Calendar*)), this, SLOT(slotCalendarRemoved(const Calendar*)) );
148 disconnect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) );
149 }
150 m_project = project;
151 if ( project ) {
152 connect( m_project, SIGNAL(calendarChanged(Calendar*)), this, SLOT(slotCalendarChanged(Calendar*)) );
153
154 connect( m_project, SIGNAL(calendarAdded(const Calendar*)), this, SLOT(slotCalendarInserted(const Calendar*)) );
155 connect( m_project, SIGNAL(calendarToBeAdded(const Calendar*,int)), this, SLOT(slotCalendarToBeInserted(const Calendar*,int)) );
156
157 connect( m_project, SIGNAL(calendarRemoved(const Calendar*)), this, SLOT(slotCalendarRemoved(const Calendar*)) );
158 connect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) );
159 }
160 reset();
161}
162
163Qt::ItemFlags CalendarItemModel::flags( const QModelIndex &index ) const
164{
165 Qt::ItemFlags flags = ItemModelBase::flags( index );
166 if ( !m_readWrite ) {
167 return flags &= ~Qt::ItemIsEditable;
168 }
169 flags |= Qt::ItemIsDropEnabled;
170 if ( !index.isValid() ) {
171 return flags;
172 }
173 flags |= Qt::ItemIsDragEnabled;
174 if ( calendar ( index ) ) {
175 switch ( index.column() ) {
176 case Name:
177 flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
178 break;
179 case TimeZone:
180 if ( parent( index ).isValid() ) {
181 flags &= ~Qt::ItemIsEditable;
182 } else {
183 flags |= Qt::ItemIsEditable;
184 }
185 break;
186 default:
187 flags |= Qt::ItemIsEditable;
188 break;
189 }
190 }
191 return flags;
192}
193
194
195QModelIndex CalendarItemModel::parent( const QModelIndex &index ) const
196{
197 if ( !index.isValid() || m_project == 0 ) {
198 return QModelIndex();
199 }
200 //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column();
201 Calendar *a = calendar( index );
202 if ( a == 0 ) {
203 return QModelIndex();
204 }
205 Calendar *par = a->parentCal();
206 if ( par ) {
207 a = par->parentCal();
208 int row = -1;
209 if ( a ) {
210 row = a->indexOf( par );
211 } else {
212 row = m_project->indexOf( par );
213 }
214 //kDebug(planDbg())<<par->name()<<":"<<row;
215 return createIndex( row, 0, par );
216 }
217 return QModelIndex();
218}
219
220QModelIndex CalendarItemModel::index( int row, int column, const QModelIndex &parent ) const
221{
222 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
223 return QModelIndex();
224 }
225 Calendar *par = calendar( parent );
226 if ( par == 0 ) {
227 if ( row < m_project->calendars().count() ) {
228 return createIndex( row, column, m_project->calendars().at( row ) );
229 }
230 } else if ( row < par->calendars().count() ) {
231 return createIndex( row, column, par->calendars().at( row ) );
232 }
233 return QModelIndex();
234}
235
236QModelIndex CalendarItemModel::index( const Calendar *calendar, int column ) const
237{
238 if ( m_project == 0 || calendar == 0 ) {
239 return QModelIndex();
240 }
241 Calendar *a = const_cast<Calendar*>(calendar);
242 int row = -1;
243 Calendar *par = a->parentCal();
244 if ( par == 0 ) {
245 row = m_project->calendars().indexOf( a );
246 } else {
247 row = par->indexOf( a );
248 }
249 if ( row == -1 ) {
250 return QModelIndex();
251 }
252 return createIndex( row, column, a );
253
254}
255
256int CalendarItemModel::columnCount( const QModelIndex & ) const
257{
258 return columnMap().keyCount();
259}
260
261int CalendarItemModel::rowCount( const QModelIndex &parent ) const
262{
263 if ( m_project == 0 ) {
264 return 0;
265 }
266 Calendar *par = calendar( parent );
267 if ( par == 0 ) {
268 return m_project->calendars().count();
269 }
270 return par->calendars().count();
271}
272
273QVariant CalendarItemModel::name( const Calendar *a, int role ) const
274{
275 //kDebug(planDbg())<<res->name()<<","<<role;
276 switch ( role ) {
277 case Qt::DisplayRole:
278 case Qt::EditRole:
279 return a->name();
280 case Qt::ToolTipRole:
281 if ( a->isDefault() ) {
282 return i18nc( "1=calendar name", "%1 (Default calendar)", a->name() );
283 }
284 return a->name();
285 case Qt::StatusTipRole:
286 case Qt::WhatsThisRole:
287 return QVariant();
288 case Qt::CheckStateRole:
289 return a->isDefault() ? Qt::Checked : Qt::Unchecked;
290 }
291 return QVariant();
292}
293
294bool CalendarItemModel::setName( Calendar *a, const QVariant &value, int role )
295{
296 switch ( role ) {
297 case Qt::EditRole:
298 if ( value.toString() != a->name() ) {
299 emit executeCommand( new CalendarModifyNameCmd( a, value.toString(), kundo2_i18n( "Modify calendar name" ) ) );
300 return true;
301 }
302 break;
303 case Qt::CheckStateRole: {
304 switch ( value.toInt() ) {
305 case Qt::Unchecked:
306 if ( a->isDefault() ) {
307 emit executeCommand( new ProjectModifyDefaultCalendarCmd( m_project, 0, kundo2_i18n( "De-select as default calendar" ) ) );
308 return true;
309 }
310 break;
311 case Qt::Checked:
312 if ( ! a->isDefault() ) {
313 emit executeCommand( new ProjectModifyDefaultCalendarCmd( m_project, a, kundo2_i18n( "Select as default calendar" ) ) );
314 return true;
315 }
316 break;
317 default: break;
318 }
319 }
320 default: break;
321 }
322 return false;
323}
324
325QVariant CalendarItemModel::timeZone( const Calendar *a, int role ) const
326{
327 //kDebug(planDbg())<<res->name()<<","<<role;
328 switch ( role ) {
329 case Qt::DisplayRole:
330 case Qt::EditRole:
331 case Qt::ToolTipRole:
332 return i18n( a->timeZone().name().toUtf8() );
333 case Role::EnumList: {
334 QStringList lst;
335 foreach ( const QString &s, KSystemTimeZones::timeZones()->zones().keys() ) {
336 lst << i18n( s.toUtf8() );
337 }
338 lst.sort();
339 return lst;
340 }
341 case Role::EnumListValue: {
342 QStringList lst = timeZone( a, Role::EnumList ).toStringList();
343 return lst.indexOf( i18n ( a->timeZone().name().toUtf8() ) );
344 }
345 case Qt::StatusTipRole:
346 case Qt::WhatsThisRole:
347 return QVariant();
348 }
349 return QVariant();
350}
351
352bool CalendarItemModel::setTimeZone( Calendar *a, const QVariant &value, int role )
353{
354 switch ( role ) {
355 case Qt::EditRole: {
356 if ( timeZone( a, Role::EnumListValue ) == value.toInt() ) {
357 return false;
358 }
359 QStringList lst = timeZone( a, Role::EnumList ).toStringList();
360 QString name = lst.value( value.toInt() );
361 KTimeZone tz;
362 foreach ( const QString &s, KSystemTimeZones::timeZones()->zones().keys() ) {
363 if ( name == i18n( s.toUtf8() ) ) {
364 tz = KSystemTimeZones::zone( s );
365 break;
366 }
367 }
368 if ( !tz.isValid() ) {
369 return false;
370 }
371 emit executeCommand( new CalendarModifyTimeZoneCmd( a, tz, kundo2_i18n( "Modify calendar timezone" ) ) );
372 return true;
373 }
374 }
375 return false;
376}
377
378QVariant CalendarItemModel::data( const QModelIndex &index, int role ) const
379{
380 QVariant result;
381 Calendar *a = calendar( index );
382 if ( a == 0 ) {
383 return QVariant();
384 }
385 switch ( index.column() ) {
386 case Name: result = name( a, role ); break;
387 case TimeZone: result = timeZone( a, role ); break;
388 default:
389 kDebug(planDbg())<<"data: invalid display value column"<<index.column();
390 return QVariant();
391 }
392 return result;
393}
394
395bool CalendarItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
396{
397 if ( ! index.isValid() ) {
398 return ItemModelBase::setData( index, value, role );
399 }
400 if ( ( flags( index ) &( Qt::ItemIsEditable | Qt::CheckStateRole ) ) == 0 ) {
401 Q_ASSERT( true );
402 return false;
403 }
404 Calendar *a = calendar( index );
405 switch (index.column()) {
406 case Name: return setName( a, value, role );
407 case TimeZone: return setTimeZone( a, value, role );
408 default:
409 kWarning()<<"data: invalid display value column "<<index.column();
410 return false;
411 }
412 return false;
413}
414
415QVariant CalendarItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
416{
417 if ( orientation == Qt::Horizontal ) {
418 if ( role == Qt::DisplayRole ) {
419 switch ( section ) {
420 case Name: return i18nc( "@title:column", "Name" );
421 case TimeZone: return i18nc( "@title:column", "Timezone" );
422 default: return QVariant();
423 }
424 } else if ( role == Qt::TextAlignmentRole ) {
425 switch (section) {
426 default: return QVariant();
427 }
428 }
429 }
430 if ( role == Qt::ToolTipRole ) {
431 switch ( section ) {
432 case Name: return ToolTip::calendarName();
433 case TimeZone: return ToolTip::calendarTimeZone();
434 default: return QVariant();
435 }
436 }
437 return ItemModelBase::headerData(section, orientation, role);
438}
439
440Calendar *CalendarItemModel::calendar( const QModelIndex &index ) const
441{
442 return static_cast<Calendar*>( index.internalPointer() );
443}
444
445void CalendarItemModel::slotCalendarChanged( Calendar *calendar )
446{
447 Calendar *par = calendar->parentCal();
448 if ( par ) {
449 int row = par->indexOf( calendar );
450 emit dataChanged( createIndex( row, 0, calendar ), createIndex( row, columnCount() - 1, calendar ) );
451 } else {
452 int row = m_project->indexOf( calendar );
453 emit dataChanged( createIndex( row, 0, calendar ), createIndex( row, columnCount() - 1, calendar ) );
454 }
455}
456
457Qt::DropActions CalendarItemModel::supportedDropActions() const
458{
459 return Qt::CopyAction | Qt::MoveAction;
460}
461
462
463QStringList CalendarItemModel::mimeTypes() const
464{
465 return QStringList() << "application/x-vnd.kde.plan.calendarid.internal";
466}
467
468QMimeData *CalendarItemModel::mimeData( const QModelIndexList & indexes ) const
469{
470 QMimeData *m = new QMimeData();
471 QByteArray encodedData;
472 QDataStream stream(&encodedData, QIODevice::WriteOnly);
473 QList<int> rows;
474 foreach (const QModelIndex &index, indexes) {
475 if ( index.isValid() && !rows.contains( index.row() ) ) {
476 kDebug(planDbg())<<index.row();
477 Calendar *c = calendar( index );
478 if ( c ) {
479 stream << c->id();
480 }
481 }
482 }
483 m->setData("application/x-vnd.kde.plan.calendarid.internal", encodedData);
484 return m;
485}
486
487bool CalendarItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent )
488{
489 kDebug(planDbg())<<action<<row;
490 if (action == Qt::IgnoreAction) {
491 return true;
492 }
493 if ( !data->hasFormat( "application/x-vnd.kde.plan.calendarid.internal" ) ) {
494 return false;
495 }
496 if ( action == Qt::MoveAction ) {
497 kDebug(planDbg())<<"MoveAction";
498
499 QByteArray encodedData = data->data( "application/x-vnd.kde.plan.calendarid.internal" );
500 QDataStream stream(&encodedData, QIODevice::ReadOnly);
501 Calendar *par = 0;
502 if ( parent.isValid() ) {
503 par = calendar( parent );
504 }
505 MacroCommand *cmd = 0;
506 QList<Calendar*> lst = calendarList( stream );
507 foreach ( Calendar *c, lst ) {
508 if ( c->parentCal() != par ) {
509 if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Re-parent calendar" ) );
510 cmd->addCommand( new CalendarModifyParentCmd( m_project, c, par ) );
511 } else {
512 if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move calendar" ) );
513 cmd->addCommand( new CalendarMoveCmd( m_project, c, row, par ) );
514 }
515 }
516 if ( cmd ) {
517 emit executeCommand( cmd );
518 return true;
519 }
520 //kDebug(planDbg())<<row<<","<<column<<" parent="<<parent.row()<<","<<parent.column()<<":"<<par->name();
521 }
522 return false;
523}
524
525QList<Calendar*> CalendarItemModel::calendarList( QDataStream &stream ) const
526{
527 QList<Calendar*> lst;
528 while (!stream.atEnd()) {
529 QString id;
530 stream >> id;
531 Calendar *c = m_project->findCalendar( id );
532 if ( c ) {
533 lst << c;
534 }
535 }
536 return lst;
537}
538
539bool CalendarItemModel::dropAllowed( Calendar *on, const QMimeData *data )
540{
541 kDebug(planDbg())<<on<<data->hasFormat("application/x-vnd.kde.plan.calendarid.internal");
542 if ( !data->hasFormat("application/x-vnd.kde.plan.calendarid.internal") ) {
543 return false;
544 }
545 if ( on == 0 && ! ( flags( QModelIndex() ) & (int)Qt::ItemIsDropEnabled ) ) {
546 return false;
547 }
548 QByteArray encodedData = data->data( "application/x-vnd.kde.plan.calendarid.internal" );
549 QDataStream stream(&encodedData, QIODevice::ReadOnly);
550 QList<Calendar*> lst = calendarList( stream );
551 foreach ( Calendar *c, lst ) {
552 if ( (flags( index( c ) ) & (int)Qt::ItemIsDropEnabled) == 0 ) {
553 return false;
554 }
555 if ( on != 0 && on == c->parentCal() ) {
556 return false;
557 }
558 if ( on != 0 && ( on == c || on->isChildOf( c ) ) ) {
559 return false;
560 }
561 }
562 return true;
563}
564
565QModelIndex CalendarItemModel::insertCalendar ( Calendar *calendar, int pos, Calendar *parent )
566{
567 //kDebug(planDbg())<<calendar<<pos<<parent;
568 emit executeCommand( new CalendarAddCmd( m_project, calendar, pos, parent, kundo2_i18n( "Add calendar" ) ) );
569 int row = -1;
570 if ( parent ) {
571 row = parent->indexOf( calendar );
572 } else {
573 row = m_project->indexOf( calendar );
574 }
575 if ( row != -1 ) {
576 //kDebug(planDbg())<<"Inserted:"<<calendar->name()<<"row="<<row;
577 return createIndex( row, 0, calendar );
578 }
579 return QModelIndex();
580}
581
582void CalendarItemModel::removeCalendar( QList<Calendar *> /*lst*/ )
583{
584}
585
586void CalendarItemModel::removeCalendar( Calendar *calendar )
587{
588 if ( calendar == 0 ) {
589 return;
590 }
591 emit executeCommand( new CalendarRemoveCmd( m_project, calendar, kundo2_i18n( "Delete calendar" ) ) );
592}
593
594
595//------------------------------------------
596CalendarDayItemModel::CalendarDayItemModel( QObject *parent )
597 : CalendarDayItemModelBase( parent )
598{
599}
600
601CalendarDayItemModel::~CalendarDayItemModel()
602{
603}
604
605void CalendarDayItemModel::slotWorkIntervalAdded( CalendarDay *day, TimeInterval *ti )
606{
607 Q_UNUSED(ti);
608 //kDebug(planDbg())<<day<<","<<ti;
609 int c = m_calendar->indexOfWeekday( day );
610 if ( c == -1 ) {
611 return;
612 }
613 dataChanged( createIndex( 0, c, day ), createIndex( 0, c, day ) );
614}
615
616void CalendarDayItemModel::slotWorkIntervalRemoved( CalendarDay *day, TimeInterval *ti )
617{
618 Q_UNUSED(ti);
619 int c = m_calendar->indexOfWeekday( day );
620 if ( c == -1 ) {
621 return;
622 }
623 dataChanged( createIndex( 0, c, day ), createIndex( 0, c, day ) );
624}
625
626void CalendarDayItemModel::slotDayChanged( CalendarDay *day )
627{
628 int c = m_calendar->indexOfWeekday( day );
629 if ( c == -1 ) {
630 return;
631 }
632 kDebug(planDbg())<<day<<", "<<c;
633 emit dataChanged( createIndex( 0, c, day ), createIndex( 0, c, day ) );
634}
635
636void CalendarDayItemModel::slotTimeIntervalChanged( TimeInterval *ti )
637{
638 Q_UNUSED(ti);
639/* CalendarDay *d = parentDay( ti );
640 if ( d == 0 ) {
641 return;
642 }
643 int row = d->indexOf( ti );
644 emit dataChanged( createIndex( row, 0, ti ), createIndex( row, columnCount() - 1, ti ) );*/
645}
646
647void CalendarDayItemModel::setCalendar( Calendar *calendar )
648{
649 //kDebug(planDbg())<<m_calendar<<" -->"<<calendar;
650 if ( m_calendar ) {
651 disconnect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SLOT(slotDayChanged(CalendarDay*)) );
652 disconnect( m_calendar, SIGNAL(changed(TimeInterval*)), this, SLOT(slotTimeIntervalChanged(TimeInterval*)) );
653
654 disconnect( m_calendar, SIGNAL(workIntervalAdded(CalendarDay*,TimeInterval*)), this, SLOT(slotWorkIntervalAdded(CalendarDay*,TimeInterval*)) );
655 disconnect( m_calendar, SIGNAL(workIntervalRemoved(CalendarDay*,TimeInterval*)), this, SLOT(slotWorkIntervalRemoved(CalendarDay*,TimeInterval*)) );
656 }
657 m_calendar = calendar;
658 if ( calendar ) {
659 connect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SLOT(slotDayChanged(CalendarDay*)) );
660 connect( m_calendar, SIGNAL(changed(TimeInterval*)), this, SLOT(slotTimeIntervalChanged(TimeInterval*)) );
661
662 connect( m_calendar, SIGNAL(workIntervalAdded(CalendarDay*,TimeInterval*)), this, SLOT(slotWorkIntervalAdded(CalendarDay*,TimeInterval*)) );
663 connect( m_calendar, SIGNAL(workIntervalRemoved(CalendarDay*,TimeInterval*)), this, SLOT(slotWorkIntervalRemoved(CalendarDay*,TimeInterval*)) );
664 }
665 reset();
666}
667
668Qt::ItemFlags CalendarDayItemModel::flags( const QModelIndex &index ) const
669{
670 Qt::ItemFlags flags = ItemModelBase::flags( index );
671 if ( !m_readWrite ) {
672 return flags &= ~Qt::ItemIsEditable;
673 }
674 return flags |= Qt::ItemIsEditable;
675}
676
677QModelIndex CalendarDayItemModel::parent( const QModelIndex &index ) const
678{
679 Q_UNUSED(index);
680 return QModelIndex();
681}
682
683bool CalendarDayItemModel::hasChildren( const QModelIndex &parent ) const
684{
685 //kDebug(planDbg())<<parent.internalPointer()<<":"<<parent.row()<<","<<parent.column();
686 if ( m_project == 0 || m_calendar == 0 ) {
687 return false;
688 }
689 return ! parent.isValid();
690}
691
692QModelIndex CalendarDayItemModel::index( int row, int column, const QModelIndex &par ) const
693{
694 if ( m_project == 0 || m_calendar == 0 ) {
695 return QModelIndex();
696 }
697 if ( par.isValid() ) {
698 return QModelIndex();
699 }
700 CalendarDay *d = m_calendar->weekday( column + 1 ); // weekdays are 1..7
701 if ( d == 0 ) {
702 return QModelIndex();
703 }
704 return createIndex( row, column, d );
705}
706
707QModelIndex CalendarDayItemModel::index( const CalendarDay *d) const
708{
709 if ( m_project == 0 || m_calendar == 0 ) {
710 return QModelIndex();
711 }
712 int col = m_calendar->indexOfWeekday( d );
713 if ( col == -1 ) {
714 return QModelIndex();
715 }
716 return createIndex( 0, col, const_cast<CalendarDay*>( d ) );
717}
718
719int CalendarDayItemModel::columnCount( const QModelIndex &/*parent*/ ) const
720{
721 return 7;
722}
723
724int CalendarDayItemModel::rowCount( const QModelIndex &parent ) const
725{
726 if ( m_project == 0 || m_calendar == 0 || parent.isValid() ) {
727 return 0;
728 }
729 return 1;
730}
731
732QVariant CalendarDayItemModel::name( int weekday, int role ) const
733{
734 //kDebug(planDbg())<<res->name()<<","<<role;
735 switch ( role ) {
736 case Qt::DisplayRole:
737 if ( weekday >= 1 && weekday <= 7 ) {
738 return KGlobal::locale()->calendar()->weekDayName( weekday, KCalendarSystem::ShortDayName );
739 }
740 break;
741 case Qt::ToolTipRole:
742 if ( weekday >= 1 && weekday <= 7 ) {
743 return KGlobal::locale()->calendar()->weekDayName( weekday );
744 }
745 break;
746 case Qt::EditRole:
747 case Qt::StatusTipRole:
748 case Qt::WhatsThisRole:
749 return QVariant();
750 }
751 return QVariant();
752}
753
754QVariant CalendarDayItemModel::dayState( const CalendarDay *d, int role ) const
755{
756 switch ( role ) {
757 case Qt::DisplayRole:
758 switch ( d->state() ) {
759 case CalendarDay::Undefined: return i18nc( "Undefined", "U" );
760 case CalendarDay::NonWorking: return i18nc( "NonWorking", "NW" );
761 case CalendarDay::Working: return i18nc( "Working", "W" );
762 }
763 break;
764 case Qt::ToolTipRole:
765 return CalendarDay::stateToString( d->state(), true );
766 case Role::EnumList: {
767 QStringList lst = CalendarDay::stateList( true );
768 return lst;
769 }
770 case Qt::EditRole:
771 case Role::EnumListValue: {
772 return d->state();
773 }
774 case Qt::TextAlignmentRole:
775 return Qt::AlignCenter;
776 case Qt::StatusTipRole:
777 case Qt::WhatsThisRole:
778 return QVariant();
779 case Role::EditorType:
780 return Delegate::EnumEditor;
781 }
782 return QVariant();
783}
784bool CalendarDayItemModel::setDayState( CalendarDay *d, const QVariant &value, int role )
785{
786 //kDebug(planDbg());
787 switch ( role ) {
788 case Qt::EditRole:
789 int v = value.toInt();
790 emit executeCommand( new CalendarModifyStateCmd( m_calendar, d, static_cast<CalendarDay::State>( v ), kundo2_i18n( "Modify calendar state" ) ) );
791 return true;
792 }
793 return false;
794}
795
796QVariant CalendarDayItemModel::workDuration( const CalendarDay *day, int role ) const
797{
798 //kDebug(planDbg())<<day->date()<<","<<role;
799 switch ( role ) {
800 case Qt::DisplayRole: {
801 if ( day->state() == CalendarDay::Working ) {
802 return KGlobal::locale()->formatNumber( day->workDuration().toDouble( Duration::Unit_h ), 1 );
803 }
804 return QVariant();
805 }
806 case Qt::ToolTipRole: {
807 if ( day->state() == CalendarDay::Working ) {
808 KLocale *l = KGlobal::locale();
809 QStringList tip;
810 foreach ( TimeInterval *i, day->timeIntervals() ) {
811 tip << i18nc( "1=time 2=The number of hours of work duration (non integer)", "%1, %2 hours", l->formatTime( i->startTime() ), l->formatNumber( i->hours() ) );
812 }
813 return tip.join( "\n" );
814 }
815 return QVariant();
816 }
817 case Qt::EditRole:
818 break;
819 case Qt::StatusTipRole:
820 case Qt::WhatsThisRole:
821 return QVariant();
822 case Qt::TextAlignmentRole:
823 return Qt::AlignCenter;
824 }
825 return QVariant();
826}
827
828QVariant CalendarDayItemModel::data( const QModelIndex &index, int role ) const
829{
830 QVariant result;
831 if ( ! index.isValid() ) {
832 return result;
833 }
834 CalendarDay *d = day( index );
835 if ( d == 0 ) {
836 return QVariant();
837 }
838 switch ( role ) {
839 case Qt::DisplayRole: {
840 switch ( d->state() ) {
841 case CalendarDay::Working:
842 result = workDuration( d, role );
843 break;
844 case CalendarDay::NonWorking:
845 result = dayState( d, role );
846 break;
847 default: {
848 // Return parent value (if any)
849 for ( Calendar *c = m_calendar->parentCal(); c != 0; c = c->parentCal() ) {
850 d = c->weekday( index.column() + 1 );
851 Q_ASSERT( d );
852 if ( d->state() == CalendarDay::Working ) {
853 return workDuration( d, role );
854 }
855 if ( d->state() == CalendarDay::NonWorking ) {
856 return dayState( d, role );
857 }
858 }
859 break;
860 }
861 }
862 break;
863 }
864 case Qt::ToolTipRole: {
865 if ( d->state() == CalendarDay::Undefined ) {
866 return i18nc( "@info:tooltip", "Undefined" );
867 }
868 if ( d->state() == CalendarDay::NonWorking ) {
869 return i18nc( "@info:tooltip", "Non-working" );
870 }
871 KLocale *l = KGlobal::locale();
872 QStringList tip;
873 foreach ( TimeInterval *i, d->timeIntervals() ) {
874 tip << i18nc( "@info:tooltip 1=time 2=The work duration (non integer)", "%1, %2", l->formatLocaleTime( i->startTime(), KLocale::TimeWithoutSeconds ), l->formatDuration( i->second ) );
875 }
876 return tip.join( "\n" );
877 }
878 case Qt::FontRole: {
879 if ( d->state() != CalendarDay::Undefined ) {
880 return QVariant();
881 }
882 // If defined in parent, return italic
883 for ( Calendar *c = m_calendar->parentCal(); c != 0; c = c->parentCal() ) {
884 d = c->weekday( index.column() + 1 );
885 Q_ASSERT( d );
886 if ( d->state() != CalendarDay::Undefined ) {
887 QFont f;
888 f.setItalic( true );
889 return f;
890 }
891 }
892 break;
893 }
894 }
895 return result;
896}
897
898bool CalendarDayItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
899{
900 return ItemModelBase::setData( index, value, role );
901}
902
903QVariant CalendarDayItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
904{
905 if ( orientation == Qt::Horizontal ) {
906 if ( role == Qt::DisplayRole ) {
907 switch ( section ) {
908 case 0:
909 case 1:
910 case 2:
911 case 3:
912 case 4:
913 case 5:
914 case 6:
915 return name( section + 1, role );
916 default:
917 return QVariant();
918 }
919 } else if ( role == Qt::TextAlignmentRole ) {
920 switch (section) {
921 default: return Qt::AlignCenter;
922 }
923 }
924 }
925 if ( role == Qt::ToolTipRole ) {
926 switch ( section ) {
927 /* case 0: return ToolTip::Calendar Name;*/
928 default: return QVariant();
929 }
930 }
931 return ItemModelBase::headerData(section, orientation, role);
932}
933
934CalendarDay *CalendarDayItemModel::day( const QModelIndex &index ) const
935{
936 return static_cast<CalendarDay*>( index.internalPointer() );
937}
938
939QAbstractItemDelegate *CalendarDayItemModel::createDelegate( int column, QWidget *parent ) const
940{
941 Q_UNUSED(parent);
942 switch ( column ) {
943 default: return 0;
944 }
945 return 0;
946}
947
948//-----------------------
949DateTableDataModel::DateTableDataModel( QObject *parent )
950 : KDateTableDataModel( parent ),
951 m_calendar( 0 )
952{
953}
954
955void DateTableDataModel::setCalendar( Calendar *calendar )
956{
957 if ( m_calendar ) {
958 disconnect( m_calendar, SIGNAL(dayAdded(CalendarDay*)), this, SIGNAL(reset()) );
959 disconnect( m_calendar, SIGNAL(dayRemoved(CalendarDay*)), this, SIGNAL(reset()) );
960 disconnect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SIGNAL(reset()) );
961 }
962 m_calendar = calendar;
963 if ( m_calendar ) {
964 connect( m_calendar, SIGNAL(dayAdded(CalendarDay*)), this, SIGNAL(reset()) );
965 connect( m_calendar, SIGNAL(dayRemoved(CalendarDay*)), this, SIGNAL(reset()) );
966 connect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SIGNAL(reset()) );
967 }
968 emit reset();
969}
970
971QVariant DateTableDataModel::data( const Calendar &cal, const QDate &date, int role ) const
972{
973 switch ( role ) {
974 case Qt::DisplayRole: {
975 CalendarDay *day = cal.findDay( date );
976 if ( day == 0 || day->state() == CalendarDay::Undefined ) {
977 if ( cal.parentCal() ) {
978 return data( *( cal.parentCal() ), date, role );
979 }
980 return "";
981 }
982 if ( day->state() == CalendarDay::NonWorking ) {
983 return i18nc( "NonWorking", "NW" );
984 }
985 double v;
986 v = day->workDuration().toDouble( Duration::Unit_h );
987 return KGlobal::locale()->formatNumber( v, 1 );
988 }
989 case Qt::TextAlignmentRole:
990 return (uint)( Qt::AlignHCenter | Qt::AlignBottom );
991 case Qt::FontRole: {
992 CalendarDay *day = cal.findDay( date );
993 if ( day && day->state() != CalendarDay::Undefined ) {
994 if ( &cal != m_calendar ) {
995 QFont f;
996 f.setItalic( true );
997 return f;
998 }
999 return QVariant();
1000 }
1001 if ( cal.parentCal() ) {
1002 return data( *( cal.parentCal() ), date, role );
1003 }
1004 break;
1005 }
1006 default:
1007 break;
1008 }
1009 return QVariant();
1010}
1011
1012QVariant DateTableDataModel::data( const QDate &date, int role, int dataType ) const
1013{
1014 //kDebug(planDbg())<<date<<role<<dataType;
1015 if ( role == Qt::ToolTipRole ) {
1016 if ( m_calendar == 0 ) {
1017 return QVariant();
1018 }
1019 CalendarDay *day = m_calendar->findDay( date );
1020 if ( day == 0 || day->state() == CalendarDay::Undefined ) {
1021 return i18nc( "@info:tooltip", "Undefined" );
1022 }
1023 if ( day->state() == CalendarDay::NonWorking ) {
1024 return i18nc( "@info:tooltip", "Non-working" );
1025 }
1026 KLocale *l = KGlobal::locale();
1027 QStringList tip;
1028 foreach ( TimeInterval *i, day->timeIntervals() ) {
1029 tip << i18nc( "@info:tooltip 1=time 2=The work duration (non integer)", "%1, %2", l->formatLocaleTime( i->startTime(), KLocale::TimeWithoutSeconds ), l->formatDuration( i->second ) );
1030 }
1031 return tip.join( "\n" );
1032 }
1033
1034 switch ( dataType ) {
1035 case -1: { //default (date)
1036 switch ( role ) {
1037 case Qt::DisplayRole: {
1038 return QVariant();
1039 }
1040 case Qt::TextAlignmentRole:
1041 return (uint)Qt::AlignLeft | Qt::AlignTop;
1042 case Qt::FontRole:
1043 break;//return QFont( "Helvetica", 6 );
1044 case Qt::BackgroundRole:
1045 break;//return QColor( "red" );
1046 default:
1047 break;
1048 }
1049 break;
1050 }
1051 case 0: {
1052 if ( m_calendar == 0 ) {
1053 return "";
1054 }
1055 return data( *m_calendar, date, role );
1056 }
1057 default:
1058 break;
1059 }
1060 return QVariant();
1061}
1062
1063QVariant DateTableDataModel::weekDayData( int day, int role ) const
1064{
1065 Q_UNUSED(day);
1066 Q_UNUSED(role);
1067 return QVariant();
1068}
1069
1070QVariant DateTableDataModel::weekNumberData( int week, int role ) const
1071{
1072 Q_UNUSED(week);
1073 Q_UNUSED(role);
1074 return QVariant();
1075}
1076
1077//-------------
1078DateTableDateDelegate::DateTableDateDelegate( QObject *parent )
1079 : KDateTableDateDelegate( parent )
1080{
1081}
1082
1083QRectF DateTableDateDelegate::paint( QPainter *painter, const StyleOptionViewItem &option, const QDate &date, KDateTableDataModel *model )
1084{
1085 //kDebug(planDbg())<<date;
1086 QRectF r;
1087 StyleOptionViewItem style = option;
1088 style.font.setPointSize( style.font.pointSize() - 2 );
1089 //kDebug(planDbg())<<" fonts: "<<option.font.pointSize()<<style.font.pointSize();
1090 r = KDateTableDateDelegate::paint( painter, style, date, model );
1091 if ( model == 0 ) {
1092 return r;
1093 }
1094 painter->save();
1095 //const KCalendarSystem * calendar = KGlobal::locale()->calendar();
1096
1097 painter->translate( r.width(), 0.0 );
1098 QRectF rect( 1, 1, option.rectF.right() - r.width(), option.rectF.bottom() );
1099 //kDebug(planDbg())<<" rects: "<<r<<rect;
1100
1101 QString text = model->data( date, Qt::DisplayRole, 0 ).toString();
1102 int align = model->data( date, Qt::TextAlignmentRole, 0 ).toInt();
1103 QFont f = option.font;
1104 QVariant v = model->data( date, Qt::FontRole, 0 );
1105 if ( v.isValid() ) {
1106 f = v.value<QFont>();
1107 }
1108 painter->setFont( f );
1109
1110 if ( option.state & QStyle::State_Selected ) {
1111 painter->setPen( option.palette.highlightedText().color() );
1112 } else {
1113 painter->setPen( option.palette.color( QPalette::Text ) );
1114 }
1115 painter->drawText(rect, align, text, &r);
1116
1117 painter->restore();
1118 return r;
1119}
1120
1121//-------------------------------------
1122CalendarExtendedItemModel::CalendarExtendedItemModel( QObject *parent )
1123 : CalendarItemModel( parent )
1124{
1125}
1126
1127Qt::ItemFlags CalendarExtendedItemModel::flags( const QModelIndex &index ) const
1128{
1129 Qt::ItemFlags flags = CalendarItemModel::flags( index );
1130 if ( ! m_readWrite || ! index.isValid() || calendar( index ) == 0 ) {
1131 return flags;
1132 }
1133 return flags |= Qt::ItemIsEditable;
1134}
1135
1136
1137QModelIndex CalendarExtendedItemModel::index( int row, int column, const QModelIndex &parent ) const
1138{
1139 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
1140 return QModelIndex();
1141 }
1142 Calendar *par = calendar( parent );
1143 if ( par == 0 ) {
1144 if ( row < m_project->calendars().count() ) {
1145 return createIndex( row, column, m_project->calendars().at( row ) );
1146 }
1147 } else if ( row < par->calendars().count() ) {
1148 return createIndex( row, column, par->calendars().at( row ) );
1149 }
1150 return QModelIndex();
1151}
1152
1153int CalendarExtendedItemModel::columnCount( const QModelIndex & ) const
1154{
1155 return CalendarItemModel::columnCount() + 2; // weekdays + date
1156}
1157
1158QVariant CalendarExtendedItemModel::data( const QModelIndex &index, int role ) const
1159{
1160 QVariant result;
1161 Calendar *a = calendar( index );
1162 if ( a == 0 ) {
1163 return QVariant();
1164 }
1165 int col = index.column() - CalendarItemModel::columnCount( index );
1166 if ( col < 0 ) {
1167 return CalendarItemModel::data( index, role );
1168 }
1169 switch ( col ) {
1170 default:
1171 kDebug(planDbg())<<"Fetching data from weekdays and date is not supported";
1172 break;
1173 }
1174 return result;
1175}
1176
1177bool CalendarExtendedItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
1178{
1179 int col = index.column() - CalendarItemModel::columnCount( index );
1180 if ( col < 0 ) {
1181 return CalendarItemModel::setData( index, value, role );
1182 }
1183 if ( ( flags( index ) &( Qt::ItemIsEditable ) ) == 0 ) {
1184 return false;
1185 }
1186 Calendar *cal = calendar( index );
1187 if ( cal == 0 || col > 2 ) {
1188 return false;
1189 }
1190 switch ( col ) {
1191 case 0: { // weekday
1192 if ( value.type() != QVariant::List ) {
1193 return false;
1194 }
1195 QVariantList lst = value.toList();
1196 if ( lst.count() < 2 ) {
1197 return false;
1198 }
1199 int wd = CalendarWeekdays::dayOfWeek( lst.at( 0 ).toString() );
1200 if ( wd < 1 || wd > 7 ) {
1201 return false;
1202 }
1203 CalendarDay *day = new CalendarDay();
1204 if ( lst.count() == 2 ) {
1205 QString state = lst.at( 1 ).toString();
1206 if ( state == "NonWorking" ) {
1207 day->setState( CalendarDay::NonWorking );
1208 } else if ( state == "Undefined" ) {
1209 day->setState( CalendarDay::Undefined );
1210 } else {
1211 delete day;
1212 return false;
1213 }
1214 CalendarModifyWeekdayCmd *cmd = new CalendarModifyWeekdayCmd( cal, wd, day, kundo2_i18n( "Modify calendar weekday" ) );
1215 emit executeCommand( cmd );
1216 return true;
1217 }
1218 if ( lst.count() % 2 == 0 ) {
1219 delete day;
1220 return false;
1221 }
1222 day->setState( CalendarDay::Working );
1223 for ( int i = 1; i < lst.count(); i = i + 2 ) {
1224 QTime t1 = lst.at( i ).toTime();
1225 QTime t2 = lst.at( i + 1 ).toTime();
1226 int length = t1.msecsTo( t2 );
1227 if ( t1 == QTime( 0, 0, 0 ) && t2 == t1 ) {
1228 length = 24 * 60 * 60 *1000;
1229 } else if ( length < 0 && t2 == QTime( 0, 0, 0 ) ) {
1230 length += 24 * 60 * 60 *1000;
1231 } else if ( length == 0 || ( length < 0 && t2 != QTime( 0, 0, 0 ) ) ) {
1232 delete day;
1233 return false;
1234 }
1235 length = qAbs( length );
1236 day->addInterval( t1, length );
1237 }
1238 CalendarModifyWeekdayCmd *cmd = new CalendarModifyWeekdayCmd( cal, wd, day, kundo2_i18n( "Modify calendar weekday" ) );
1239 emit executeCommand( cmd );
1240 return true;
1241 }
1242 case 1: { // day
1243 if ( value.type() != QVariant::List ) {
1244 return false;
1245 }
1246 CalendarDay *day = new CalendarDay();
1247 QVariantList lst = value.toList();
1248 if ( lst.count() < 2 ) {
1249 return false;
1250 }
1251 day->setDate( lst.at( 0 ).toDate() );
1252 if ( ! day->date().isValid() ) {
1253 delete day;
1254 return false;
1255 }
1256 if ( lst.count() == 2 ) {
1257 QString state = lst.at( 1 ).toString();
1258 if ( state == "NonWorking" ) {
1259 day->setState( CalendarDay::NonWorking );
1260 } else if ( state == "Undefined" ) {
1261 day->setState( CalendarDay::Undefined );
1262 } else {
1263 delete day;
1264 return false;
1265 }
1266 CalendarModifyDayCmd *cmd = new CalendarModifyDayCmd( cal, day, kundo2_i18n( "Modify calendar date" ) );
1267 emit executeCommand( cmd );
1268 return true;
1269 }
1270 if ( lst.count() % 2 == 0 ) {
1271 delete day;
1272 return false;
1273 }
1274 day->setState( CalendarDay::Working );
1275 for ( int i = 1; i < lst.count(); i = i + 2 ) {
1276 QTime t1 = lst.at( i ).toTime();
1277 QTime t2 = lst.at( i + 1 ).toTime();
1278 int length = t1.msecsTo( t2 );
1279 if ( t1 == QTime( 0, 0, 0 ) && t2 == t1 ) {
1280 length = 24 * 60 * 60 *1000;
1281 } else if ( length < 0 && t2 == QTime( 0, 0, 0 ) ) {
1282 length += 24 * 60 * 60 *1000;
1283 } else if ( length == 0 || ( length < 0 && t2 != QTime( 0, 0, 0 ) ) ) {
1284 delete day;
1285 return false;
1286 }
1287 length = qAbs( length );
1288 day->addInterval( t1, length );
1289 }
1290 CalendarModifyDayCmd *cmd = new CalendarModifyDayCmd( cal, day, kundo2_i18n( "Modify calendar date" ) );
1291 emit executeCommand( cmd );
1292 return true;
1293 }
1294 }
1295 return false;
1296}
1297
1298QVariant CalendarExtendedItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
1299{
1300 int col = section - CalendarItemModel::columnCount();
1301 if ( col < 0 ) {
1302 return CalendarItemModel::headerData( section, orientation, role );
1303 }
1304 if ( orientation == Qt::Horizontal ) {
1305 if ( role == Qt::DisplayRole ) {
1306 switch ( col ) {
1307 case 0: return i18nc( "@title:column", "Weekday" );
1308 case 1: return i18nc( "@title:column", "Date" );
1309 default: return QVariant();
1310 }
1311 } else if ( role == Qt::TextAlignmentRole ) {
1312 switch ( col ) {
1313 default: return QVariant();
1314 }
1315 }
1316 }
1317 if ( role == Qt::ToolTipRole ) {
1318 switch ( section ) {
1319 default: return QVariant();
1320 }
1321 }
1322 return QVariant();
1323}
1324
1325int CalendarExtendedItemModel::columnNumber(const QString& name) const
1326{
1327 QStringList lst;
1328 lst << "Weekday"
1329 << "Date";
1330 if ( lst.contains( name ) ) {
1331 return lst.indexOf( name ) + CalendarItemModel::columnCount();
1332 }
1333 return CalendarItemModel::columnMap().keyToValue( name.toUtf8() );
1334}
1335
1336} // namespace KPlato
1337
1338#include "kptcalendarmodel.moc"
1339