korganizer

kotodoview.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include <qlayout.h>
00027 #include <qheader.h>
00028 #include <qcursor.h>
00029 #include <qlabel.h>
00030 #include <qtimer.h>
00031 
00032 #include <kdebug.h>
00033 #include <klocale.h>
00034 #include <kglobal.h>
00035 #include <kiconloader.h>
00036 #include <kmessagebox.h>
00037 
00038 #include <libkcal/calhelper.h>
00039 #include <libkcal/icaldrag.h>
00040 #include <libkcal/vcaldrag.h>
00041 #include <libkcal/dndfactory.h>
00042 #include <libkcal/calendarresources.h>
00043 #include <libkcal/resourcecalendar.h>
00044 #include <libkcal/calfilter.h>
00045 #include <libkcal/incidenceformatter.h>
00046 
00047 #include <libkdepim/clicklineedit.h>
00048 #include <libkdepim/kdatepickerpopup.h>
00049 
00050 #include <libemailfunctions/email.h>
00051 
00052 #include "docprefs.h"
00053 
00054 #include "koincidencetooltip.h"
00055 #include "kodialogmanager.h"
00056 #include "kotodoview.h"
00057 #include "koprefs.h"
00058 #include "koglobals.h"
00059 using namespace KOrg;
00060 #include "kotodoviewitem.h"
00061 #include "kotodoview.moc"
00062 #ifndef KORG_NOPRINTER
00063 #include "kocorehelper.h"
00064 #include "calprinter.h"
00065 #endif
00066 
00067 KOTodoListViewToolTip::KOTodoListViewToolTip (QWidget *parent,
00068                                               Calendar *calendar,
00069                                               KOTodoListView *lv )
00070   :QToolTip(parent), mCalendar( calendar )
00071 {
00072   todolist=lv;
00073 }
00074 
00075 void KOTodoListViewToolTip::maybeTip( const QPoint & pos)
00076 {
00077   QRect r;
00078   int headerPos;
00079   int col=todolist->header()->sectionAt(todolist->contentsX() + pos.x());
00080   KOTodoViewItem *i=(KOTodoViewItem *)todolist->itemAt(pos);
00081 
00082   /* Check wether a tooltip is necessary. */
00083   if( i && KOPrefs::instance()->mEnableToolTips )
00084   {
00085 
00086     /* Calculate the rectangle. */
00087     r=todolist->itemRect(i);
00088     headerPos = todolist->header()->sectionPos(col)-todolist->contentsX();
00089     r.setLeft( (headerPos < 0 ? 0 : headerPos) );
00090     r.setRight(headerPos + todolist->header()->sectionSize(col));
00091 
00092     /* Show the tip */
00093     QString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->todo(), QDate(), true ) );;
00094     if ( !tipText.isEmpty() ) {
00095       tip(r, tipText);
00096     }
00097   }
00098 
00099 }
00100 
00101 
00102 
00103 KOTodoListView::KOTodoListView( QWidget *parent, const char *name )
00104   : KListView( parent, name ), mCalendar( 0 ), mChanger( 0 )
00105 {
00106   mOldCurrent = 0;
00107   mMousePressed = false;
00108 }
00109 
00110 KOTodoListView::~KOTodoListView()
00111 {
00112 }
00113 
00114 void KOTodoListView::setCalendar( Calendar *cal )
00115 {
00116   mCalendar = cal;
00117   setAcceptDrops( mCalendar );
00118   viewport()->setAcceptDrops( mCalendar );
00119 }
00120 
00121 bool KOTodoListView::event(QEvent *e)
00122 {
00123   int tmp=0;
00124   KOTodoViewItem *i;
00125 
00126   /* Checks for an ApplicationPaletteChange event and updates
00127    * the small Progress bars to make therm have the right colors. */
00128   if(e->type()==QEvent::ApplicationPaletteChange)
00129   {
00130 
00131     KListView::event(e);
00132     i=(KOTodoViewItem *)itemAtIndex(tmp);
00133 
00134     while(i!=0)
00135     {
00136       i->construct();
00137       tmp++;
00138       i=(KOTodoViewItem *)itemAtIndex(tmp);
00139     }
00140 
00141   }
00142 
00143   return (KListView::event(e) || e->type()==QEvent::ApplicationPaletteChange);
00144 }
00145 
00146 void KOTodoListView::contentsDragEnterEvent(QDragEnterEvent *e)
00147 {
00148 #ifndef KORG_NODND
00149 //  kdDebug(5850) << "KOTodoListView::contentsDragEnterEvent" << endl;
00150   if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00151        !QTextDrag::canDecode( e ) ) {
00152     e->ignore();
00153     return;
00154   }
00155 
00156   mOldCurrent = currentItem();
00157 #endif
00158 }
00159 
00160 void KOTodoListView::contentsDragMoveEvent(QDragMoveEvent *e)
00161 {
00162 #ifndef KORG_NODND
00163 //  kdDebug(5850) << "KOTodoListView::contentsDragMoveEvent" << endl;
00164 
00165   if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00166        !QTextDrag::canDecode( e ) ) {
00167     e->ignore();
00168     return;
00169   }
00170 
00171   e->accept();
00172 #endif
00173 }
00174 
00175 void KOTodoListView::contentsDragLeaveEvent( QDragLeaveEvent * )
00176 {
00177 #ifndef KORG_NODND
00178 //  kdDebug(5850) << "KOTodoListView::contentsDragLeaveEvent" << endl;
00179 
00180   setCurrentItem(mOldCurrent);
00181   setSelected(mOldCurrent,true);
00182 #endif
00183 }
00184 
00185 void KOTodoListView::contentsDropEvent( QDropEvent *e )
00186 {
00187 #ifndef KORG_NODND
00188   kdDebug(5850) << "KOTodoListView::contentsDropEvent" << endl;
00189 
00190   if ( !mCalendar || !mChanger ||
00191        ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00192          !QTextDrag::canDecode( e ) ) ) {
00193     e->ignore();
00194     return;
00195   }
00196 
00197   DndFactory factory( mCalendar );
00198   Todo *todo = factory.createDropTodo(e);
00199 
00200   if ( todo ) {
00201     e->acceptAction();
00202 
00203     KOTodoViewItem *destination =
00204         (KOTodoViewItem *)itemAt(contentsToViewport(e->pos()));
00205     Todo *destinationEvent = 0;
00206     if (destination) destinationEvent = destination->todo();
00207 
00208     Todo *existingTodo = mCalendar->todo(todo->uid());
00209 
00210     if( existingTodo ) {
00211        kdDebug(5850) << "Drop existing Todo " << existingTodo << " onto " << destinationEvent << endl;
00212       Incidence *to = destinationEvent;
00213       while(to) {
00214         if (to->uid() == todo->uid()) {
00215           KMessageBox::information(this,
00216               i18n("Cannot move to-do to itself or a child of itself."),
00217               i18n("Drop To-do"), "NoDropTodoOntoItself" );
00218           delete todo;
00219           return;
00220         }
00221         to = to->relatedTo();
00222       }
00223 
00224       Todo*oldTodo = existingTodo->clone();
00225       if ( mChanger->beginChange( existingTodo, 0, QString() ) ) {
00226         existingTodo->setRelatedTo( destinationEvent );
00227         mChanger->changeIncidence( oldTodo, existingTodo, KOGlobals::RELATION_MODIFIED, this );
00228         mChanger->endChange( existingTodo, 0, QString() );
00229       } else {
00230         KMessageBox::sorry( this, i18n("Unable to change to-do's parent, "
00231                             "because the to-do cannot be locked.") );
00232       }
00233       delete oldTodo;
00234       delete todo;
00235     } else {
00236 //      kdDebug(5850) << "Drop new Todo" << endl;
00237       todo->setRelatedTo(destinationEvent);
00238       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
00239         KODialogManager::errorSaveIncidence( this, todo );
00240         delete todo;
00241         return;
00242       }
00243     }
00244   } else {
00245     QString text;
00246     KOTodoViewItem *todoi = dynamic_cast<KOTodoViewItem *>(itemAt( contentsToViewport(e->pos()) ));
00247     if ( ! todoi ) {
00248       // Not dropped on a todo item:
00249       e->ignore();
00250       kdDebug( 5850 ) << "KOTodoListView::contentsDropEvent(): Not dropped on a todo item" << endl;
00251       kdDebug( 5850 ) << "TODO: Create a new todo with the given data" << endl;
00252       // FIXME: Create a new todo with the given text/contact/whatever
00253     } else if ( QTextDrag::decode(e, text) ) {
00254       //QListViewItem *qlvi = itemAt( contentsToViewport(e->pos()) );
00255       kdDebug(5850) << "Dropped : " << text << endl;
00256       Todo*todo = todoi->todo();
00257       if( mChanger->beginChange( todo, 0, QString() ) ) {
00258         Todo*oldtodo = todo->clone();
00259 
00260         if( text.startsWith( "file:" ) ) {
00261           todo->addAttachment( new Attachment( text ) );
00262         } else {
00263           QStringList emails = KPIM::splitEmailAddrList( text );
00264           for(QStringList::ConstIterator it = emails.begin();it!=emails.end();++it) {
00265             kdDebug(5850) << " Email: " << (*it) << endl;
00266             int pos = (*it).find("<");
00267             QString name = (*it).left(pos);
00268             QString email = (*it).mid(pos);
00269             if (!email.isEmpty() && todoi) {
00270               todo->addAttendee( new Attendee( name, email ) );
00271             }
00272           }
00273         }
00274         //FIXME: attendees or attachment added, so there is something modified
00275         mChanger->changeIncidence( oldtodo, todo, KOGlobals::NOTHING_MODIFIED, this );
00276         mChanger->endChange( todo, 0, QString() );
00277       } else {
00278         KMessageBox::sorry( this, i18n("Unable to add attendees to the to-do, "
00279             "because the to-do cannot be locked.") );
00280       }
00281     }
00282     else {
00283       kdDebug(5850) << "KOTodoListView::contentsDropEvent(): Todo from drop not decodable" << endl;
00284       e->ignore();
00285     }
00286   }
00287 #endif
00288 }
00289 
00290 void KOTodoListView::contentsMousePressEvent(QMouseEvent* e)
00291 {
00292   QListView::contentsMousePressEvent(e);
00293   QPoint p(contentsToViewport(e->pos()));
00294   QListViewItem *i = itemAt(p);
00295   if (i) {
00296     // if the user clicked into the root decoration of the item, don't
00297     // try to start a drag!
00298     if (p.x() > header()->sectionPos(header()->mapToIndex(0)) +
00299         treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) +
00300         itemMargin() ||
00301         p.x() < header()->sectionPos(header()->mapToIndex(0))) {
00302       if (e->button()==Qt::LeftButton) {
00303         mPressPos = e->pos();
00304         mMousePressed = true;
00305       }
00306     }
00307   }
00308 }
00309 
00310 void KOTodoListView::contentsMouseMoveEvent(QMouseEvent* e)
00311 {
00312 #ifndef KORG_NODND
00313 //  kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent()" << endl;
00314   QListView::contentsMouseMoveEvent(e);
00315   if (mMousePressed && (mPressPos - e->pos()).manhattanLength() >
00316       QApplication::startDragDistance()) {
00317     mMousePressed = false;
00318     QListViewItem *item = itemAt(contentsToViewport(mPressPos));
00319     if ( item && mCalendar ) {
00320 //      kdDebug(5850) << "Start Drag for item " << item->text(0) << endl;
00321       DndFactory factory( mCalendar );
00322       ICalDrag *vd = factory.createDrag(
00323                           ((KOTodoViewItem *)item)->todo(),viewport());
00324       if (vd->drag()) {
00325         kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent(): Delete drag source" << endl;
00326       }
00327 /*
00328       QString source = fullPath(item);
00329       if ( QFile::exists(source) ) {
00330         KURL url;
00331         url.setPath(source);
00332         KURLDrag* ud = KURLDrag::newDrag(KURL::List(url), viewport());
00333         if ( ud->drag() )
00334           QMessageBox::information( this, "Drag source",
00335                                     QString("Delete ")+source, "Not implemented" );
00336 */
00337     }
00338   }
00339 #endif
00340 }
00341 
00342 void KOTodoListView::contentsMouseReleaseEvent(QMouseEvent *e)
00343 {
00344   QListView::contentsMouseReleaseEvent(e);
00345   mMousePressed = false;
00346 }
00347 
00348 void KOTodoListView::contentsMouseDoubleClickEvent(QMouseEvent *e)
00349 {
00350   if (!e) return;
00351 
00352   QPoint vp = contentsToViewport(e->pos());
00353 
00354   QListViewItem *item = itemAt(vp);
00355 
00356   if (!item) return;
00357 
00358   emit doubleClicked(item,vp,0);
00359 }
00360 
00362 
00363 KOTodoView::KOTodoView( Calendar *calendar, QWidget *parent, const char* name)
00364   : KOrg::BaseView( calendar, parent, name )
00365 {
00366   QBoxLayout *topLayout = new QVBoxLayout( this );
00367 
00368   QLabel *title = new QLabel( i18n("To-dos:"), this );
00369   title->setFrameStyle( QFrame::Panel | QFrame::Raised );
00370   topLayout->addWidget( title );
00371 
00372   mQuickAdd = new KPIM::ClickLineEdit( this, i18n( "Click to add a new to-do" ) );
00373   mQuickAdd->setAcceptDrops( false );
00374   topLayout->addWidget( mQuickAdd );
00375 
00376   if ( !KOPrefs::instance()->mEnableQuickTodo ) mQuickAdd->hide();
00377 
00378   mTodoListView = new KOTodoListView( this );
00379   topLayout->addWidget( mTodoListView );
00380 
00381   mTodoListView->setRootIsDecorated( true );
00382   mTodoListView->setAllColumnsShowFocus( true );
00383 
00384   mTodoListView->setShowSortIndicator( true );
00385 
00386   mTodoListView->addColumn( i18n("Summary") );
00387   mTodoListView->addColumn( i18n("Recurs") );
00388   mTodoListView->addColumn( i18n("Priority") );
00389   mTodoListView->setColumnAlignment( ePriorityColumn, AlignHCenter );
00390   mTodoListView->addColumn( i18n("Complete") );
00391   mTodoListView->setColumnAlignment( ePercentColumn, AlignRight );
00392   mTodoListView->addColumn( i18n("Due Date/Time") );
00393   mTodoListView->setColumnAlignment( eDueDateColumn, AlignLeft );
00394   mTodoListView->addColumn( i18n("Categories") );
00395   mTodoListView->addColumn( i18n( "Calendar" ) );
00396 #if 0
00397   mTodoListView->addColumn( i18n("Sort Id") );
00398   mTodoListView->setColumnAlignment( 4, AlignHCenter );
00399 #endif
00400 
00401   mTodoListView->setMinimumHeight( 60 );
00402   mTodoListView->setItemsRenameable( true );
00403   mTodoListView->setRenameable( 0 );
00404 
00405   mTodoListView->setColumnWidthMode( eSummaryColumn, QListView::Manual );
00406   mTodoListView->setColumnWidthMode( eRecurColumn, QListView::Manual );
00407   mTodoListView->setColumnWidthMode( ePriorityColumn, QListView::Manual );
00408   mTodoListView->setColumnWidthMode( ePercentColumn, QListView::Manual );
00409   mTodoListView->setColumnWidthMode( eDueDateColumn, QListView::Manual );
00410   mTodoListView->setColumnWidthMode( eCategoriesColumn, QListView::Manual );
00411   mTodoListView->setColumnWidthMode( eFolderColumn, QListView::Manual );
00412 #if 0
00413   mTodoListView->setColumnWidthMode( eDescriptionColumn, QListView::Manual );
00414 #endif
00415 
00416   mPriorityPopupMenu = new QPopupMenu( this );
00417   mPriority[ mPriorityPopupMenu->insertItem( i18n("Unspecified priority", "unspecified") ) ] = 0;
00418   mPriority[ mPriorityPopupMenu->insertItem( i18n( "1 (highest)") ) ] = 1;
00419   mPriority[ mPriorityPopupMenu->insertItem( i18n( "2" ) ) ] = 2;
00420   mPriority[ mPriorityPopupMenu->insertItem( i18n( "3" ) ) ] = 3;
00421   mPriority[ mPriorityPopupMenu->insertItem( i18n( "4" ) ) ] = 4;
00422   mPriority[ mPriorityPopupMenu->insertItem( i18n( "5 (medium)" ) ) ] = 5;
00423   mPriority[ mPriorityPopupMenu->insertItem( i18n( "6" ) ) ] = 6;
00424   mPriority[ mPriorityPopupMenu->insertItem( i18n( "7" ) ) ] = 7;
00425   mPriority[ mPriorityPopupMenu->insertItem( i18n( "8" ) ) ] = 8;
00426   mPriority[ mPriorityPopupMenu->insertItem( i18n( "9 (lowest)" ) ) ] = 9;
00427   connect( mPriorityPopupMenu, SIGNAL( activated( int ) ),
00428            SLOT( setNewPriority( int ) ));
00429 
00430   mPercentageCompletedPopupMenu = new QPopupMenu(this);
00431   for (int i = 0; i <= 100; i+=10) {
00432     QString label = QString ("%1 %").arg (i);
00433     mPercentage[mPercentageCompletedPopupMenu->insertItem (label)] = i;
00434   }
00435   connect( mPercentageCompletedPopupMenu, SIGNAL( activated( int ) ),
00436            SLOT( setNewPercentage( int ) ) );
00437 
00438   mMovePopupMenu = new KDatePickerPopup(
00439                              KDatePickerPopup::NoDate |
00440                              KDatePickerPopup::DatePicker |
00441                              KDatePickerPopup::Words );
00442   mCopyPopupMenu = new KDatePickerPopup(
00443                              KDatePickerPopup::NoDate |
00444                              KDatePickerPopup::DatePicker |
00445                              KDatePickerPopup::Words );
00446 
00447 
00448   connect( mMovePopupMenu, SIGNAL( dateChanged( QDate )),
00449            SLOT( setNewDate( QDate ) ) );
00450   connect( mCopyPopupMenu, SIGNAL( dateChanged( QDate )),
00451            SLOT( copyTodoToDate( QDate ) ) );
00452 
00453   mItemPopupMenu = new QPopupMenu(this);
00454   mItemPopupMenu->insertItem(i18n("&Show"), this,
00455                              SLOT (showTodo()));
00456   mItemPopupMenu->insertItem(i18n("&Edit..."), this,
00457                              SLOT (editTodo()), 0, ePopupEdit );
00458 #ifndef KORG_NOPRINTER
00459   mItemPopupMenu->insertItem(KOGlobals::self()->smallIcon("printer1"), i18n("&Print..."), this, SLOT( printTodo() ) );
00460 #endif
00461   mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("editdelete"), i18n("&Delete"), this,
00462                              SLOT (deleteTodo()), 0, ePopupDelete );
00463   mItemPopupMenu->insertSeparator();
00464   mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("New &To-do..."), this,
00465                              SLOT (newTodo()) );
00466   mItemPopupMenu->insertItem(i18n("New Su&b-to-do..."), this,
00467                              SLOT (newSubTodo()));
00468   mItemPopupMenu->insertItem( i18n("&Make this To-do Independent"), this,
00469       SIGNAL( unSubTodoSignal() ), 0, ePopupUnSubTodo );
00470   mItemPopupMenu->insertItem( i18n("Make all Sub-to-dos &Independent"), this,
00471       SIGNAL( unAllSubTodoSignal() ), 0, ePopupUnAllSubTodo );
00472   mItemPopupMenu->insertSeparator();
00473   mItemPopupMenu->insertItem( i18n("&Copy To"), mCopyPopupMenu, ePopupCopyTo );
00474   mItemPopupMenu->insertItem(i18n("&Move To"), mMovePopupMenu, ePopupMoveTo );
00475   mItemPopupMenu->insertSeparator();
00476   mItemPopupMenu->insertItem(i18n("delete completed to-dos","Pur&ge Completed"),
00477                              this, SLOT( purgeCompleted() ) );
00478 
00479   connect( mMovePopupMenu, SIGNAL( dateChanged( QDate ) ),
00480            mItemPopupMenu, SLOT( hide() ) );
00481   connect( mCopyPopupMenu, SIGNAL( dateChanged( QDate ) ),
00482            mItemPopupMenu, SLOT( hide() ) );
00483 
00484   mPopupMenu = new QPopupMenu(this);
00485   mPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("&New To-do..."), this,
00486                          SLOT(newTodo()) );
00487   mPopupMenu->insertItem(i18n("delete completed to-dos","&Purge Completed"),
00488                          this, SLOT(purgeCompleted()));
00489 
00490   mDocPrefs = new DocPrefs( name );
00491 
00492   // Double clicking conflicts with opening/closing the subtree
00493   connect( mTodoListView, SIGNAL( doubleClicked( QListViewItem *,
00494                                                  const QPoint &, int ) ),
00495            SLOT( editItem( QListViewItem *, const QPoint &, int ) ) );
00496   connect( mTodoListView, SIGNAL( returnPressed( QListViewItem * ) ),
00497            SLOT( editItem( QListViewItem * ) ) );
00498   connect( mTodoListView, SIGNAL( contextMenuRequested( QListViewItem *,
00499                                                         const QPoint &, int ) ),
00500            SLOT( popupMenu( QListViewItem *, const QPoint &, int ) ) );
00501   connect( mTodoListView, SIGNAL( expanded( QListViewItem * ) ),
00502            SLOT( itemStateChanged( QListViewItem * ) ) );
00503   connect( mTodoListView, SIGNAL( collapsed( QListViewItem * ) ),
00504            SLOT( itemStateChanged( QListViewItem * ) ) );
00505 
00506 #if 0
00507   connect(mTodoListView,SIGNAL(selectionChanged(QListViewItem *)),
00508           SLOT(selectionChanged(QListViewItem *)));
00509   connect(mTodoListView,SIGNAL(clicked(QListViewItem *)),
00510           SLOT(selectionChanged(QListViewItem *)));
00511   connect(mTodoListView,SIGNAL(pressed(QListViewItem *)),
00512           SLOT(selectionChanged(QListViewItem *)));
00513 #endif
00514   connect( mTodoListView, SIGNAL(selectionChanged() ),
00515            SLOT( processSelectionChange() ) );
00516   connect( mQuickAdd, SIGNAL( returnPressed () ),
00517            SLOT( addQuickTodo() ) );
00518 
00519   new KOTodoListViewToolTip( mTodoListView->viewport(), calendar, mTodoListView );
00520 }
00521 
00522 KOTodoView::~KOTodoView()
00523 {
00524   delete mDocPrefs;
00525 }
00526 
00527 void KOTodoView::setCalendar( Calendar *cal )
00528 {
00529   BaseView::setCalendar( cal );
00530   mTodoListView->setCalendar( cal );
00531 }
00532 
00533 void KOTodoView::updateView()
00534 {
00535 //  kdDebug(5850) << "KOTodoView::updateView()" << endl;
00536   int oldPos = mTodoListView->contentsY();
00537   mItemsToDelete.clear();
00538   mTodoListView->clear();
00539 
00540   Todo::List todoList = calendar()->todos();
00541 
00542 /*
00543   kdDebug(5850) << "KOTodoView::updateView(): Todo List:" << endl;
00544   Event *t;
00545   for(t = todoList.first(); t; t = todoList.next()) {
00546     kdDebug(5850) << "  " << t->getSummary() << endl;
00547 
00548     if (t->getRelatedTo()) {
00549       kdDebug(5850) << "      (related to " << t->getRelatedTo()->getSummary() << ")" << endl;
00550     }
00551 
00552     QPtrList<Event> l = t->getRelations();
00553     Event *c;
00554     for(c=l.first();c;c=l.next()) {
00555       kdDebug(5850) << "    - relation: " << c->getSummary() << endl;
00556     }
00557   }
00558 */
00559 
00560   // Put for each Event a KOTodoViewItem in the list view. Don't rely on a
00561   // specific order of events. That means that we have to generate parent items
00562   // recursively for proper hierarchical display of Todos.
00563   mTodoMap.clear();
00564   Todo::List::ConstIterator it;
00565   for( it = todoList.begin(); it != todoList.end(); ++it ) {
00566     if ( !mTodoMap.contains( *it ) ) {
00567       insertTodoItem( *it );
00568     }
00569   }
00570 
00571   // Restore opened/closed state
00572   mTodoListView->blockSignals( true );
00573   if( mDocPrefs ) restoreItemState( mTodoListView->firstChild() );
00574   mTodoListView->blockSignals( false );
00575 
00576   mTodoListView->setContentsPos( 0, oldPos );
00577 
00578   processSelectionChange();
00579 }
00580 
00581 void KOTodoView::restoreItemState( QListViewItem *item )
00582 {
00583   while( item ) {
00584     KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
00585     todoItem->setOpen( mDocPrefs->readBoolEntry( todoItem->todo()->uid() ) );
00586     if( item->childCount() > 0 ) restoreItemState( item->firstChild() );
00587     item = item->nextSibling();
00588   }
00589 }
00590 
00591 
00592 QMap<Todo *,KOTodoViewItem *>::ConstIterator
00593   KOTodoView::insertTodoItem(Todo *todo)
00594 {
00595 //  kdDebug(5850) << "KOTodoView::insertTodoItem(): " << todo->getSummary() << endl;
00596   Incidence *incidence = todo->relatedTo();
00597   if (incidence && incidence->type() == "Todo") {
00598     // Use dynamic_cast, because in the future the related item might also be an event
00599     Todo *relatedTodo = dynamic_cast<Todo *>(incidence);
00600 
00601     // just make sure we know we have this item already to avoid endless recursion (Bug 101696)
00602     mTodoMap.insert(todo,0);
00603 
00604 //    kdDebug(5850) << "  has Related" << endl;
00605     QMap<Todo *,KOTodoViewItem *>::ConstIterator itemIterator;
00606     itemIterator = mTodoMap.find(relatedTodo);
00607     if (itemIterator == mTodoMap.end()) {
00608 //      kdDebug(5850) << "    related not yet in list" << endl;
00609       itemIterator = insertTodoItem (relatedTodo);
00610     }
00611     // isn't this pretty stupid? We give one Todo  to the KOTodoViewItem
00612     // and one into the map. Sure finding is more easy but why? -zecke
00613     KOTodoViewItem *todoItem;
00614 
00615     // in case we found a related parent, which has no KOTodoViewItem yet, this must
00616     // be the case where 2 items refer to each other, therefore simply create item as root item
00617     if ( *itemIterator == 0 ) {
00618       todo->setRelatedTo(0);  // break the recursion, else we will have troubles later
00619       todoItem = new KOTodoViewItem(mTodoListView,todo,this);
00620     }
00621     else
00622       todoItem = new KOTodoViewItem(*itemIterator,todo,this);
00623 
00624     return mTodoMap.insert(todo,todoItem);
00625   } else {
00626 //    kdDebug(5850) << "  no Related" << endl;
00627       // see above -zecke
00628     KOTodoViewItem *todoItem = new KOTodoViewItem(mTodoListView,todo,this);
00629     return mTodoMap.insert(todo,todoItem);
00630   }
00631 }
00632 
00633 void KOTodoView::removeTodoItems()
00634 {
00635   KOTodoViewItem *item;
00636   for ( item = mItemsToDelete.first(); item; item = mItemsToDelete.next() ) {
00637     Todo *todo = item->todo();
00638     if ( todo && mTodoMap.contains( todo ) ) {
00639       mTodoMap.remove( todo );
00640     }
00641     delete item;
00642   }
00643   mItemsToDelete.clear();
00644 }
00645 
00646 
00647 bool KOTodoView::scheduleRemoveTodoItem( KOTodoViewItem *todoItem )
00648 {
00649   if ( todoItem ) {
00650     mItemsToDelete.append( todoItem );
00651     QTimer::singleShot( 0, this, SLOT( removeTodoItems() ) );
00652     return true;
00653   } else
00654     return false;
00655 }
00656 
00657 void KOTodoView::updateConfig()
00658 {
00659   mTodoListView->repaintContents();
00660 }
00661 
00662 Incidence::List KOTodoView::selectedIncidences()
00663 {
00664   Incidence::List selected;
00665 
00666   KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
00667 //  if (!item) item = mActiveItem;
00668   if (item) selected.append(item->todo());
00669 
00670   return selected;
00671 }
00672 
00673 Todo::List KOTodoView::selectedTodos()
00674 {
00675   Todo::List selected;
00676 
00677   KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem());
00678 //  if (!item) item = mActiveItem;
00679   if (item) selected.append(item->todo());
00680 
00681   return selected;
00682 }
00683 
00684 void KOTodoView::changeIncidenceDisplay(Incidence *incidence, int action)
00685 {
00686   // The todo view only displays todos, so exit on all other incidences
00687   if ( incidence->type() != "Todo" )
00688     return;
00689   CalFilter *filter = calendar()->filter();
00690   bool isFiltered = filter && !filter->filterIncidence( incidence );
00691   Todo *todo = static_cast<Todo *>(incidence);
00692   if ( todo ) {
00693     KOTodoViewItem *todoItem = 0;
00694     if ( mTodoMap.contains( todo ) ) {
00695       todoItem = mTodoMap[todo];
00696     }
00697     switch ( action ) {
00698       case KOGlobals::INCIDENCEADDED:
00699       case KOGlobals::INCIDENCEEDITED:
00700         // If it's already there, edit it, otherwise just add
00701         if ( todoItem ) {
00702           if ( isFiltered ) {
00703             scheduleRemoveTodoItem( todoItem );
00704           } else {
00705             // correctly update changes in relations
00706             Todo*parent = dynamic_cast<Todo*>( todo->relatedTo() );
00707             KOTodoViewItem*parentItem = 0;
00708             if ( parent && mTodoMap.contains(parent) ) {
00709               parentItem = mTodoMap[ parent ];
00710             }
00711             if ( todoItem->parent() != parentItem ) {
00712               // The relations changed
00713               if ( parentItem ) {
00714                 parentItem->insertItem( todoItem );
00715               } else {
00716                 mTodoListView->insertItem( todoItem );
00717               }
00718             }
00719             todoItem->construct();
00720           }
00721         } else {
00722           if ( !isFiltered ) {
00723             insertTodoItem( todo );
00724           }
00725         }
00726         mTodoListView->sort();
00727         break;
00728       case KOGlobals::INCIDENCEDELETED:
00729         if ( todoItem ) {
00730           scheduleRemoveTodoItem( todoItem );
00731         }
00732         break;
00733       default:
00734         QTimer::singleShot( 0, this, SLOT( updateView() ) );
00735     }
00736   } else {
00737     // use a QTimer here, because when marking todos finished using
00738     // the checkbox, this slot gets called, but we cannot update the views
00739     // because we're still inside KOTodoViewItem::stateChange
00740     QTimer::singleShot(0,this,SLOT(updateView()));
00741   }
00742 }
00743 
00744 void KOTodoView::showDates(const QDate &, const QDate &)
00745 {
00746 }
00747 
00748 void KOTodoView::showIncidences( const Incidence::List &, const QDate & )
00749 {
00750   kdDebug(5850) << "KOTodoView::showIncidences( const Incidence::List & ): not yet implemented" << endl;
00751 }
00752 
00753 CalPrinterBase::PrintType KOTodoView::printType()
00754 {
00755   if ( mTodoListView->selectedItem() ) {
00756     return CalPrinterBase::Incidence;
00757   } else {
00758     return CalPrinterBase::Todolist;
00759   }
00760 }
00761 
00762 void KOTodoView::editItem( QListViewItem *item )
00763 {
00764   if ( item ) {
00765     emit editIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), QDate () );
00766   }
00767 }
00768 
00769 void KOTodoView::editItem( QListViewItem *item, const QPoint &, int )
00770 {
00771   editItem( item );
00772 }
00773 
00774 void KOTodoView::showItem( QListViewItem *item )
00775 {
00776   if ( item ) {
00777     emit showIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), QDate() );
00778   }
00779 }
00780 
00781 void KOTodoView::showItem( QListViewItem *item, const QPoint &, int )
00782 {
00783   showItem( item );
00784 }
00785 
00786 void KOTodoView::popupMenu( QListViewItem *item, const QPoint &, int column )
00787 {
00788   mActiveItem = static_cast<KOTodoViewItem *>( item );
00789   if ( mActiveItem && mActiveItem->todo() &&
00790        !mActiveItem->todo()->isReadOnly() ) {
00791     bool editable = !mActiveItem->todo()->isReadOnly();
00792     mItemPopupMenu->setItemEnabled( ePopupEdit, editable );
00793     mItemPopupMenu->setItemEnabled( ePopupDelete, editable );
00794     mItemPopupMenu->setItemEnabled( ePopupMoveTo, editable );
00795     mItemPopupMenu->setItemEnabled( ePopupCopyTo, editable );
00796     mItemPopupMenu->setItemEnabled( ePopupUnSubTodo, editable );
00797     mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo, editable );
00798 
00799     if ( editable ) {
00800       QDate date = mActiveItem->todo()->dtDue().date();
00801       if ( mActiveItem->todo()->hasDueDate () ) {
00802         mMovePopupMenu->datePicker()->setDate( date );
00803       } else {
00804         mMovePopupMenu->datePicker()->setDate( QDate::currentDate() );
00805       }
00806       switch ( column ) {
00807         case ePriorityColumn:
00808           mPriorityPopupMenu->popup( QCursor::pos() );
00809           break;
00810         case ePercentColumn: {
00811           mPercentageCompletedPopupMenu->popup( QCursor::pos() );
00812           break;
00813         }
00814         case eDueDateColumn:
00815           mMovePopupMenu->popup( QCursor::pos() );
00816           break;
00817         case eCategoriesColumn:
00818           getCategoryPopupMenu( mActiveItem )->popup( QCursor::pos() );
00819           break;
00820         default:
00821           mCopyPopupMenu->datePicker()->setDate( date );
00822           mCopyPopupMenu->datePicker()->setDate( QDate::currentDate() );
00823           mItemPopupMenu->setItemEnabled( ePopupUnSubTodo,
00824                                           mActiveItem->todo()->relatedTo() );
00825           mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo,
00826                                           !mActiveItem->todo()->relations().isEmpty() );
00827           mItemPopupMenu->popup( QCursor::pos() );
00828       }
00829     } else {
00830       mItemPopupMenu->popup( QCursor::pos() );
00831     }
00832   } else mPopupMenu->popup( QCursor::pos() );
00833 }
00834 
00835 void KOTodoView::newTodo()
00836 {
00837   kdDebug() << k_funcinfo << endl;
00838   emit newTodoSignal( 0/*ResourceCalendar*/, QString()/*subResource*/,
00839                       QDate::currentDate().addDays(7) );
00840 }
00841 
00842 void KOTodoView::newSubTodo()
00843 {
00844   if (mActiveItem) {
00845     emit newSubTodoSignal(mActiveItem->todo());
00846   }
00847 }
00848 
00849 void KOTodoView::editTodo()
00850 {
00851   editItem( mActiveItem );
00852 }
00853 
00854 void KOTodoView::showTodo()
00855 {
00856   showItem( mActiveItem );
00857 }
00858 
00859 void KOTodoView::printTodo()
00860 {
00861 #ifndef KORG_NOPRINTER
00862   KOCoreHelper helper;
00863   CalPrinter printer( this, BaseView::calendar(), &helper );
00864   connect( this, SIGNAL(configChanged()), &printer, SLOT(updateConfig()) );
00865 
00866   Incidence::List selectedIncidences;
00867   selectedIncidences.append( mActiveItem->todo() );
00868 
00869   printer.print( KOrg::CalPrinterBase::Incidence,
00870                  QDate(), QDate(), selectedIncidences );
00871 #endif
00872 }
00873 
00874 void KOTodoView::deleteTodo()
00875 {
00876   if (mActiveItem) {
00877     emit deleteIncidenceSignal( mActiveItem->todo() );
00878   }
00879 }
00880 
00881 void KOTodoView::setNewPriority(int index)
00882 {
00883   if ( !mActiveItem || !mChanger ) return;
00884   Todo *todo = mActiveItem->todo();
00885   if ( !todo->isReadOnly () &&
00886        mChanger->beginChange( todo, 0, QString() ) ) {
00887     Todo *oldTodo = todo->clone();
00888     todo->setPriority(mPriority[index]);
00889     mActiveItem->construct();
00890 
00891     mChanger->changeIncidence( oldTodo, todo, KOGlobals::PRIORITY_MODIFIED, this );
00892     mChanger->endChange( todo, 0, QString() );
00893     delete oldTodo;
00894   }
00895 }
00896 
00897 void KOTodoView::setNewPercentage( KOTodoViewItem *item, int percentage )
00898 {
00899   kdDebug(5850) << "KOTodoView::setNewPercentage( " << percentage << "), item = " << item << endl;
00900   if ( !item || !mChanger  ) return;
00901   Todo *todo = item->todo();
00902   if ( !todo ) return;
00903 
00904   if ( !todo->isReadOnly () &&
00905        mChanger->beginChange( todo, 0, QString() ) ) {
00906     Todo *oldTodo = todo->clone();
00907 
00908 /*  Old code to make sub-items's percentage related to this one's:
00909     QListViewItem *myChild = firstChild();
00910     KOTodoViewItem *item;
00911     while( myChild ) {
00912       item = static_cast<KOTodoViewItem*>(myChild);
00913       item->stateChange(state);
00914       myChild = myChild->nextSibling();
00915     }*/
00916     if ( percentage == 100 ) {
00917       todo->setCompleted( QDateTime::currentDateTime() );
00918       // If the todo does recur, it doesn't get set as completed. However, the
00919       // item is still checked. Uncheck it again.
00920       if ( !todo->isCompleted() ) item->setState( QCheckListItem::Off );
00921       else todo->setPercentComplete( percentage );
00922     } else {
00923       todo->setCompleted( false );
00924       todo->setPercentComplete( percentage );
00925     }
00926     item->construct();
00927     if ( todo->doesRecur() && percentage == 100 )
00928       mChanger->changeIncidence( oldTodo, todo,
00929                                  KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE, this );
00930     else
00931       mChanger->changeIncidence( oldTodo, todo,
00932                                  KOGlobals::COMPLETION_MODIFIED, this );
00933     mChanger->endChange( todo, 0, QString() );
00934     delete oldTodo;
00935   } else {
00936     item->construct();
00937     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
00938   }
00939 }
00940 
00941 void KOTodoView::setNewPercentage( int index )
00942 {
00943   setNewPercentage( mActiveItem, mPercentage[index] );
00944 }
00945 
00946 void KOTodoView::setNewDate( QDate date )
00947 {
00948   if ( !mActiveItem || !mChanger ) return;
00949   Todo *todo = mActiveItem->todo();
00950   if ( !todo ) return;
00951 
00952   if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, QString() ) ) {
00953     Todo *oldTodo = todo->clone();
00954 
00955     QDateTime dt;
00956     dt.setDate( date );
00957 
00958     if ( !todo->doesFloat() )
00959       dt.setTime( todo->dtDue().time() );
00960 
00961     if ( date.isNull() )
00962       todo->setHasDueDate( false );
00963     else if ( !todo->hasDueDate() )
00964       todo->setHasDueDate( true );
00965     todo->setDtDue( dt );
00966 
00967     mActiveItem->construct();
00968     mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this );
00969     mChanger->endChange( todo, 0, QString() );
00970     delete oldTodo;
00971   } else {
00972     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
00973   }
00974 }
00975 
00976 void KOTodoView::copyTodoToDate( QDate date )
00977 {
00978   QDateTime dt( date );
00979 
00980   if ( mActiveItem && mChanger ) {
00981     Todo *newTodo = mActiveItem->todo()->clone();
00982     newTodo->recreate();
00983 
00984    newTodo->setHasDueDate( !date.isNull() );
00985    newTodo->setDtDue( dt );
00986    newTodo->setPercentComplete( 0 );
00987 
00988    // avoid forking
00989    if ( newTodo->doesRecur() )
00990      newTodo->recurrence()->unsetRecurs();
00991 
00992    QPair<ResourceCalendar *, QString>p =
00993      CalHelper::incSubResourceCalendar( calendar(), mActiveItem->todo() );
00994 
00995    mChanger->addIncidence( newTodo, p.first, p.second, this );
00996  }
00997 }
00998 
00999 QPopupMenu *KOTodoView::getCategoryPopupMenu( KOTodoViewItem *todoItem )
01000 {
01001   QPopupMenu *tempMenu = new QPopupMenu( this );
01002   QStringList checkedCategories = todoItem->todo()->categories();
01003 
01004   tempMenu->setCheckable( true );
01005   QStringList::Iterator it;
01006   for ( it = KOPrefs::instance()->mCustomCategories.begin();
01007         it != KOPrefs::instance()->mCustomCategories.end();
01008         ++it ) {
01009     int index = tempMenu->insertItem( *it );
01010     mCategory[ index ] = *it;
01011     if ( checkedCategories.find( *it ) != checkedCategories.end() )
01012       tempMenu->setItemChecked( index, true );
01013   }
01014 
01015   connect ( tempMenu, SIGNAL( activated( int ) ),
01016             SLOT( changedCategories( int ) ) );
01017   return tempMenu;
01018 }
01019 
01020 void KOTodoView::changedCategories(int index)
01021 {
01022   if ( !mActiveItem || !mChanger ) return;
01023   Todo *todo = mActiveItem->todo();
01024   if ( !todo ) return;
01025 
01026   if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, QString() ) ) {
01027     Todo *oldTodo = todo->clone();
01028 
01029     QStringList categories = todo->categories ();
01030     if ( categories.find( mCategory[index] ) != categories.end() )
01031       categories.remove( mCategory[index] );
01032     else
01033       categories.insert( categories.end(), mCategory[index] );
01034     categories.sort();
01035     todo->setCategories( categories );
01036     mActiveItem->construct();
01037     mChanger->changeIncidence( oldTodo, todo, KOGlobals::CATEGORY_MODIFIED, this );
01038     mChanger->endChange( todo, 0, QString() );
01039     delete oldTodo;
01040   } else {
01041     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
01042   }
01043 }
01044 
01045 void KOTodoView::setDocumentId( const QString &id )
01046 {
01047   kdDebug(5850) << "KOTodoView::setDocumentId()" << endl;
01048 
01049   mDocPrefs->setDoc( id );
01050 }
01051 
01052 void KOTodoView::itemStateChanged( QListViewItem *item )
01053 {
01054   if (!item) return;
01055 
01056   KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
01057 
01058 //  kdDebug(5850) << "KOTodoView::itemStateChanged(): " << todoItem->todo()->summary() << endl;
01059 
01060   if( mDocPrefs ) mDocPrefs->writeEntry( todoItem->todo()->uid(), todoItem->isOpen() );
01061 }
01062 
01063 void KOTodoView::setNewPercentageDelayed( KOTodoViewItem *item, int percentage )
01064 {
01065   mPercentChangedMap.append( qMakePair( item, percentage ) );
01066 
01067   QTimer::singleShot( 0, this, SLOT( processDelayedNewPercentage() ) );
01068 }
01069 
01070 void KOTodoView::processDelayedNewPercentage()
01071 {
01072   QValueList< QPair< KOTodoViewItem *, int> >::Iterator it;
01073   for ( it = mPercentChangedMap.begin(); it != mPercentChangedMap.end(); ++it )
01074     setNewPercentage( (*it).first, (*it).second );
01075 
01076   mPercentChangedMap.clear();
01077 }
01078 
01079 void KOTodoView::saveLayout(KConfig *config, const QString &group) const
01080 {
01081   mTodoListView->saveLayout(config,group);
01082 }
01083 
01084 void KOTodoView::restoreLayout(KConfig *config, const QString &group)
01085 {
01086   mTodoListView->restoreLayout(config,group);
01087 }
01088 
01089 void KOTodoView::processSelectionChange()
01090 {
01091 //  kdDebug(5850) << "KOTodoView::processSelectionChange()" << endl;
01092 
01093   KOTodoViewItem *item =
01094     static_cast<KOTodoViewItem *>( mTodoListView->selectedItem() );
01095 
01096   if ( !item ) {
01097     emit incidenceSelected( 0, QDate() );
01098   } else {
01099     if ( selectedIncidenceDates().isEmpty() ) {
01100       emit incidenceSelected( item->todo(), QDate() );
01101     } else {
01102       emit incidenceSelected( item->todo(), selectedIncidenceDates().first() );
01103     }
01104   }
01105 }
01106 
01107 void KOTodoView::clearSelection()
01108 {
01109   mTodoListView->selectAll( false );
01110 }
01111 
01112 void KOTodoView::purgeCompleted()
01113 {
01114   emit purgeCompletedSignal();
01115 }
01116 
01117 void KOTodoView::addQuickTodo()
01118 {
01119   if ( ! mQuickAdd->text().stripWhiteSpace().isEmpty() ) {
01120     Todo *todo = new Todo();
01121     todo->setSummary( mQuickAdd->text() );
01122     todo->setOrganizer( Person( KOPrefs::instance()->fullName(),
01123                         KOPrefs::instance()->email() ) );
01124     if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01125       delete todo;
01126       return;
01127     }
01128     mQuickAdd->setText( QString::null );
01129   }
01130 }
01131 
01132 void KOTodoView::setIncidenceChanger( IncidenceChangerBase *changer )
01133 {
01134   mChanger = changer;
01135   mTodoListView->setIncidenceChanger( changer );
01136 }
KDE Home | KDE Accessibility Home | Description of Access Keys