korganizer

kotodoview.cpp
1/*
2 This file is part of KOrganizer.
3
4 Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 As a special exception, permission is given to link this program
22 with any edition of TQt, and distribute the resulting executable,
23 without including the source code for TQt in the source distribution.
24*/
25
26#include <tqlayout.h>
27#include <tqheader.h>
28#include <tqcursor.h>
29#include <tqlabel.h>
30#include <tqtimer.h>
31
32#include <kdebug.h>
33#include <tdelocale.h>
34#include <tdeglobal.h>
35#include <kiconloader.h>
36#include <tdemessagebox.h>
37
38#include <libkcal/calhelper.h>
39#include <libkcal/icaldrag.h>
40#include <libkcal/vcaldrag.h>
41#include <libkcal/dndfactory.h>
43#include <libkcal/resourcecalendar.h>
44#include <libkcal/calfilter.h>
45#include <libkcal/incidenceformatter.h>
46
47#include <libtdepim/clicklineedit.h>
48#include <libtdepim/kdatepickerpopup.h>
49
50#include <libemailfunctions/email.h>
51
52#include "docprefs.h"
53
54#include "koincidencetooltip.h"
55#include "kodialogmanager.h"
56#include "kotodoview.h"
57#include "koprefs.h"
58#include "koglobals.h"
59using namespace KOrg;
60#include "kotodoviewitem.h"
61#include "kotodoview.moc"
62#ifndef KORG_NOPRINTER
63#include "kocorehelper.h"
64#include "calprinter.h"
65#endif
66
67KOTodoListViewToolTip::KOTodoListViewToolTip (TQWidget *parent,
68 Calendar *calendar,
69 KOTodoListView *lv )
70 :TQToolTip(parent), mCalendar( calendar )
71{
72 todolist=lv;
73}
74
75void KOTodoListViewToolTip::maybeTip( const TQPoint & pos)
76{
77 TQRect r;
78 int headerPos;
79 int col=todolist->header()->sectionAt(todolist->contentsX() + pos.x());
80 KOTodoViewItem *i=(KOTodoViewItem *)todolist->itemAt(pos);
81
82 /* Check wether a tooltip is necessary. */
83 if( i && KOPrefs::instance()->mEnableToolTips )
84 {
85
86 /* Calculate the rectangle. */
87 r=todolist->itemRect(i);
88 headerPos = todolist->header()->sectionPos(col)-todolist->contentsX();
89 r.setLeft( (headerPos < 0 ? 0 : headerPos) );
90 r.setRight(headerPos + todolist->header()->sectionSize(col));
91
92 /* Show the tip */
93 TQString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->todo(), TQDate(), true ) );;
94 if ( !tipText.isEmpty() ) {
95 tip(r, tipText);
96 }
97 }
98
99}
100
101
102
103KOTodoListView::KOTodoListView( TQWidget *parent, const char *name )
104 : TDEListView( parent, name ), mCalendar( 0 ), mChanger( 0 )
105{
106 mOldCurrent = 0;
107 mMousePressed = false;
108}
109
110KOTodoListView::~KOTodoListView()
111{
112}
113
114void KOTodoListView::setCalendar( Calendar *cal )
115{
116 mCalendar = cal;
117 setAcceptDrops( mCalendar );
118 viewport()->setAcceptDrops( mCalendar );
119}
120
121bool KOTodoListView::event(TQEvent *e)
122{
123 int tmp=0;
125
126 /* Checks for an ApplicationPaletteChange event and updates
127 * the small Progress bars to make therm have the right colors. */
128 if(e->type()==TQEvent::ApplicationPaletteChange)
129 {
130
131 TDEListView::event(e);
132 i=(KOTodoViewItem *)itemAtIndex(tmp);
133
134 while(i!=0)
135 {
136 i->construct();
137 tmp++;
138 i=(KOTodoViewItem *)itemAtIndex(tmp);
139 }
140
141 }
142
143 return (TDEListView::event(e) || e->type()==TQEvent::ApplicationPaletteChange);
144}
145
146void KOTodoListView::contentsDragEnterEvent(TQDragEnterEvent *e)
147{
148#ifndef KORG_NODND
149// kdDebug(5850) << "KOTodoListView::contentsDragEnterEvent" << endl;
150 if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
151 !TQTextDrag::canDecode( e ) ) {
152 e->ignore();
153 return;
154 }
155
156 mOldCurrent = currentItem();
157#endif
158}
159
160void KOTodoListView::contentsDragMoveEvent(TQDragMoveEvent *e)
161{
162#ifndef KORG_NODND
163// kdDebug(5850) << "KOTodoListView::contentsDragMoveEvent" << endl;
164
165 if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
166 !TQTextDrag::canDecode( e ) ) {
167 e->ignore();
168 return;
169 }
170
171 e->accept();
172#endif
173}
174
175void KOTodoListView::contentsDragLeaveEvent( TQDragLeaveEvent * )
176{
177#ifndef KORG_NODND
178// kdDebug(5850) << "KOTodoListView::contentsDragLeaveEvent" << endl;
179
180 setCurrentItem(mOldCurrent);
181 setSelected(mOldCurrent,true);
182#endif
183}
184
185void KOTodoListView::contentsDropEvent( TQDropEvent *e )
186{
187#ifndef KORG_NODND
188 kdDebug(5850) << "KOTodoListView::contentsDropEvent" << endl;
189
190 if ( !mCalendar || !mChanger ||
191 ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
192 !TQTextDrag::canDecode( e ) ) ) {
193 e->ignore();
194 return;
195 }
196
197 DndFactory factory( mCalendar );
198 Todo *todo = factory.createDropTodo(e);
199
200 if ( todo ) {
201 e->acceptAction();
202
203 KOTodoViewItem *destination =
204 (KOTodoViewItem *)itemAt(contentsToViewport(e->pos()));
205 Todo *destinationEvent = 0;
206 if (destination) destinationEvent = destination->todo();
207
208 Todo *existingTodo = mCalendar->todo(todo->uid());
209
210 if( existingTodo ) {
211 kdDebug(5850) << "Drop existing Todo " << existingTodo << " onto " << destinationEvent << endl;
212 Incidence *to = destinationEvent;
213 while(to) {
214 if (to->uid() == todo->uid()) {
215 KMessageBox::information(this,
216 i18n("Cannot move to-do to itself or a child of itself."),
217 i18n("Drop To-do"), "NoDropTodoOntoItself" );
218 delete todo;
219 return;
220 }
221 to = to->relatedTo();
222 }
223
224 Todo*oldTodo = existingTodo->clone();
225 if ( mChanger->beginChange( existingTodo, 0, TQString() ) ) {
226 existingTodo->setRelatedTo( destinationEvent );
227 mChanger->changeIncidence( oldTodo, existingTodo, KOGlobals::RELATION_MODIFIED, this );
228 mChanger->endChange( existingTodo, 0, TQString() );
229 } else {
230 KMessageBox::sorry( this, i18n("Unable to change to-do's parent, "
231 "because the to-do cannot be locked.") );
232 }
233 delete oldTodo;
234 delete todo;
235 } else {
236// kdDebug(5850) << "Drop new Todo" << endl;
237 todo->setRelatedTo(destinationEvent);
238 if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) {
239 KODialogManager::errorSaveIncidence( this, todo );
240 delete todo;
241 return;
242 }
243 }
244 } else {
245 TQString text;
246 KOTodoViewItem *todoi = dynamic_cast<KOTodoViewItem *>(itemAt( contentsToViewport(e->pos()) ));
247 if ( ! todoi ) {
248 // Not dropped on a todo item:
249 e->ignore();
250 kdDebug( 5850 ) << "KOTodoListView::contentsDropEvent(): Not dropped on a todo item" << endl;
251 kdDebug( 5850 ) << "TODO: Create a new todo with the given data" << endl;
252 // FIXME: Create a new todo with the given text/contact/whatever
253 } else if ( TQTextDrag::decode(e, text) ) {
254 //TQListViewItem *qlvi = itemAt( contentsToViewport(e->pos()) );
255 kdDebug(5850) << "Dropped : " << text << endl;
256 Todo*todo = todoi->todo();
257 if( mChanger->beginChange( todo, 0, TQString() ) ) {
258 Todo*oldtodo = todo->clone();
259
260 if( text.startsWith( "file:" ) ) {
261 todo->addAttachment( new Attachment( text ) );
262 } else {
263 TQStringList emails = KPIM::splitEmailAddrList( text );
264 for(TQStringList::ConstIterator it = emails.begin();it!=emails.end();++it) {
265 kdDebug(5850) << " Email: " << (*it) << endl;
266 int pos = (*it).find("<");
267 TQString name = (*it).left(pos);
268 TQString email = (*it).mid(pos);
269 if (!email.isEmpty() && todoi) {
270 todo->addAttendee( new Attendee( name, email ) );
271 }
272 }
273 }
274 //FIXME: attendees or attachment added, so there is something modified
275 mChanger->changeIncidence( oldtodo, todo, KOGlobals::NOTHING_MODIFIED, this );
276 mChanger->endChange( todo, 0, TQString() );
277 } else {
278 KMessageBox::sorry( this, i18n("Unable to add attendees to the to-do, "
279 "because the to-do cannot be locked.") );
280 }
281 }
282 else {
283 kdDebug(5850) << "KOTodoListView::contentsDropEvent(): Todo from drop not decodable" << endl;
284 e->ignore();
285 }
286 }
287#endif
288}
289
290void KOTodoListView::contentsMousePressEvent(TQMouseEvent* e)
291{
292 TQListView::contentsMousePressEvent(e);
293 TQPoint p(contentsToViewport(e->pos()));
294 TQListViewItem *i = itemAt(p);
295 if (i) {
296 // if the user clicked into the root decoration of the item, don't
297 // try to start a drag!
298 if (p.x() > header()->sectionPos(header()->mapToIndex(0)) +
299 treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) +
300 itemMargin() ||
301 p.x() < header()->sectionPos(header()->mapToIndex(0))) {
302 if (e->button()==TQt::LeftButton) {
303 mPressPos = e->pos();
304 mMousePressed = true;
305 }
306 }
307 }
308}
309
310void KOTodoListView::contentsMouseMoveEvent(TQMouseEvent* e)
311{
312#ifndef KORG_NODND
313// kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent()" << endl;
314 TQListView::contentsMouseMoveEvent(e);
315 if (mMousePressed && (mPressPos - e->pos()).manhattanLength() >
316 TQApplication::startDragDistance()) {
317 mMousePressed = false;
318 TQListViewItem *item = itemAt(contentsToViewport(mPressPos));
319 if ( item && mCalendar ) {
320// kdDebug(5850) << "Start Drag for item " << item->text(0) << endl;
321 DndFactory factory( mCalendar );
322 ICalDrag *vd = factory.createDrag(
323 ((KOTodoViewItem *)item)->todo(),viewport());
324 if (vd->drag()) {
325 kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent(): Delete drag source" << endl;
326 }
327/*
328 TQString source = fullPath(item);
329 if ( TQFile::exists(source) ) {
330 KURL url;
331 url.setPath(source);
332 KURLDrag* ud = KURLDrag::newDrag(KURL::List(url), viewport());
333 if ( ud->drag() )
334 TQMessageBox::information( this, "Drag source",
335 TQString("Delete ")+source, "Not implemented" );
336*/
337 }
338 }
339#endif
340}
341
342void KOTodoListView::contentsMouseReleaseEvent(TQMouseEvent *e)
343{
344 TQListView::contentsMouseReleaseEvent(e);
345 mMousePressed = false;
346}
347
348void KOTodoListView::contentsMouseDoubleClickEvent(TQMouseEvent *e)
349{
350 if (!e) return;
351
352 TQPoint vp = contentsToViewport(e->pos());
353
354 TQListViewItem *item = itemAt(vp);
355
356 if (!item) return;
357
358 emit doubleClicked(item,vp,0);
359}
360
362
363KOTodoView::KOTodoView( Calendar *calendar, TQWidget *parent, const char* name)
364 : KOrg::BaseView( calendar, parent, name )
365{
366 TQBoxLayout *topLayout = new TQVBoxLayout( this );
367
368 TQLabel *title = new TQLabel( i18n("To-dos:"), this );
369 title->setFrameStyle( TQFrame::Panel | TQFrame::Raised );
370 topLayout->addWidget( title );
371
372 mQuickAdd = new KPIM::ClickLineEdit( this, i18n( "Click to add a new to-do" ) );
373 mQuickAdd->setAcceptDrops( false );
374 topLayout->addWidget( mQuickAdd );
375
376 if ( !KOPrefs::instance()->mEnableQuickTodo ) mQuickAdd->hide();
377
378 mTodoListView = new KOTodoListView( this );
379 topLayout->addWidget( mTodoListView );
380
381 mTodoListView->setRootIsDecorated( true );
382 mTodoListView->setAllColumnsShowFocus( true );
383
384 mTodoListView->setShowSortIndicator( true );
385
386 mTodoListView->addColumn( i18n("Summary") );
387 mTodoListView->addColumn( i18n("Recurs") );
388 mTodoListView->addColumn( i18n("Priority") );
389 mTodoListView->setColumnAlignment( ePriorityColumn, AlignHCenter );
390 mTodoListView->addColumn( i18n("Complete") );
391 mTodoListView->setColumnAlignment( ePercentColumn, AlignRight );
392 mTodoListView->addColumn( i18n("Due Date/Time") );
393 mTodoListView->setColumnAlignment( eDueDateColumn, AlignLeft );
394 mTodoListView->addColumn( i18n("Categories") );
395 mTodoListView->addColumn( i18n( "Calendar" ) );
396#if 0
397 mTodoListView->addColumn( i18n("Sort Id") );
398 mTodoListView->setColumnAlignment( 4, AlignHCenter );
399#endif
400
401 mTodoListView->setMinimumHeight( 60 );
402 mTodoListView->setItemsRenameable( true );
403 mTodoListView->setRenameable( 0 );
404
405 mTodoListView->setColumnWidthMode( eSummaryColumn, TQListView::Manual );
406 mTodoListView->setColumnWidthMode( eRecurColumn, TQListView::Manual );
407 mTodoListView->setColumnWidthMode( ePriorityColumn, TQListView::Manual );
408 mTodoListView->setColumnWidthMode( ePercentColumn, TQListView::Manual );
409 mTodoListView->setColumnWidthMode( eDueDateColumn, TQListView::Manual );
410 mTodoListView->setColumnWidthMode( eCategoriesColumn, TQListView::Manual );
411 mTodoListView->setColumnWidthMode( eFolderColumn, TQListView::Manual );
412#if 0
413 mTodoListView->setColumnWidthMode( eDescriptionColumn, TQListView::Manual );
414#endif
415
416 mPriorityPopupMenu = new TQPopupMenu( this );
417 mPriority[ mPriorityPopupMenu->insertItem( i18n("Unspecified priority", "unspecified") ) ] = 0;
418 mPriority[ mPriorityPopupMenu->insertItem( i18n( "1 (highest)") ) ] = 1;
419 mPriority[ mPriorityPopupMenu->insertItem( i18n( "2" ) ) ] = 2;
420 mPriority[ mPriorityPopupMenu->insertItem( i18n( "3" ) ) ] = 3;
421 mPriority[ mPriorityPopupMenu->insertItem( i18n( "4" ) ) ] = 4;
422 mPriority[ mPriorityPopupMenu->insertItem( i18n( "5 (medium)" ) ) ] = 5;
423 mPriority[ mPriorityPopupMenu->insertItem( i18n( "6" ) ) ] = 6;
424 mPriority[ mPriorityPopupMenu->insertItem( i18n( "7" ) ) ] = 7;
425 mPriority[ mPriorityPopupMenu->insertItem( i18n( "8" ) ) ] = 8;
426 mPriority[ mPriorityPopupMenu->insertItem( i18n( "9 (lowest)" ) ) ] = 9;
427 connect( mPriorityPopupMenu, TQ_SIGNAL( activated( int ) ),
428 TQ_SLOT( setNewPriority( int ) ));
429
430 mPercentageCompletedPopupMenu = new TQPopupMenu(this);
431 for (int i = 0; i <= 100; i+=10) {
432 TQString label = TQString ("%1 %").arg (i);
433 mPercentage[mPercentageCompletedPopupMenu->insertItem (label)] = i;
434 }
435 connect( mPercentageCompletedPopupMenu, TQ_SIGNAL( activated( int ) ),
436 TQ_SLOT( setNewPercentage( int ) ) );
437
438 mMovePopupMenu = new KDatePickerPopup(
439 KDatePickerPopup::NoDate |
440 KDatePickerPopup::DatePicker |
441 KDatePickerPopup::Words );
442 mCopyPopupMenu = new KDatePickerPopup(
443 KDatePickerPopup::NoDate |
444 KDatePickerPopup::DatePicker |
445 KDatePickerPopup::Words );
446
447
448 connect( mMovePopupMenu, TQ_SIGNAL( dateChanged( TQDate )),
449 TQ_SLOT( setNewDate( TQDate ) ) );
450 connect( mCopyPopupMenu, TQ_SIGNAL( dateChanged( TQDate )),
451 TQ_SLOT( copyTodoToDate( TQDate ) ) );
452
453 mItemPopupMenu = new TQPopupMenu(this);
454 mItemPopupMenu->insertItem(i18n("&Show"), this,
455 TQ_SLOT (showTodo()));
456 mItemPopupMenu->insertItem(i18n("&Edit..."), this,
457 TQ_SLOT (editTodo()), 0, ePopupEdit );
458#ifndef KORG_NOPRINTER
459 mItemPopupMenu->insertItem(KOGlobals::self()->smallIcon("printer"), i18n("&Print..."), this, TQ_SLOT( printTodo() ) );
460#endif
461 mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("edit-delete"), i18n("&Delete"), this,
462 TQ_SLOT (deleteTodo()), 0, ePopupDelete );
463 mItemPopupMenu->insertSeparator();
464 mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("New &To-do..."), this,
465 TQ_SLOT (newTodo()) );
466 mItemPopupMenu->insertItem(i18n("New Su&b-to-do..."), this,
467 TQ_SLOT (newSubTodo()));
468 mItemPopupMenu->insertItem( i18n("&Make this To-do Independent"), this,
469 TQ_SIGNAL( unSubTodoSignal() ), 0, ePopupUnSubTodo );
470 mItemPopupMenu->insertItem( i18n("Make all Sub-to-dos &Independent"), this,
471 TQ_SIGNAL( unAllSubTodoSignal() ), 0, ePopupUnAllSubTodo );
472 mItemPopupMenu->insertSeparator();
473 mItemPopupMenu->insertItem( i18n("&Copy To"), mCopyPopupMenu, ePopupCopyTo );
474 mItemPopupMenu->insertItem(i18n("&Move To"), mMovePopupMenu, ePopupMoveTo );
475 mItemPopupMenu->insertSeparator();
476 mItemPopupMenu->insertItem(i18n("delete completed to-dos","Pur&ge Completed"),
477 this, TQ_SLOT( purgeCompleted() ) );
478
479 connect( mMovePopupMenu, TQ_SIGNAL( dateChanged( TQDate ) ),
480 mItemPopupMenu, TQ_SLOT( hide() ) );
481 connect( mCopyPopupMenu, TQ_SIGNAL( dateChanged( TQDate ) ),
482 mItemPopupMenu, TQ_SLOT( hide() ) );
483
484 mPopupMenu = new TQPopupMenu(this);
485 mPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("&New To-do..."), this,
486 TQ_SLOT(newTodo()) );
487 mPopupMenu->insertItem(i18n("delete completed to-dos","&Purge Completed"),
488 this, TQ_SLOT(purgeCompleted()));
489
490 mDocPrefs = new DocPrefs( name );
491
492 // Double clicking conflicts with opening/closing the subtree
493 connect( mTodoListView, TQ_SIGNAL( doubleClicked( TQListViewItem *,
494 const TQPoint &, int ) ),
495 TQ_SLOT( editItem( TQListViewItem *, const TQPoint &, int ) ) );
496 connect( mTodoListView, TQ_SIGNAL( returnPressed( TQListViewItem * ) ),
497 TQ_SLOT( editItem( TQListViewItem * ) ) );
498 connect( mTodoListView, TQ_SIGNAL( contextMenuRequested( TQListViewItem *,
499 const TQPoint &, int ) ),
500 TQ_SLOT( popupMenu( TQListViewItem *, const TQPoint &, int ) ) );
501 connect( mTodoListView, TQ_SIGNAL( expanded( TQListViewItem * ) ),
502 TQ_SLOT( itemStateChanged( TQListViewItem * ) ) );
503 connect( mTodoListView, TQ_SIGNAL( collapsed( TQListViewItem * ) ),
504 TQ_SLOT( itemStateChanged( TQListViewItem * ) ) );
505
506#if 0
507 connect(mTodoListView,TQ_SIGNAL(selectionChanged(TQListViewItem *)),
508 TQ_SLOT(selectionChanged(TQListViewItem *)));
509 connect(mTodoListView,TQ_SIGNAL(clicked(TQListViewItem *)),
510 TQ_SLOT(selectionChanged(TQListViewItem *)));
511 connect(mTodoListView,TQ_SIGNAL(pressed(TQListViewItem *)),
512 TQ_SLOT(selectionChanged(TQListViewItem *)));
513#endif
514 connect( mTodoListView, TQ_SIGNAL(selectionChanged() ),
515 TQ_SLOT( processSelectionChange() ) );
516 connect( mQuickAdd, TQ_SIGNAL( returnPressed () ),
517 TQ_SLOT( addQuickTodo() ) );
518
519 new KOTodoListViewToolTip( mTodoListView->viewport(), calendar, mTodoListView );
520}
521
522KOTodoView::~KOTodoView()
523{
524 delete mDocPrefs;
525}
526
527void KOTodoView::setCalendar( Calendar *cal )
528{
529 BaseView::setCalendar( cal );
530 mTodoListView->setCalendar( cal );
531}
532
533void KOTodoView::updateView()
534{
535// kdDebug(5850) << "KOTodoView::updateView()" << endl;
536 int oldPos = mTodoListView->contentsY();
537 mItemsToDelete.clear();
538 mTodoListView->clear();
539
540 Todo::List todoList = calendar()->todos();
541
542/*
543 kdDebug(5850) << "KOTodoView::updateView(): Todo List:" << endl;
544 Event *t;
545 for(t = todoList.first(); t; t = todoList.next()) {
546 kdDebug(5850) << " " << t->getSummary() << endl;
547
548 if (t->getRelatedTo()) {
549 kdDebug(5850) << " (related to " << t->getRelatedTo()->getSummary() << ")" << endl;
550 }
551
552 TQPtrList<Event> l = t->getRelations();
553 Event *c;
554 for(c=l.first();c;c=l.next()) {
555 kdDebug(5850) << " - relation: " << c->getSummary() << endl;
556 }
557 }
558*/
559
560 // Put for each Event a KOTodoViewItem in the list view. Don't rely on a
561 // specific order of events. That means that we have to generate parent items
562 // recursively for proper hierarchical display of Todos.
563 mTodoMap.clear();
564 Todo::List::ConstIterator it;
565 for( it = todoList.begin(); it != todoList.end(); ++it ) {
566 if ( !mTodoMap.contains( *it ) ) {
567 insertTodoItem( *it );
568 }
569 }
570
571 // Restore opened/closed state
572 mTodoListView->blockSignals( true );
573 if( mDocPrefs ) restoreItemState( mTodoListView->firstChild() );
574 mTodoListView->blockSignals( false );
575
576 mTodoListView->setContentsPos( 0, oldPos );
577
578 processSelectionChange();
579}
580
581void KOTodoView::restoreItemState( TQListViewItem *item )
582{
583 while( item ) {
584 KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
585 todoItem->setOpen( mDocPrefs->readBoolEntry( todoItem->todo()->uid() ) );
586 if( item->childCount() > 0 ) restoreItemState( item->firstChild() );
587 item = item->nextSibling();
588 }
589}
590
591
592TQMap<Todo *,KOTodoViewItem *>::ConstIterator
593 KOTodoView::insertTodoItem(Todo *todo)
594{
595// kdDebug(5850) << "KOTodoView::insertTodoItem(): " << todo->getSummary() << endl;
596 Incidence *incidence = todo->relatedTo();
597 if (incidence && incidence->type() == "Todo") {
598 // Use dynamic_cast, because in the future the related item might also be an event
599 Todo *relatedTodo = dynamic_cast<Todo *>(incidence);
600
601 // just make sure we know we have this item already to avoid endless recursion (Bug 101696)
602 mTodoMap.insert(todo,0);
603
604// kdDebug(5850) << " has Related" << endl;
605 TQMap<Todo *,KOTodoViewItem *>::ConstIterator itemIterator;
606 itemIterator = mTodoMap.find(relatedTodo);
607 if (itemIterator == mTodoMap.end()) {
608// kdDebug(5850) << " related not yet in list" << endl;
609 itemIterator = insertTodoItem (relatedTodo);
610 }
611 // isn't this pretty stupid? We give one Todo to the KOTodoViewItem
612 // and one into the map. Sure finding is more easy but why? -zecke
613 KOTodoViewItem *todoItem;
614
615 // in case we found a related parent, which has no KOTodoViewItem yet, this must
616 // be the case where 2 items refer to each other, therefore simply create item as root item
617 if ( *itemIterator == 0 ) {
618 todo->setRelatedTo(0); // break the recursion, else we will have troubles later
619 todoItem = new KOTodoViewItem(mTodoListView,todo,this);
620 }
621 else
622 todoItem = new KOTodoViewItem(*itemIterator,todo,this);
623
624 return mTodoMap.insert(todo,todoItem);
625 } else {
626// kdDebug(5850) << " no Related" << endl;
627 // see above -zecke
628 KOTodoViewItem *todoItem = new KOTodoViewItem(mTodoListView,todo,this);
629 return mTodoMap.insert(todo,todoItem);
630 }
631}
632
633void KOTodoView::removeTodoItems()
634{
635 KOTodoViewItem *item;
636 for ( item = mItemsToDelete.first(); item; item = mItemsToDelete.next() ) {
637 Todo *todo = item->todo();
638 if ( todo && mTodoMap.contains( todo ) ) {
639 mTodoMap.remove( todo );
640 }
641 delete item;
642 }
643 mItemsToDelete.clear();
644}
645
646
647bool KOTodoView::scheduleRemoveTodoItem( KOTodoViewItem *todoItem )
648{
649 if ( todoItem ) {
650 mItemsToDelete.append( todoItem );
651 TQTimer::singleShot( 0, this, TQ_SLOT( removeTodoItems() ) );
652 return true;
653 } else
654 return false;
655}
656
657void KOTodoView::updateConfig()
658{
659 mTodoListView->repaintContents();
660}
661
663{
664 Incidence::List selected;
665
666 KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
667// if (!item) item = mActiveItem;
668 if (item) selected.append(item->todo());
669
670 return selected;
671}
672
673Todo::List KOTodoView::selectedTodos()
674{
675 Todo::List selected;
676
677 KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
678// if (!item) item = mActiveItem;
679 if (item) selected.append(item->todo());
680
681 return selected;
682}
683
684void KOTodoView::changeIncidenceDisplay(Incidence *incidence, int action)
685{
686 // The todo view only displays todos, so exit on all other incidences
687 if ( incidence->type() != "Todo" )
688 return;
689 CalFilter *filter = calendar()->filter();
690 bool isFiltered = filter && !filter->filterIncidence( incidence );
691 Todo *todo = static_cast<Todo *>(incidence);
692 if ( todo ) {
693 KOTodoViewItem *todoItem = 0;
694 if ( mTodoMap.contains( todo ) ) {
695 todoItem = mTodoMap[todo];
696 }
697 switch ( action ) {
698 case KOGlobals::INCIDENCEADDED:
699 case KOGlobals::INCIDENCEEDITED:
700 // If it's already there, edit it, otherwise just add
701 if ( todoItem ) {
702 if ( isFiltered ) {
703 scheduleRemoveTodoItem( todoItem );
704 } else {
705 // correctly update changes in relations
706 Todo*parent = dynamic_cast<Todo*>( todo->relatedTo() );
707 KOTodoViewItem*parentItem = 0;
708 if ( parent && mTodoMap.contains(parent) ) {
709 parentItem = mTodoMap[ parent ];
710 }
711 if ( todoItem->parent() != parentItem ) {
712 // The relations changed
713 if ( parentItem ) {
714 parentItem->insertItem( todoItem );
715 } else {
716 mTodoListView->insertItem( todoItem );
717 }
718 }
719 todoItem->construct();
720 }
721 } else {
722 if ( !isFiltered ) {
723 insertTodoItem( todo );
724 }
725 }
726 mTodoListView->sort();
727 break;
728 case KOGlobals::INCIDENCEDELETED:
729 if ( todoItem ) {
730 scheduleRemoveTodoItem( todoItem );
731 }
732 break;
733 default:
734 TQTimer::singleShot( 0, this, TQ_SLOT( updateView() ) );
735 }
736 } else {
737 // use a TQTimer here, because when marking todos finished using
738 // the checkbox, this slot gets called, but we cannot update the views
739 // because we're still inside KOTodoViewItem::stateChange
740 TQTimer::singleShot(0,this,TQ_SLOT(updateView()));
741 }
742}
743
744void KOTodoView::showDates(const TQDate &, const TQDate &)
745{
746}
747
748void KOTodoView::showIncidences( const Incidence::List &, const TQDate & )
749{
750 kdDebug(5850) << "KOTodoView::showIncidences( const Incidence::List & ): not yet implemented" << endl;
751}
752
753CalPrinterBase::PrintType KOTodoView::printType()
754{
755 return CalPrinterBase::Todolist;
756}
757
758void KOTodoView::editItem( TQListViewItem *item )
759{
760 if ( item ) {
761 emit editIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate () );
762 }
763}
764
765void KOTodoView::editItem( TQListViewItem *item, const TQPoint &, int )
766{
767 editItem( item );
768}
769
770void KOTodoView::showItem( TQListViewItem *item )
771{
772 if ( item ) {
773 emit showIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate() );
774 }
775}
776
777void KOTodoView::showItem( TQListViewItem *item, const TQPoint &, int )
778{
779 showItem( item );
780}
781
782void KOTodoView::popupMenu( TQListViewItem *item, const TQPoint &, int column )
783{
784 mActiveItem = static_cast<KOTodoViewItem *>( item );
785 if ( mActiveItem && mActiveItem->todo() &&
786 !mActiveItem->todo()->isReadOnly() ) {
787 bool editable = !mActiveItem->todo()->isReadOnly();
788 mItemPopupMenu->setItemEnabled( ePopupEdit, editable );
789 mItemPopupMenu->setItemEnabled( ePopupDelete, editable );
790 mItemPopupMenu->setItemEnabled( ePopupMoveTo, editable );
791 mItemPopupMenu->setItemEnabled( ePopupCopyTo, editable );
792 mItemPopupMenu->setItemEnabled( ePopupUnSubTodo, editable );
793 mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo, editable );
794
795 if ( editable ) {
796 TQDate date = mActiveItem->todo()->dtDue().date();
797 if ( mActiveItem->todo()->hasDueDate () ) {
798 mMovePopupMenu->datePicker()->setDate( date );
799 } else {
800 mMovePopupMenu->datePicker()->setDate( TQDate::currentDate() );
801 }
802 switch ( column ) {
803 case ePriorityColumn:
804 mPriorityPopupMenu->popup( TQCursor::pos() );
805 break;
806 case ePercentColumn: {
807 mPercentageCompletedPopupMenu->popup( TQCursor::pos() );
808 break;
809 }
810 case eDueDateColumn:
811 mMovePopupMenu->popup( TQCursor::pos() );
812 break;
813 case eCategoriesColumn:
814 getCategoryPopupMenu( mActiveItem )->popup( TQCursor::pos() );
815 break;
816 default:
817 mCopyPopupMenu->datePicker()->setDate( date );
818 mCopyPopupMenu->datePicker()->setDate( TQDate::currentDate() );
819 mItemPopupMenu->setItemEnabled( ePopupUnSubTodo,
820 mActiveItem->todo()->relatedTo() );
821 mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo,
822 !mActiveItem->todo()->relations().isEmpty() );
823 mItemPopupMenu->popup( TQCursor::pos() );
824 }
825 } else {
826 mItemPopupMenu->popup( TQCursor::pos() );
827 }
828 } else mPopupMenu->popup( TQCursor::pos() );
829}
830
831void KOTodoView::newTodo()
832{
833 kdDebug() << k_funcinfo << endl;
834 emit newTodoSignal( 0/*ResourceCalendar*/, TQString()/*subResource*/,
835 TQDate::currentDate().addDays(7) );
836}
837
838void KOTodoView::newSubTodo()
839{
840 if (mActiveItem) {
841 emit newSubTodoSignal(mActiveItem->todo());
842 }
843}
844
845void KOTodoView::editTodo()
846{
847 editItem( mActiveItem );
848}
849
850void KOTodoView::showTodo()
851{
852 showItem( mActiveItem );
853}
854
855void KOTodoView::printTodo()
856{
857#ifndef KORG_NOPRINTER
858 KOCoreHelper helper;
859 CalPrinter printer( this, BaseView::calendar(), &helper );
860 connect( this, TQ_SIGNAL(configChanged()), &printer, TQ_SLOT(updateConfig()) );
861
862 Incidence::List selectedIncidences;
863 selectedIncidences.append( mActiveItem->todo() );
864
865 TQDateTime todoDate;
866 if ( mActiveItem->todo() && mActiveItem->todo()->hasStartDate() ) {
867 todoDate = mActiveItem->todo()->dtStart();
868 } else {
869 todoDate = mActiveItem->todo()->dtDue();
870 }
871
872 printer.print( KOrg::CalPrinterBase::Incidence,
873 todoDate.date(), todoDate.date(), selectedIncidences );
874#endif
875}
876
877void KOTodoView::deleteTodo()
878{
879 if (mActiveItem) {
880 emit deleteIncidenceSignal( mActiveItem->todo() );
881 }
882}
883
884void KOTodoView::setNewPriority(int index)
885{
886 if ( !mActiveItem || !mChanger ) return;
887 Todo *todo = mActiveItem->todo();
888 if ( !todo->isReadOnly () &&
889 mChanger->beginChange( todo, 0, TQString() ) ) {
890 Todo *oldTodo = todo->clone();
891 todo->setPriority(mPriority[index]);
892 mActiveItem->construct();
893
894 mChanger->changeIncidence( oldTodo, todo, KOGlobals::PRIORITY_MODIFIED, this );
895 mChanger->endChange( todo, 0, TQString() );
896 delete oldTodo;
897 }
898}
899
900void KOTodoView::setNewPercentage( KOTodoViewItem *item, int percentage )
901{
902 kdDebug(5850) << "KOTodoView::setNewPercentage( " << percentage << "), item = " << item << endl;
903 if ( !item || !mChanger ) return;
904 Todo *todo = item->todo();
905 if ( !todo ) return;
906
907 if ( !todo->isReadOnly () &&
908 mChanger->beginChange( todo, 0, TQString() ) ) {
909 Todo *oldTodo = todo->clone();
910
911/* Old code to make sub-items's percentage related to this one's:
912 TQListViewItem *myChild = firstChild();
913 KOTodoViewItem *item;
914 while( myChild ) {
915 item = static_cast<KOTodoViewItem*>(myChild);
916 item->stateChange(state);
917 myChild = myChild->nextSibling();
918 }*/
919 if ( percentage == 100 ) {
920 todo->setCompleted( TQDateTime::currentDateTime() );
921 // If the todo does recur, it doesn't get set as completed. However, the
922 // item is still checked. Uncheck it again.
923 if ( !todo->isCompleted() ) {
924 item->setState( TQCheckListItem::Off );
925 }
926 } else {
927 todo->setPercentComplete( percentage );
928 }
929 item->construct();
930 if ( todo->doesRecur() && percentage == 100 )
931 mChanger->changeIncidence( oldTodo, todo,
932 KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE, this );
933 else
934 mChanger->changeIncidence( oldTodo, todo,
935 KOGlobals::COMPLETION_MODIFIED, this );
936 mChanger->endChange( todo, 0, TQString() );
937 delete oldTodo;
938 } else {
939 item->construct();
940 kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
941 }
942}
943
944void KOTodoView::setNewPercentage( int index )
945{
946 setNewPercentage( mActiveItem, mPercentage[index] );
947}
948
949void KOTodoView::setNewDate( TQDate date )
950{
951 if ( !mActiveItem || !mChanger ) return;
952 Todo *todo = mActiveItem->todo();
953 if ( !todo ) return;
954
955 if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, TQString() ) ) {
956 Todo *oldTodo = todo->clone();
957
958 TQDateTime dt;
959 dt.setDate( date );
960
961 if ( !todo->doesFloat() ) {
962 dt.setTime( todo->dtDue().time() );
963 }
964
965 todo->setHasDueDate( !date.isNull() );
966 todo->setDtDue( dt );
967
968 mActiveItem->construct();
969 mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this );
970 mChanger->endChange( todo, 0, TQString() );
971 delete oldTodo;
972 } else {
973 kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
974 }
975}
976
977void KOTodoView::copyTodoToDate( TQDate date )
978{
979 TQDateTime dt( date );
980
981 if ( mActiveItem && mChanger ) {
982 Todo *oldTodo = mActiveItem->todo();
983 Todo *newTodo = oldTodo->clone();
984 newTodo->recreate();
985
986 newTodo->setHasDueDate( !date.isNull() );
987
988 if ( oldTodo->hasDueDate() && !oldTodo->doesFloat() ) {
989 dt.setTime( oldTodo->dtDue().time() );
990 }
991
992 newTodo->setDtDue( dt );
993 newTodo->setPercentComplete( 0 );
994
995 TQPair<ResourceCalendar *, TQString>p =
996 CalHelper::incSubResourceCalendar( calendar(), mActiveItem->todo() );
997
998 mChanger->addIncidence( newTodo, p.first, p.second, this );
999 }
1000}
1001
1003{
1004 TQPopupMenu *tempMenu = new TQPopupMenu( this );
1005 TQStringList checkedCategories = todoItem->todo()->categories();
1006
1007 tempMenu->setCheckable( true );
1008 TQStringList::Iterator it;
1009 for ( it = KOPrefs::instance()->mCustomCategories.begin();
1010 it != KOPrefs::instance()->mCustomCategories.end();
1011 ++it ) {
1012 int index = tempMenu->insertItem( *it );
1013 mCategory[ index ] = *it;
1014 if ( checkedCategories.find( *it ) != checkedCategories.end() )
1015 tempMenu->setItemChecked( index, true );
1016 }
1017
1018 connect ( tempMenu, TQ_SIGNAL( activated( int ) ),
1019 TQ_SLOT( changedCategories( int ) ) );
1020 return tempMenu;
1021}
1022
1023void KOTodoView::changedCategories(int index)
1024{
1025 if ( !mActiveItem || !mChanger ) return;
1026 Todo *todo = mActiveItem->todo();
1027 if ( !todo ) return;
1028
1029 if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, TQString() ) ) {
1030 Todo *oldTodo = todo->clone();
1031
1032 TQStringList categories = todo->categories ();
1033 if ( categories.find( mCategory[index] ) != categories.end() )
1034 categories.remove( mCategory[index] );
1035 else
1036 categories.insert( categories.end(), mCategory[index] );
1037 categories.sort();
1038 todo->setCategories( categories );
1039 mActiveItem->construct();
1040 mChanger->changeIncidence( oldTodo, todo, KOGlobals::CATEGORY_MODIFIED, this );
1041 mChanger->endChange( todo, 0, TQString() );
1042 delete oldTodo;
1043 } else {
1044 kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
1045 }
1046}
1047
1048void KOTodoView::setDocumentId( const TQString &id )
1049{
1050 kdDebug(5850) << "KOTodoView::setDocumentId()" << endl;
1051
1052 mDocPrefs->setDoc( id );
1053}
1054
1055void KOTodoView::itemStateChanged( TQListViewItem *item )
1056{
1057 if (!item) return;
1058
1059 KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
1060
1061// kdDebug(5850) << "KOTodoView::itemStateChanged(): " << todoItem->todo()->summary() << endl;
1062
1063 if( mDocPrefs ) mDocPrefs->writeEntry( todoItem->todo()->uid(), todoItem->isOpen() );
1064}
1065
1066void KOTodoView::setNewPercentageDelayed( KOTodoViewItem *item, int percentage )
1067{
1068 mPercentChangedMap.append( qMakePair( item, percentage ) );
1069
1070 TQTimer::singleShot( 0, this, TQ_SLOT( processDelayedNewPercentage() ) );
1071}
1072
1073void KOTodoView::processDelayedNewPercentage()
1074{
1075 TQValueList< TQPair< KOTodoViewItem *, int> >::Iterator it;
1076 for ( it = mPercentChangedMap.begin(); it != mPercentChangedMap.end(); ++it )
1077 setNewPercentage( (*it).first, (*it).second );
1078
1079 mPercentChangedMap.clear();
1080}
1081
1082void KOTodoView::saveLayout(TDEConfig *config, const TQString &group) const
1083{
1084 mTodoListView->saveLayout(config,group);
1085}
1086
1087void KOTodoView::restoreLayout(TDEConfig *config, const TQString &group)
1088{
1089 mTodoListView->restoreLayout(config,group);
1090}
1091
1092void KOTodoView::processSelectionChange()
1093{
1094// kdDebug(5850) << "KOTodoView::processSelectionChange()" << endl;
1095
1096 KOTodoViewItem *item =
1097 static_cast<KOTodoViewItem *>( mTodoListView->selectedItem() );
1098
1099 if ( !item ) {
1100 emit incidenceSelected( 0, TQDate() );
1101 } else {
1102 if ( selectedIncidenceDates().isEmpty() ) {
1103 emit incidenceSelected( item->todo(), TQDate() );
1104 } else {
1105 emit incidenceSelected( item->todo(), selectedIncidenceDates().first() );
1106 }
1107 }
1108}
1109
1110void KOTodoView::clearSelection()
1111{
1112 mTodoListView->selectAll( false );
1113}
1114
1115void KOTodoView::purgeCompleted()
1116{
1117 emit purgeCompletedSignal();
1118}
1119
1120void KOTodoView::addQuickTodo()
1121{
1122 if ( ! mQuickAdd->text().stripWhiteSpace().isEmpty() ) {
1123 Todo *todo = new Todo();
1124 todo->setSummary( mQuickAdd->text() );
1125 todo->setOrganizer( Person( KOPrefs::instance()->fullName(),
1126 KOPrefs::instance()->email() ) );
1127 if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) {
1128 delete todo;
1129 return;
1130 }
1131 mQuickAdd->setText( TQString() );
1132 }
1133}
1134
1135void KOTodoView::setIncidenceChanger( IncidenceChangerBase *changer )
1136{
1137 mChanger = changer;
1138 mTodoListView->setIncidenceChanger( changer );
1139}
CalPrinter is a class for printing Calendars.
Definition: calprinter.h:54
bool filterIncidence(Incidence *) const
CalFilter * filter()
virtual Todo::List todos(TodoSortField sortField=TodoSortUnsorted, SortDirection sortDirection=SortDirectionAscending)
void setOrganizer(const Person &o)
bool doesFloat() const
TQString uid() const
bool isReadOnly() const
void addAttendee(Attendee *attendee, bool doUpdate=true)
void addAttachment(Attachment *attachment)
void setSummary(const TQString &summary)
void setRelatedTo(Incidence *relatedTo)
void setPriority(int priority)
Incidence * relatedTo() const
TQStringList categories() const
bool doesRecur() const
void setCategories(const TQStringList &categories)
Incidence::List relations() const
bool hasDueDate() const
bool isCompleted() const
bool hasStartDate() const
Todo * clone()
void setDtDue(const TQDateTime &dtDue, bool first=false)
void setCompleted(bool completed)
TQDateTime dtStart(bool first=false) const
TQDateTime dtDue(bool first=false) const
void setHasDueDate(bool hasDueDate)
void setPercentComplete(int)
This class provides a way of displaying a single Event of Todo-Type in a KTodoView.
DateList selectedIncidenceDates()
Definition: kotodoview.h:126
TQPopupMenu * getCategoryPopupMenu(KOTodoViewItem *todoItem)
Create a popup menu to set categories.
Incidence::List selectedIncidences()
Definition: kotodoview.cpp:662
void setIncidenceChanger(IncidenceChangerBase *changer)
Assign a new incidence change helper object.
This class provides an interface for all views being displayed within the main calendar view.
Definition: baseview.h:60
virtual Calendar * calendar()
Return calendar object of this view.
Definition: baseview.h:89
void editIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to begin editing the incidence specified in some manner.
void deleteIncidenceSignal(Incidence *)
instructs the receiver to delete the Incidence in some manner; some possibilities include automatical...
void showIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to show the incidence in read-only mode.