00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <qlayout.h>
00025 #include <qheader.h>
00026 #include <qcursor.h>
00027 #include <qtimer.h>
00028
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 #include <kglobal.h>
00032 #include <kiconloader.h>
00033 #include <kmessagebox.h>
00034
00035 #include <libkcal/icaldrag.h>
00036 #include <libkcal/vcaldrag.h>
00037 #include <libkcal/dndfactory.h>
00038
00039 #ifndef KORG_NOPRINTER
00040 #include "calprinter.h"
00041 #endif
00042 #include "docprefs.h"
00043
00044 #include "kotodoview.h"
00045 using namespace KOrg;
00046 #include "kotodoview.moc"
00047
00048 KOTodoListView::KOTodoListView(Calendar *calendar,QWidget *parent,
00049 const char *name) :
00050 KListView(parent,name)
00051 {
00052 mCalendar = calendar;
00053
00054 mOldCurrent = 0;
00055 mMousePressed = false;
00056
00057 setAcceptDrops(true);
00058 viewport()->setAcceptDrops(true);
00059 }
00060
00061 void KOTodoListView::contentsDragEnterEvent(QDragEnterEvent *e)
00062 {
00063 #ifndef KORG_NODND
00064
00065 if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00066 !QTextDrag::canDecode( e ) ) {
00067 e->ignore();
00068 return;
00069 }
00070
00071 mOldCurrent = currentItem();
00072 #endif
00073 }
00074
00075
00076 void KOTodoListView::contentsDragMoveEvent(QDragMoveEvent *e)
00077 {
00078 #ifndef KORG_NODND
00079
00080
00081 if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00082 !QTextDrag::canDecode( e ) ) {
00083 e->ignore();
00084 return;
00085 }
00086
00087 e->accept();
00088 #endif
00089 }
00090
00091 void KOTodoListView::contentsDragLeaveEvent(QDragLeaveEvent *)
00092 {
00093 #ifndef KORG_NODND
00094
00095
00096 setCurrentItem(mOldCurrent);
00097 setSelected(mOldCurrent,true);
00098 #endif
00099 }
00100
00101 void KOTodoListView::contentsDropEvent(QDropEvent *e)
00102 {
00103 #ifndef KORG_NODND
00104
00105
00106 if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00107 !QTextDrag::canDecode( e ) ) {
00108 e->ignore();
00109 return;
00110 }
00111
00112 DndFactory factory( mCalendar );
00113 Todo *todo = factory.createDropTodo(e);
00114
00115 if (todo) {
00116 e->acceptAction();
00117
00118 KOTodoViewItem *destination =
00119 (KOTodoViewItem *)itemAt(contentsToViewport(e->pos()));
00120 Todo *destinationEvent = 0;
00121 if (destination) destinationEvent = destination->todo();
00122
00123 Todo *existingTodo = mCalendar->todo(todo->uid());
00124
00125 if(existingTodo) {
00126
00127 Incidence *to = destinationEvent;
00128 while(to) {
00129 if (to->uid() == todo->uid()) {
00130 KMessageBox::sorry(this,
00131 i18n("Cannot move To-Do to itself or a child of itself"),
00132 i18n("Drop To-Do"));
00133 delete todo;
00134 return;
00135 }
00136 to = to->relatedTo();
00137 }
00138 existingTodo->setRelatedTo(destinationEvent);
00139 emit todoDropped(todo);
00140 delete todo;
00141 } else {
00142
00143 todo->setRelatedTo(destinationEvent);
00144 mCalendar->addTodo(todo);
00145 emit todoDropped(todo);
00146 }
00147 }
00148 else {
00149 QString text;
00150 if (QTextDrag::decode(e,text)) {
00151
00152 KOTodoViewItem *todoi = static_cast<KOTodoViewItem *>(itemAt( contentsToViewport(e->pos()) ));
00153 kdDebug() << "Dropped : " << text << endl;
00154 QStringList emails = QStringList::split(",",text);
00155 for(QStringList::ConstIterator it = emails.begin();it!=emails.end();++it) {
00156 kdDebug() << " Email: " << (*it) << endl;
00157 int pos = (*it).find("<");
00158 QString name = (*it).left(pos);
00159 QString email = (*it).mid(pos);
00160 if (!email.isEmpty() && todoi) {
00161 todoi->todo()->addAttendee(new Attendee(name,email));
00162 }
00163 }
00164 }
00165 else {
00166 kdDebug() << "KOTodoListView::contentsDropEvent(): Todo from drop not decodable" << endl;
00167 e->ignore();
00168 }
00169 }
00170 #endif
00171 }
00172
00173 void KOTodoListView::contentsMousePressEvent(QMouseEvent* e)
00174 {
00175 QListView::contentsMousePressEvent(e);
00176 QPoint p(contentsToViewport(e->pos()));
00177 QListViewItem *i = itemAt(p);
00178 if (i) {
00179
00180
00181 if (p.x() > header()->sectionPos(header()->mapToIndex(0)) +
00182 treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) +
00183 itemMargin() ||
00184 p.x() < header()->sectionPos(header()->mapToIndex(0))) {
00185 if (e->button()==Qt::LeftButton) {
00186 mPressPos = e->pos();
00187 mMousePressed = true;
00188 }
00189 }
00190 }
00191 }
00192
00193 void KOTodoListView::contentsMouseMoveEvent(QMouseEvent* e)
00194 {
00195 #ifndef KORG_NODND
00196
00197 QListView::contentsMouseMoveEvent(e);
00198 if (mMousePressed && (mPressPos - e->pos()).manhattanLength() >
00199 QApplication::startDragDistance()) {
00200 mMousePressed = false;
00201 QListViewItem *item = itemAt(contentsToViewport(mPressPos));
00202 if (item) {
00203
00204 DndFactory factory( mCalendar );
00205 ICalDrag *vd = factory.createDragTodo(
00206 ((KOTodoViewItem *)item)->todo(),viewport());
00207 if (vd->drag()) {
00208 kdDebug() << "KOTodoListView::contentsMouseMoveEvent(): Delete drag source" << endl;
00209 }
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 }
00220 }
00221 #endif
00222 }
00223
00224 void KOTodoListView::contentsMouseReleaseEvent(QMouseEvent *e)
00225 {
00226 QListView::contentsMouseReleaseEvent(e);
00227 mMousePressed = false;
00228 }
00229
00230 void KOTodoListView::contentsMouseDoubleClickEvent(QMouseEvent *e)
00231 {
00232 if (!e) return;
00233
00234 QPoint vp = contentsToViewport(e->pos());
00235
00236 QListViewItem *item = itemAt(vp);
00237
00238 if (!item) return;
00239
00240 emit doubleClicked(item,vp,0);
00241 }
00242
00244
00245 KOTodoView::KOTodoView(Calendar *calendar,QWidget* parent,const char* name) :
00246 KOrg::BaseView(calendar,parent,name)
00247 {
00248 QBoxLayout *topLayout = new QVBoxLayout(this);
00249
00250 QLabel *title = new QLabel(i18n("To-do items:"),this);
00251 title->setFrameStyle(QFrame::Panel|QFrame::Raised);
00252 topLayout->addWidget(title);
00253
00254 mTodoListView = new KOTodoListView(calendar,this);
00255 topLayout->addWidget(mTodoListView);
00256
00257 mTodoListView->setRootIsDecorated(true);
00258 mTodoListView->setAllColumnsShowFocus(true);
00259
00260 mTodoListView->setShowSortIndicator(true);
00261
00262 mTodoListView->addColumn(i18n("Summary"));
00263 mTodoListView->addColumn(i18n("Priority"));
00264 mTodoListView->setColumnAlignment(1,AlignHCenter);
00265 mTodoListView->addColumn(i18n("Complete"));
00266 mTodoListView->setColumnAlignment(2,AlignRight);
00267 mTodoListView->addColumn(i18n("Due Date"));
00268 mTodoListView->setColumnAlignment(3,AlignHCenter);
00269 mTodoListView->addColumn(i18n("Due Time"));
00270 mTodoListView->setColumnAlignment(4,AlignHCenter);
00271 mTodoListView->addColumn(i18n("Categories"));
00272 #if 0
00273 mTodoListView->addColumn(i18n("Sort Id"));
00274 mTodoListView->setColumnAlignment(4,AlignHCenter);
00275 #endif
00276
00277 mTodoListView->setMinimumHeight( 60 );
00278 mTodoListView->setItemsRenameable( TRUE );
00279 mTodoListView->setRenameable( 0 );
00280
00281 mTodoListView->setColumnWidthMode(0, QListView::Manual);
00282 mTodoListView->setColumnWidthMode(1, QListView::Manual);
00283 mTodoListView->setColumnWidthMode(2, QListView::Manual);
00284 mTodoListView->setColumnWidthMode(3, QListView::Manual);
00285 mTodoListView->setColumnWidthMode(4, QListView::Manual);
00286 mTodoListView->setColumnWidthMode(5, QListView::Manual);
00287 #if 0
00288 mTodoListView->setColumnWidthMode(6, QListView::Manual);
00289 #endif
00290
00291 mPriorityPopupMenu = new QPopupMenu(this);
00292 for (int i = 1; i <= 5; i++) {
00293 QString label = QString ("%1").arg (i);
00294 mPriority[mPriorityPopupMenu->insertItem (label)] = i;
00295 }
00296 connect (mPriorityPopupMenu, SIGNAL(activated (int)), SLOT (setNewPriority(int)));
00297
00298 mPercentageCompletedPopupMenu = new QPopupMenu(this);
00299 for (int i = 0; i <= 100; i+=20) {
00300 QString label = QString ("%1 %").arg (i);
00301 mPercentage[mPercentageCompletedPopupMenu->insertItem (label)] = i;
00302 }
00303 connect (mPercentageCompletedPopupMenu, SIGNAL (activated (int)), SLOT (setNewPercentage (int)));
00304
00305
00306
00307 mItemPopupMenu = new QPopupMenu(this);
00308 mItemPopupMenu->insertItem(i18n("Show"), this,
00309 SLOT (showTodo()));
00310 mItemPopupMenu->insertItem(i18n("Edit..."), this,
00311 SLOT (editTodo()));
00312 mItemPopupMenu->insertItem(SmallIconSet("editdelete"), i18n("Delete"), this,
00313 SLOT (deleteTodo()));
00314 mItemPopupMenu->insertSeparator();
00315 mItemPopupMenu->insertItem(SmallIconSet("todo"), i18n("New To-Do..."), this,
00316 SLOT (newTodo()));
00317 mItemPopupMenu->insertItem(i18n("New Sub-To-Do..."), this,
00318 SLOT (newSubTodo()));
00319 mItemPopupMenu->insertSeparator();
00320 mItemPopupMenu->insertItem(i18n("delete completed To-Dos","Purge Completed"),
00321 this, SLOT( purgeCompleted() ) );
00322
00323 mPopupMenu = new QPopupMenu(this);
00324 mPopupMenu->insertItem(SmallIconSet("todo"), i18n("New To-Do"), this,
00325 SLOT (newTodo()));
00326 mPopupMenu->insertItem(i18n("delete completed To-Dos","Purge Completed"),
00327 this, SLOT(purgeCompleted()));
00328
00329 mDocPrefs = new DocPrefs( name );
00330
00331
00332 connect( mTodoListView, SIGNAL( doubleClicked( QListViewItem *,
00333 const QPoint &, int) ),
00334 SLOT( editItem( QListViewItem *, const QPoint &, int) ) );
00335 connect( mTodoListView, SIGNAL( contextMenuRequested( QListViewItem *,
00336 const QPoint &, int ) ),
00337 SLOT( popupMenu( QListViewItem *, const QPoint &, int ) ) );
00338 connect( mTodoListView, SIGNAL( clicked( QListViewItem * ) ),
00339 SLOT( itemClicked( QListViewItem * ) ) );
00340 connect( mTodoListView, SIGNAL( todoDropped( Todo * ) ),
00341 SLOT( updateView() ) );
00342 connect( mTodoListView, SIGNAL( expanded( QListViewItem * ) ),
00343 SLOT( itemStateChanged( QListViewItem * ) ) );
00344 connect( mTodoListView, SIGNAL( collapsed( QListViewItem * ) ),
00345 SLOT( itemStateChanged( QListViewItem * ) ) );
00346
00347 #if 0
00348 connect(mTodoListView,SIGNAL(selectionChanged(QListViewItem *)),
00349 SLOT(selectionChanged(QListViewItem *)));
00350 connect(mTodoListView,SIGNAL(clicked(QListViewItem *)),
00351 SLOT(selectionChanged(QListViewItem *)));
00352 connect(mTodoListView,SIGNAL(pressed(QListViewItem *)),
00353 SLOT(selectionChanged(QListViewItem *)));
00354 #endif
00355 connect( mTodoListView, SIGNAL(selectionChanged() ),
00356 SLOT( processSelectionChange() ) );
00357 }
00358
00359 KOTodoView::~KOTodoView()
00360 {
00361 delete mDocPrefs;
00362 }
00363
00364 void KOTodoView::updateView()
00365 {
00366
00367
00368 mTodoListView->clear();
00369
00370 QPtrList<Todo> todoList = calendar()->todos();
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393 mTodoMap.clear();
00394 Todo *todo;
00395 for(todo = todoList.first(); todo; todo = todoList.next()) {
00396 if (!mTodoMap.contains(todo)) {
00397 insertTodoItem(todo);
00398 }
00399 }
00400
00401
00402 mTodoListView->blockSignals( true );
00403 if( mDocPrefs ) restoreItemState( mTodoListView->firstChild() );
00404 mTodoListView->blockSignals( false );
00405
00406 processSelectionChange();
00407 }
00408
00409 void KOTodoView::restoreItemState( QListViewItem *item )
00410 {
00411 while( item ) {
00412 KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
00413 todoItem->setOpen( mDocPrefs->readBoolEntry( todoItem->todo()->uid() ) );
00414 if( item->childCount() > 0 ) restoreItemState( item->firstChild() );
00415 item = item->nextSibling();
00416 }
00417 }
00418
00419
00420 QMap<Todo *,KOTodoViewItem *>::ConstIterator
00421 KOTodoView::insertTodoItem(Todo *todo)
00422 {
00423
00424
00425 Incidence *incidence = todo->relatedTo();
00426 if (incidence && incidence->type() == "Todo") {
00427 Todo *relatedTodo = static_cast<Todo *>(incidence);
00428
00429
00430 QMap<Todo *,KOTodoViewItem *>::ConstIterator itemIterator;
00431 itemIterator = mTodoMap.find(relatedTodo);
00432 if (itemIterator == mTodoMap.end()) {
00433
00434 itemIterator = insertTodoItem (relatedTodo);
00435 }
00436
00437
00438 KOTodoViewItem *todoItem = new KOTodoViewItem(*itemIterator,todo,this);
00439 return mTodoMap.insert(todo,todoItem);
00440 } else {
00441
00442
00443 KOTodoViewItem *todoItem = new KOTodoViewItem(mTodoListView,todo,this);
00444 return mTodoMap.insert(todo,todoItem);
00445 }
00446 }
00447
00448
00449 void KOTodoView::updateConfig()
00450 {
00451 mTodoListView->repaintContents();
00452 }
00453
00454 QPtrList<Incidence> KOTodoView::selectedIncidences()
00455 {
00456 QPtrList<Incidence> selected;
00457
00458 KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
00459
00460 if (item) selected.append(item->todo());
00461
00462 return selected;
00463 }
00464
00465 QPtrList<Todo> KOTodoView::selectedTodos()
00466 {
00467 QPtrList<Todo> selected;
00468
00469 KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
00470
00471 if (item) selected.append(item->todo());
00472
00473 return selected;
00474 }
00475
00476 void KOTodoView::changeEventDisplay(Event *, int)
00477 {
00478 updateView();
00479 }
00480
00481 void KOTodoView::showDates(const QDate &, const QDate &)
00482 {
00483 }
00484
00485 void KOTodoView::showEvents(QPtrList<Event>)
00486 {
00487 kdDebug() << "KOTodoView::selectEvents(): not yet implemented" << endl;
00488 }
00489
00490 void KOTodoView::printPreview(CalPrinter *calPrinter, const QDate &fd,
00491 const QDate &td)
00492 {
00493 #ifndef KORG_NOPRINTER
00494 calPrinter->preview(CalPrinter::Todolist, fd, td);
00495 #endif
00496 }
00497
00498 void KOTodoView::editItem(QListViewItem *item,const QPoint &,int)
00499 {
00500 emit editTodoSignal(((KOTodoViewItem *)item)->todo());
00501 }
00502
00503 void KOTodoView::showItem(QListViewItem *item,const QPoint &,int)
00504 {
00505 emit showTodoSignal(((KOTodoViewItem *)item)->todo());
00506 }
00507
00508 void KOTodoView::popupMenu(QListViewItem *item,const QPoint &,int column)
00509 {
00510 mActiveItem = (KOTodoViewItem *)item;
00511 if (item) {
00512 switch (column){
00513 case 1:
00514 mPriorityPopupMenu->popup(QCursor::pos ()); break;
00515 case 2:
00516 mPercentageCompletedPopupMenu->popup(QCursor::pos ()); break;
00517 case 5:
00518 getCategoryPopupMenu((KOTodoViewItem *)item)->popup(QCursor::pos ()); break;
00519 default:
00520 mItemPopupMenu->popup(QCursor::pos());
00521 }
00522 } else mPopupMenu->popup(QCursor::pos());
00523 }
00524
00525 void KOTodoView::newTodo()
00526 {
00527 emit newTodoSignal();
00528 }
00529
00530 void KOTodoView::newSubTodo()
00531 {
00532 if (mActiveItem) {
00533 emit newSubTodoSignal(mActiveItem->todo());
00534 }
00535 }
00536
00537 void KOTodoView::editTodo()
00538 {
00539 if (mActiveItem) {
00540 emit editTodoSignal(mActiveItem->todo());
00541 }
00542 }
00543
00544 void KOTodoView::showTodo()
00545 {
00546 if (mActiveItem) {
00547 emit showTodoSignal(mActiveItem->todo());
00548 }
00549 }
00550
00551 void KOTodoView::deleteTodo()
00552 {
00553 if (mActiveItem) {
00554 if (mActiveItem->childCount()) {
00555 KMessageBox::sorry(this,i18n("Cannot delete To-Do which has children."),
00556 i18n("Delete To-Do"));
00557 } else {
00558 emit deleteTodoSignal(mActiveItem->todo());
00559 }
00560 }
00561 }
00562
00563 void KOTodoView::setNewPriority(int index)
00564 {
00565 if (mActiveItem && !mActiveItem->todo()->isReadOnly ()) {
00566 mActiveItem->todo()->setPriority(mPriority[index]);
00567 mActiveItem->construct();
00568 emit todoModifiedSignal (mActiveItem->todo(), KOGlobals::PRIORITY_MODIFIED);
00569 }
00570 }
00571
00572 void KOTodoView::setNewPercentage(int index)
00573 {
00574 if (mActiveItem && !mActiveItem->todo()->isReadOnly ()) {
00575 if (mPercentage[index] == 100) {
00576 mActiveItem->todo()->setCompleted(QDateTime::currentDateTime());
00577 } else {
00578 mActiveItem->todo()->setCompleted(false);
00579 }
00580 mActiveItem->todo()->setPercentComplete(mPercentage[index]);
00581 mActiveItem->construct();
00582 emit todoModifiedSignal (mActiveItem->todo (), KOGlobals::COMPLETION_MODIFIED);
00583 }
00584 }
00585
00586
00587 QPopupMenu * KOTodoView::getCategoryPopupMenu (KOTodoViewItem *todoItem)
00588 {
00589 QPopupMenu* tempMenu = new QPopupMenu (this);
00590 QStringList checkedCategories = todoItem->todo()->categories ();
00591
00592 tempMenu->setCheckable (true);
00593 for (QStringList::Iterator it = KOPrefs::instance()->mCustomCategories.begin ();
00594 it != KOPrefs::instance()->mCustomCategories.end ();
00595 ++it) {
00596 int index = tempMenu->insertItem (*it);
00597 mCategory[index] = *it;
00598 if (checkedCategories.find (*it) != checkedCategories.end ()) tempMenu->setItemChecked (index, true);
00599 }
00600
00601 connect (tempMenu, SIGNAL (activated (int)), SLOT (changedCategories (int)));
00602 return tempMenu;
00603
00604
00605 }
00606 void KOTodoView::changedCategories(int index)
00607 {
00608 if (mActiveItem && !mActiveItem->todo()->isReadOnly ()) {
00609 QStringList categories = mActiveItem->todo()->categories ();
00610 if (categories.find (mCategory[index]) != categories.end ())
00611 categories.remove (mCategory[index]);
00612 else
00613 categories.insert (categories.end(), mCategory[index]);
00614 categories.sort ();
00615 mActiveItem->todo()->setCategories (categories);
00616 mActiveItem->construct();
00617 emit todoModifiedSignal (mActiveItem->todo (), KOGlobals::CATEGORY_MODIFIED);
00618 }
00619 }
00620
00621 void KOTodoView::itemClicked(QListViewItem *item)
00622 {
00623 if (!item) return;
00624
00625 KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
00626 int completed = todoItem->todo()->isCompleted();
00627
00628 if (todoItem->isOn()) {
00629 if (!completed) {
00630 todoItem->todo()->setCompleted(QDateTime::currentDateTime());
00631 }
00632 } else {
00633 if (completed) {
00634 todoItem->todo()->setCompleted(false);
00635 }
00636 }
00637 }
00638
00639 void KOTodoView::setDocumentId( const QString &id )
00640 {
00641 kdDebug() << "KOTodoView::setDocumentId()" << endl;
00642
00643 mDocPrefs->setDoc( id );
00644 }
00645
00646 void KOTodoView::itemStateChanged( QListViewItem *item )
00647 {
00648 if (!item) return;
00649
00650 KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
00651
00652
00653
00654 if( mDocPrefs ) mDocPrefs->writeEntry( todoItem->todo()->uid(), todoItem->isOpen() );
00655 }
00656
00657 void KOTodoView::saveLayout(KConfig *config, const QString &group) const
00658 {
00659 mTodoListView->saveLayout(config,group);
00660 }
00661
00662 void KOTodoView::restoreLayout(KConfig *config, const QString &group)
00663 {
00664 mTodoListView->restoreLayout(config,group);
00665 }
00666
00667 void KOTodoView::processSelectionChange()
00668 {
00669
00670
00671 KOTodoViewItem *item =
00672 static_cast<KOTodoViewItem *>( mTodoListView->selectedItem() );
00673
00674 if ( !item ) {
00675 emit incidenceSelected( 0 );
00676 } else {
00677 emit incidenceSelected( item->todo() );
00678 }
00679 }
00680
00681 void KOTodoView::modified(bool b)
00682 {
00683 emit isModified(b);
00684 }
00685
00686 void KOTodoView::setTodoModifiedDelayed( Todo *todo )
00687 {
00688 mTodo = todo;
00689 QTimer::singleShot( 0, this, SLOT( setTodoModified() ) );
00690 }
00691
00692 void KOTodoView::setTodoModified()
00693 {
00694 emit todoModifiedSignal( mTodo, KOGlobals::UNKNOWN_MODIFIED );
00695 }
00696
00697 void KOTodoView::clearSelection()
00698 {
00699 mTodoListView->selectAll( false );
00700 }
00701
00702 void KOTodoView::purgeCompleted()
00703 {
00704 emit purgeCompletedSignal();
00705 }