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"
59 using 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 
67 KOTodoListViewToolTip::KOTodoListViewToolTip (TQWidget *parent,
68  Calendar *calendar,
69  KOTodoListView *lv )
70  :TQToolTip(parent), mCalendar( calendar )
71 {
72  todolist=lv;
73 }
74 
75 void 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 
103 KOTodoListView::KOTodoListView( TQWidget *parent, const char *name )
104  : TDEListView( parent, name ), mCalendar( 0 ), mChanger( 0 )
105 {
106  mOldCurrent = 0;
107  mMousePressed = false;
108 }
109 
110 KOTodoListView::~KOTodoListView()
111 {
112 }
113 
114 void KOTodoListView::setCalendar( Calendar *cal )
115 {
116  mCalendar = cal;
117  setAcceptDrops( mCalendar );
118  viewport()->setAcceptDrops( mCalendar );
119 }
120 
121 bool KOTodoListView::event(TQEvent *e)
122 {
123  int tmp=0;
124  KOTodoViewItem *i;
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 
146 void 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 
160 void 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 
175 void 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 
185 void 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 
290 void 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 
310 void 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 
342 void KOTodoListView::contentsMouseReleaseEvent(TQMouseEvent *e)
343 {
344  TQListView::contentsMouseReleaseEvent(e);
345  mMousePressed = false;
346 }
347 
348 void 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 
363 KOTodoView::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 
522 KOTodoView::~KOTodoView()
523 {
524  delete mDocPrefs;
525 }
526 
527 void KOTodoView::setCalendar( Calendar *cal )
528 {
529  BaseView::setCalendar( cal );
530  mTodoListView->setCalendar( cal );
531 }
532 
533 void 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 
581 void 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 
592 TQMap<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 
633 void 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 
647 bool 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 
657 void 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 
673 Todo::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 
684 void 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 
744 void KOTodoView::showDates(const TQDate &, const TQDate &)
745 {
746 }
747 
748 void KOTodoView::showIncidences( const Incidence::List &, const TQDate & )
749 {
750  kdDebug(5850) << "KOTodoView::showIncidences( const Incidence::List & ): not yet implemented" << endl;
751 }
752 
753 CalPrinterBase::PrintType KOTodoView::printType()
754 {
755  return CalPrinterBase::Todolist;
756 }
757 
758 void KOTodoView::editItem( TQListViewItem *item )
759 {
760  if ( item ) {
761  emit editIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate () );
762  }
763 }
764 
765 void KOTodoView::editItem( TQListViewItem *item, const TQPoint &, int )
766 {
767  editItem( item );
768 }
769 
770 void KOTodoView::showItem( TQListViewItem *item )
771 {
772  if ( item ) {
773  emit showIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate() );
774  }
775 }
776 
777 void KOTodoView::showItem( TQListViewItem *item, const TQPoint &, int )
778 {
779  showItem( item );
780 }
781 
782 void 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 
831 void KOTodoView::newTodo()
832 {
833  kdDebug() << k_funcinfo << endl;
834  emit newTodoSignal( 0/*ResourceCalendar*/, TQString()/*subResource*/,
835  TQDate::currentDate().addDays(7) );
836 }
837 
838 void KOTodoView::newSubTodo()
839 {
840  if (mActiveItem) {
841  emit newSubTodoSignal(mActiveItem->todo());
842  }
843 }
844 
845 void KOTodoView::editTodo()
846 {
847  editItem( mActiveItem );
848 }
849 
850 void KOTodoView::showTodo()
851 {
852  showItem( mActiveItem );
853 }
854 
855 void 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 
877 void KOTodoView::deleteTodo()
878 {
879  if (mActiveItem) {
880  emit deleteIncidenceSignal( mActiveItem->todo() );
881  }
882 }
883 
884 void 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 
900 void 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 
944 void KOTodoView::setNewPercentage( int index )
945 {
946  setNewPercentage( mActiveItem, mPercentage[index] );
947 }
948 
949 void 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 
977 void 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 
1023 void 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 
1048 void KOTodoView::setDocumentId( const TQString &id )
1049 {
1050  kdDebug(5850) << "KOTodoView::setDocumentId()" << endl;
1051 
1052  mDocPrefs->setDoc( id );
1053 }
1054 
1055 void 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 
1066 void KOTodoView::setNewPercentageDelayed( KOTodoViewItem *item, int percentage )
1067 {
1068  mPercentChangedMap.append( qMakePair( item, percentage ) );
1069 
1070  TQTimer::singleShot( 0, this, TQ_SLOT( processDelayedNewPercentage() ) );
1071 }
1072 
1073 void 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 
1082 void KOTodoView::saveLayout(TDEConfig *config, const TQString &group) const
1083 {
1084  mTodoListView->saveLayout(config,group);
1085 }
1086 
1087 void KOTodoView::restoreLayout(TDEConfig *config, const TQString &group)
1088 {
1089  mTodoListView->restoreLayout(config,group);
1090 }
1091 
1092 void 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 
1110 void KOTodoView::clearSelection()
1111 {
1112  mTodoListView->selectAll( false );
1113 }
1114 
1115 void KOTodoView::purgeCompleted()
1116 {
1117  emit purgeCompletedSignal();
1118 }
1119 
1120 void 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 
1135 void 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
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.
virtual Calendar * calendar()
Return calendar object of this view.
Definition: baseview.h:89