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   /* Create a Tooltip */
00110   tooltip = new KOTodoListViewToolTip( viewport(), mCalendar, this );
00111 }
00112 
00113 KOTodoListView::~KOTodoListView()
00114 {
00115   delete tooltip;
00116 }
00117 
00118 void KOTodoListView::setCalendar( Calendar *cal )
00119 {
00120   mCalendar = cal;
00121   setAcceptDrops( mCalendar );
00122   viewport()->setAcceptDrops( mCalendar );
00123 }
00124 
00125 bool KOTodoListView::event(QEvent *e)
00126 {
00127   int tmp=0;
00128   KOTodoViewItem *i;
00129 
00130   /* Checks for an ApplicationPaletteChange event and updates
00131    * the small Progress bars to make therm have the right colors. */
00132   if(e->type()==QEvent::ApplicationPaletteChange)
00133   {
00134 
00135     KListView::event(e);
00136     i=(KOTodoViewItem *)itemAtIndex(tmp);
00137 
00138     while(i!=0)
00139     {
00140       i->construct();
00141       tmp++;
00142       i=(KOTodoViewItem *)itemAtIndex(tmp);
00143     }
00144 
00145   }
00146 
00147   return (KListView::event(e) || e->type()==QEvent::ApplicationPaletteChange);
00148 }
00149 
00150 void KOTodoListView::contentsDragEnterEvent(QDragEnterEvent *e)
00151 {
00152 #ifndef KORG_NODND
00153 //  kdDebug(5850) << "KOTodoListView::contentsDragEnterEvent" << endl;
00154   if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00155        !QTextDrag::canDecode( e ) ) {
00156     e->ignore();
00157     return;
00158   }
00159 
00160   mOldCurrent = currentItem();
00161 #endif
00162 }
00163 
00164 void KOTodoListView::contentsDragMoveEvent(QDragMoveEvent *e)
00165 {
00166 #ifndef KORG_NODND
00167 //  kdDebug(5850) << "KOTodoListView::contentsDragMoveEvent" << endl;
00168 
00169   if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00170        !QTextDrag::canDecode( e ) ) {
00171     e->ignore();
00172     return;
00173   }
00174 
00175   e->accept();
00176 #endif
00177 }
00178 
00179 void KOTodoListView::contentsDragLeaveEvent( QDragLeaveEvent * )
00180 {
00181 #ifndef KORG_NODND
00182 //  kdDebug(5850) << "KOTodoListView::contentsDragLeaveEvent" << endl;
00183 
00184   setCurrentItem(mOldCurrent);
00185   setSelected(mOldCurrent,true);
00186 #endif
00187 }
00188 
00189 void KOTodoListView::contentsDropEvent( QDropEvent *e )
00190 {
00191 #ifndef KORG_NODND
00192   kdDebug(5850) << "KOTodoListView::contentsDropEvent" << endl;
00193 
00194   if ( !mCalendar || !mChanger ||
00195        ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) &&
00196          !QTextDrag::canDecode( e ) ) ) {
00197     e->ignore();
00198     return;
00199   }
00200 
00201   DndFactory factory( mCalendar );
00202   Todo *todo = factory.createDropTodo(e);
00203 
00204   if ( todo ) {
00205     e->acceptAction();
00206 
00207     KOTodoViewItem *destination =
00208         (KOTodoViewItem *)itemAt(contentsToViewport(e->pos()));
00209     Todo *destinationEvent = 0;
00210     if (destination) destinationEvent = destination->todo();
00211 
00212     Todo *existingTodo = mCalendar->todo(todo->uid());
00213 
00214     if( existingTodo ) {
00215        kdDebug(5850) << "Drop existing Todo " << existingTodo << " onto " << destinationEvent << endl;
00216       Incidence *to = destinationEvent;
00217       while(to) {
00218         if (to->uid() == todo->uid()) {
00219           KMessageBox::information(this,
00220               i18n("Cannot move to-do to itself or a child of itself."),
00221               i18n("Drop To-do"), "NoDropTodoOntoItself" );
00222           delete todo;
00223           return;
00224         }
00225         to = to->relatedTo();
00226       }
00227 
00228       Todo*oldTodo = existingTodo->clone();
00229       if ( mChanger->beginChange( existingTodo, 0, QString() ) ) {
00230         existingTodo->setRelatedTo( destinationEvent );
00231         mChanger->changeIncidence( oldTodo, existingTodo, KOGlobals::RELATION_MODIFIED, this );
00232         mChanger->endChange( existingTodo, 0, QString() );
00233       } else {
00234         KMessageBox::sorry( this, i18n("Unable to change to-do's parent, "
00235                             "because the to-do cannot be locked.") );
00236       }
00237       delete oldTodo;
00238       delete todo;
00239     } else {
00240 //      kdDebug(5850) << "Drop new Todo" << endl;
00241       todo->setRelatedTo(destinationEvent);
00242       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
00243         KODialogManager::errorSaveIncidence( this, todo );
00244         delete todo;
00245         return;
00246       }
00247     }
00248   } else {
00249     QString text;
00250     KOTodoViewItem *todoi = dynamic_cast<KOTodoViewItem *>(itemAt( contentsToViewport(e->pos()) ));
00251     if ( ! todoi ) {
00252       // Not dropped on a todo item:
00253       e->ignore();
00254       kdDebug( 5850 ) << "KOTodoListView::contentsDropEvent(): Not dropped on a todo item" << endl;
00255       kdDebug( 5850 ) << "TODO: Create a new todo with the given data" << endl;
00256       // FIXME: Create a new todo with the given text/contact/whatever
00257     } else if ( QTextDrag::decode(e, text) ) {
00258       //QListViewItem *qlvi = itemAt( contentsToViewport(e->pos()) );
00259       kdDebug(5850) << "Dropped : " << text << endl;
00260       Todo*todo = todoi->todo();
00261       if( mChanger->beginChange( todo, 0, QString() ) ) {
00262         Todo*oldtodo = todo->clone();
00263 
00264         if( text.startsWith( "file:" ) ) {
00265           todo->addAttachment( new Attachment( text ) );
00266         } else {
00267           QStringList emails = KPIM::splitEmailAddrList( text );
00268           for(QStringList::ConstIterator it = emails.begin();it!=emails.end();++it) {
00269             kdDebug(5850) << " Email: " << (*it) << endl;
00270             int pos = (*it).find("<");
00271             QString name = (*it).left(pos);
00272             QString email = (*it).mid(pos);
00273             if (!email.isEmpty() && todoi) {
00274               todo->addAttendee( new Attendee( name, email ) );
00275             }
00276           }
00277         }
00278         //FIXME: attendees or attachment added, so there is something modified
00279         mChanger->changeIncidence( oldtodo, todo, KOGlobals::NOTHING_MODIFIED, this );
00280         mChanger->endChange( todo, 0, QString() );
00281       } else {
00282         KMessageBox::sorry( this, i18n("Unable to add attendees to the to-do, "
00283             "because the to-do cannot be locked.") );
00284       }
00285     }
00286     else {
00287       kdDebug(5850) << "KOTodoListView::contentsDropEvent(): Todo from drop not decodable" << endl;
00288       e->ignore();
00289     }
00290   }
00291 #endif
00292 }
00293 
00294 void KOTodoListView::contentsMousePressEvent(QMouseEvent* e)
00295 {
00296   QListView::contentsMousePressEvent(e);
00297   QPoint p(contentsToViewport(e->pos()));
00298   QListViewItem *i = itemAt(p);
00299   if (i) {
00300     // if the user clicked into the root decoration of the item, don't
00301     // try to start a drag!
00302     if (p.x() > header()->sectionPos(header()->mapToIndex(0)) +
00303         treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) +
00304         itemMargin() ||
00305         p.x() < header()->sectionPos(header()->mapToIndex(0))) {
00306       if (e->button()==Qt::LeftButton) {
00307         mPressPos = e->pos();
00308         mMousePressed = true;
00309       }
00310     }
00311   }
00312 }
00313 
00314 void KOTodoListView::contentsMouseMoveEvent(QMouseEvent* e)
00315 {
00316 #ifndef KORG_NODND
00317 //  kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent()" << endl;
00318   QListView::contentsMouseMoveEvent(e);
00319   if (mMousePressed && (mPressPos - e->pos()).manhattanLength() >
00320       QApplication::startDragDistance()) {
00321     mMousePressed = false;
00322     QListViewItem *item = itemAt(contentsToViewport(mPressPos));
00323     if ( item && mCalendar ) {
00324 //      kdDebug(5850) << "Start Drag for item " << item->text(0) << endl;
00325       DndFactory factory( mCalendar );
00326       ICalDrag *vd = factory.createDrag(
00327                           ((KOTodoViewItem *)item)->todo(),viewport());
00328       if (vd->drag()) {
00329         kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent(): Delete drag source" << endl;
00330       }
00331 /*
00332       QString source = fullPath(item);
00333       if ( QFile::exists(source) ) {
00334         KURL url;
00335         url.setPath(source);
00336         KURLDrag* ud = KURLDrag::newDrag(KURL::List(url), viewport());
00337         if ( ud->drag() )
00338           QMessageBox::information( this, "Drag source",
00339                                     QString("Delete ")+source, "Not implemented" );
00340 */
00341     }
00342   }
00343 #endif
00344 }
00345 
00346 void KOTodoListView::contentsMouseReleaseEvent(QMouseEvent *e)
00347 {
00348   QListView::contentsMouseReleaseEvent(e);
00349   mMousePressed = false;
00350 }
00351 
00352 void KOTodoListView::contentsMouseDoubleClickEvent(QMouseEvent *e)
00353 {
00354   if (!e) return;
00355 
00356   QPoint vp = contentsToViewport(e->pos());
00357 
00358   QListViewItem *item = itemAt(vp);
00359 
00360   if (!item) return;
00361 
00362   emit doubleClicked(item,vp,0);
00363 }
00364 
00366 
00367 KOTodoView::KOTodoView( Calendar *calendar, QWidget *parent, const char* name)
00368   : KOrg::BaseView( calendar, parent, name )
00369 {
00370   QBoxLayout *topLayout = new QVBoxLayout( this );
00371 
00372   QLabel *title = new QLabel( i18n("To-dos:"), this );
00373   title->setFrameStyle( QFrame::Panel | QFrame::Raised );
00374   topLayout->addWidget( title );
00375 
00376   mQuickAdd = new KPIM::ClickLineEdit( this, i18n( "Click to add a new to-do" ) );
00377   mQuickAdd->setAcceptDrops( false );
00378   topLayout->addWidget( mQuickAdd );
00379 
00380   if ( !KOPrefs::instance()->mEnableQuickTodo ) mQuickAdd->hide();
00381 
00382   mTodoListView = new KOTodoListView( this );
00383   topLayout->addWidget( mTodoListView );
00384 
00385   mTodoListView->setRootIsDecorated( true );
00386   mTodoListView->setAllColumnsShowFocus( true );
00387 
00388   mTodoListView->setShowSortIndicator( true );
00389 
00390   mTodoListView->addColumn( i18n("Summary") );
00391   mTodoListView->addColumn( i18n("Recurs") );
00392   mTodoListView->addColumn( i18n("Priority") );
00393   mTodoListView->setColumnAlignment( ePriorityColumn, AlignHCenter );
00394   mTodoListView->addColumn( i18n("Complete") );
00395   mTodoListView->setColumnAlignment( ePercentColumn, AlignRight );
00396   mTodoListView->addColumn( i18n("Due Date/Time") );
00397   mTodoListView->setColumnAlignment( eDueDateColumn, AlignLeft );
00398   mTodoListView->addColumn( i18n("Categories") );
00399 #if 0
00400   mTodoListView->addColumn( i18n("Sort Id") );
00401   mTodoListView->setColumnAlignment( 4, AlignHCenter );
00402 #endif
00403 
00404   mTodoListView->setMinimumHeight( 60 );
00405   mTodoListView->setItemsRenameable( true );
00406   mTodoListView->setRenameable( 0 );
00407 
00408   mTodoListView->setColumnWidthMode( eSummaryColumn, QListView::Manual );
00409   mTodoListView->setColumnWidthMode( eRecurColumn, QListView::Manual );
00410   mTodoListView->setColumnWidthMode( ePriorityColumn, QListView::Manual );
00411   mTodoListView->setColumnWidthMode( ePercentColumn, QListView::Manual );
00412   mTodoListView->setColumnWidthMode( eDueDateColumn, QListView::Manual );
00413   mTodoListView->setColumnWidthMode( eCategoriesColumn, QListView::Manual );
00414 #if 0
00415   mTodoListView->setColumnWidthMode( eDescriptionColumn, QListView::Manual );
00416 #endif
00417 
00418   mPriorityPopupMenu = new QPopupMenu( this );
00419   mPriority[ mPriorityPopupMenu->insertItem( i18n("Unspecified priority", "unspecified") ) ] = 0;
00420   mPriority[ mPriorityPopupMenu->insertItem( i18n( "1 (highest)") ) ] = 1;
00421   mPriority[ mPriorityPopupMenu->insertItem( i18n( "2" ) ) ] = 2;
00422   mPriority[ mPriorityPopupMenu->insertItem( i18n( "3" ) ) ] = 3;
00423   mPriority[ mPriorityPopupMenu->insertItem( i18n( "4" ) ) ] = 4;
00424   mPriority[ mPriorityPopupMenu->insertItem( i18n( "5 (medium)" ) ) ] = 5;
00425   mPriority[ mPriorityPopupMenu->insertItem( i18n( "6" ) ) ] = 6;
00426   mPriority[ mPriorityPopupMenu->insertItem( i18n( "7" ) ) ] = 7;
00427   mPriority[ mPriorityPopupMenu->insertItem( i18n( "8" ) ) ] = 8;
00428   mPriority[ mPriorityPopupMenu->insertItem( i18n( "9 (lowest)" ) ) ] = 9;
00429   connect( mPriorityPopupMenu, SIGNAL( activated( int ) ),
00430            SLOT( setNewPriority( int ) ));
00431 
00432   mPercentageCompletedPopupMenu = new QPopupMenu(this);
00433   for (int i = 0; i <= 100; i+=10) {
00434     QString label = QString ("%1 %").arg (i);
00435     mPercentage[mPercentageCompletedPopupMenu->insertItem (label)] = i;
00436   }
00437   connect( mPercentageCompletedPopupMenu, SIGNAL( activated( int ) ),
00438            SLOT( setNewPercentage( int ) ) );
00439 
00440   mMovePopupMenu = new KDatePickerPopup(
00441                              KDatePickerPopup::NoDate |
00442                              KDatePickerPopup::DatePicker |
00443                              KDatePickerPopup::Words );
00444   mCopyPopupMenu = new KDatePickerPopup(
00445                              KDatePickerPopup::NoDate |
00446                              KDatePickerPopup::DatePicker |
00447                              KDatePickerPopup::Words );
00448 
00449 
00450   connect( mMovePopupMenu, SIGNAL( dateChanged( QDate )),
00451            SLOT( setNewDate( QDate ) ) );
00452   connect( mCopyPopupMenu, SIGNAL( dateChanged( QDate )),
00453            SLOT( copyTodoToDate( QDate ) ) );
00454 
00455   mItemPopupMenu = new QPopupMenu(this);
00456   mItemPopupMenu->insertItem(i18n("&Show"), this,
00457                              SLOT (showTodo()));
00458   mItemPopupMenu->insertItem(i18n("&Edit..."), this,
00459                              SLOT (editTodo()), 0, ePopupEdit );
00460 #ifndef KORG_NOPRINTER
00461   mItemPopupMenu->insertItem(KOGlobals::self()->smallIcon("printer1"), i18n("&Print..."), this, SLOT( printTodo() ) );
00462 #endif
00463   mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("editdelete"), i18n("&Delete"), this,
00464                              SLOT (deleteTodo()), 0, ePopupDelete );
00465   mItemPopupMenu->insertSeparator();
00466   mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("New &To-do..."), this,
00467                              SLOT (newTodo()) );
00468   mItemPopupMenu->insertItem(i18n("New Su&b-to-do..."), this,
00469                              SLOT (newSubTodo()));
00470   mItemPopupMenu->insertItem( i18n("&Make this To-do Independent"), this,
00471       SIGNAL( unSubTodoSignal() ), 0, ePopupUnSubTodo );
00472   mItemPopupMenu->insertItem( i18n("Make all Sub-to-dos &Independent"), this,
00473       SIGNAL( unAllSubTodoSignal() ), 0, ePopupUnAllSubTodo );
00474   mItemPopupMenu->insertSeparator();
00475   mItemPopupMenu->insertItem( i18n("&Copy To"), mCopyPopupMenu, ePopupCopyTo );
00476   mItemPopupMenu->insertItem(i18n("&Move To"), mMovePopupMenu, ePopupMoveTo );
00477   mItemPopupMenu->insertSeparator();
00478   mItemPopupMenu->insertItem(i18n("delete completed to-dos","Pur&ge Completed"),
00479                              this, SLOT( purgeCompleted() ) );
00480 
00481   connect( mMovePopupMenu, SIGNAL( dateChanged( QDate ) ),
00482            mItemPopupMenu, SLOT( hide() ) );
00483   connect( mCopyPopupMenu, SIGNAL( dateChanged( QDate ) ),
00484            mItemPopupMenu, SLOT( hide() ) );
00485 
00486   mPopupMenu = new QPopupMenu(this);
00487   mPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("&New To-do..."), this,
00488                          SLOT(newTodo()) );
00489   mPopupMenu->insertItem(i18n("delete completed to-dos","&Purge Completed"),
00490                          this, SLOT(purgeCompleted()));
00491 
00492   mDocPrefs = new DocPrefs( name );
00493 
00494   // Double clicking conflicts with opening/closing the subtree
00495   connect( mTodoListView, SIGNAL( doubleClicked( QListViewItem *,
00496                                                  const QPoint &, int ) ),
00497            SLOT( editItem( QListViewItem *, const QPoint &, int ) ) );
00498   connect( mTodoListView, SIGNAL( returnPressed( QListViewItem * ) ),
00499            SLOT( editItem( QListViewItem * ) ) );
00500   connect( mTodoListView, SIGNAL( contextMenuRequested( QListViewItem *,
00501                                                         const QPoint &, int ) ),
00502            SLOT( popupMenu( QListViewItem *, const QPoint &, int ) ) );
00503   connect( mTodoListView, SIGNAL( expanded( QListViewItem * ) ),
00504            SLOT( itemStateChanged( QListViewItem * ) ) );
00505   connect( mTodoListView, SIGNAL( collapsed( QListViewItem * ) ),
00506            SLOT( itemStateChanged( QListViewItem * ) ) );
00507 
00508 #if 0
00509   connect(mTodoListView,SIGNAL(selectionChanged(QListViewItem *)),
00510           SLOT(selectionChanged(QListViewItem *)));
00511   connect(mTodoListView,SIGNAL(clicked(QListViewItem *)),
00512           SLOT(selectionChanged(QListViewItem *)));
00513   connect(mTodoListView,SIGNAL(pressed(QListViewItem *)),
00514           SLOT(selectionChanged(QListViewItem *)));
00515 #endif
00516   connect( mTodoListView, SIGNAL(selectionChanged() ),
00517            SLOT( processSelectionChange() ) );
00518   connect( mQuickAdd, SIGNAL( returnPressed () ),
00519            SLOT( addQuickTodo() ) );
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() );
00766 }
00767 
00768 void KOTodoView::editItem( QListViewItem *item, const QPoint &, int )
00769 {
00770   editItem( item );
00771 }
00772 
00773 void KOTodoView::showItem( QListViewItem *item )
00774 {
00775   if (item)
00776     emit showIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo() );
00777 }
00778 
00779 void KOTodoView::showItem( QListViewItem *item, const QPoint &, int )
00780 {
00781   showItem( item );
00782 }
00783 
00784 void KOTodoView::popupMenu( QListViewItem *item, const QPoint &, int column )
00785 {
00786   mActiveItem = static_cast<KOTodoViewItem *>( item );
00787   if ( mActiveItem && mActiveItem->todo() &&
00788        !mActiveItem->todo()->isReadOnly() ) {
00789     bool editable = !mActiveItem->todo()->isReadOnly();
00790     mItemPopupMenu->setItemEnabled( ePopupEdit, editable );
00791     mItemPopupMenu->setItemEnabled( ePopupDelete, editable );
00792     mItemPopupMenu->setItemEnabled( ePopupMoveTo, editable );
00793     mItemPopupMenu->setItemEnabled( ePopupCopyTo, editable );
00794     mItemPopupMenu->setItemEnabled( ePopupUnSubTodo, editable );
00795     mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo, editable );
00796 
00797     if ( editable ) {
00798       QDate date = mActiveItem->todo()->dtDue().date();
00799       if ( mActiveItem->todo()->hasDueDate () ) {
00800         mMovePopupMenu->datePicker()->setDate( date );
00801       } else {
00802         mMovePopupMenu->datePicker()->setDate( QDate::currentDate() );
00803       }
00804       switch ( column ) {
00805         case ePriorityColumn:
00806           mPriorityPopupMenu->popup( QCursor::pos() );
00807           break;
00808         case ePercentColumn: {
00809           mPercentageCompletedPopupMenu->popup( QCursor::pos() );
00810           break;
00811         }
00812         case eDueDateColumn:
00813           mMovePopupMenu->popup( QCursor::pos() );
00814           break;
00815         case eCategoriesColumn:
00816           getCategoryPopupMenu( mActiveItem )->popup( QCursor::pos() );
00817           break;
00818         default:
00819           mCopyPopupMenu->datePicker()->setDate( date );
00820           mCopyPopupMenu->datePicker()->setDate( QDate::currentDate() );
00821           mItemPopupMenu->setItemEnabled( ePopupUnSubTodo,
00822                                           mActiveItem->todo()->relatedTo() );
00823           mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo,
00824                                           !mActiveItem->todo()->relations().isEmpty() );
00825           mItemPopupMenu->popup( QCursor::pos() );
00826       }
00827     } else {
00828       mItemPopupMenu->popup( QCursor::pos() );
00829     }
00830   } else mPopupMenu->popup( QCursor::pos() );
00831 }
00832 
00833 void KOTodoView::newTodo()
00834 {
00835   kdDebug() << k_funcinfo << endl;
00836   emit newTodoSignal( 0/*ResourceCalendar*/, QString()/*subResource*/,
00837                       QDate::currentDate().addDays(7) );
00838 }
00839 
00840 void KOTodoView::newSubTodo()
00841 {
00842   if (mActiveItem) {
00843     emit newSubTodoSignal(mActiveItem->todo());
00844   }
00845 }
00846 
00847 void KOTodoView::editTodo()
00848 {
00849   editItem( mActiveItem );
00850 }
00851 
00852 void KOTodoView::showTodo()
00853 {
00854   showItem( mActiveItem );
00855 }
00856 
00857 void KOTodoView::printTodo()
00858 {
00859 #ifndef KORG_NOPRINTER
00860   KOCoreHelper helper;
00861   CalPrinter printer( this, BaseView::calendar(), &helper );
00862   connect( this, SIGNAL(configChanged()), &printer, SLOT(updateConfig()) );
00863 
00864   Incidence::List selectedIncidences;
00865   selectedIncidences.append( mActiveItem->todo() );
00866 
00867   printer.print( KOrg::CalPrinterBase::Incidence,
00868                  QDate(), QDate(), selectedIncidences );
00869 #endif
00870 }
00871 
00872 void KOTodoView::deleteTodo()
00873 {
00874   if (mActiveItem) {
00875     emit deleteIncidenceSignal( mActiveItem->todo() );
00876   }
00877 }
00878 
00879 void KOTodoView::setNewPriority(int index)
00880 {
00881   if ( !mActiveItem || !mChanger ) return;
00882   Todo *todo = mActiveItem->todo();
00883   if ( !todo->isReadOnly () &&
00884        mChanger->beginChange( todo, 0, QString() ) ) {
00885     Todo *oldTodo = todo->clone();
00886     todo->setPriority(mPriority[index]);
00887     mActiveItem->construct();
00888 
00889     mChanger->changeIncidence( oldTodo, todo, KOGlobals::PRIORITY_MODIFIED, this );
00890     mChanger->endChange( todo, 0, QString() );
00891     delete oldTodo;
00892   }
00893 }
00894 
00895 void KOTodoView::setNewPercentage( KOTodoViewItem *item, int percentage )
00896 {
00897   kdDebug(5850) << "KOTodoView::setNewPercentage( " << percentage << "), item = " << item << endl;
00898   if ( !item || !mChanger  ) return;
00899   Todo *todo = item->todo();
00900   if ( !todo ) return;
00901 
00902   if ( !todo->isReadOnly () &&
00903        mChanger->beginChange( todo, 0, QString() ) ) {
00904     Todo *oldTodo = todo->clone();
00905 
00906 /*  Old code to make sub-items's percentage related to this one's:
00907     QListViewItem *myChild = firstChild();
00908     KOTodoViewItem *item;
00909     while( myChild ) {
00910       item = static_cast<KOTodoViewItem*>(myChild);
00911       item->stateChange(state);
00912       myChild = myChild->nextSibling();
00913     }*/
00914     if ( percentage == 100 ) {
00915       todo->setCompleted( QDateTime::currentDateTime() );
00916       // If the todo does recur, it doesn't get set as completed. However, the
00917       // item is still checked. Uncheck it again.
00918       if ( !todo->isCompleted() ) item->setState( QCheckListItem::Off );
00919       else todo->setPercentComplete( percentage );
00920     } else {
00921       todo->setCompleted( false );
00922       todo->setPercentComplete( percentage );
00923     }
00924     item->construct();
00925     if ( todo->doesRecur() && percentage == 100 )
00926       mChanger->changeIncidence( oldTodo, todo,
00927                                  KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE, this );
00928     else
00929       mChanger->changeIncidence( oldTodo, todo,
00930                                  KOGlobals::COMPLETION_MODIFIED, this );
00931     mChanger->endChange( todo, 0, QString() );
00932     delete oldTodo;
00933   } else {
00934     item->construct();
00935     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
00936   }
00937 }
00938 
00939 void KOTodoView::setNewPercentage( int index )
00940 {
00941   setNewPercentage( mActiveItem, mPercentage[index] );
00942 }
00943 
00944 void KOTodoView::setNewDate( QDate date )
00945 {
00946   if ( !mActiveItem || !mChanger ) return;
00947   Todo *todo = mActiveItem->todo();
00948   if ( !todo ) return;
00949 
00950   if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, QString() ) ) {
00951     Todo *oldTodo = todo->clone();
00952 
00953     QDateTime dt;
00954     dt.setDate( date );
00955 
00956     if ( !todo->doesFloat() )
00957       dt.setTime( todo->dtDue().time() );
00958 
00959     if ( date.isNull() )
00960       todo->setHasDueDate( false );
00961     else if ( !todo->hasDueDate() )
00962       todo->setHasDueDate( true );
00963     todo->setDtDue( dt );
00964 
00965     mActiveItem->construct();
00966     mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this );
00967     mChanger->endChange( todo, 0, QString() );
00968     delete oldTodo;
00969   } else {
00970     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
00971   }
00972 }
00973 
00974 void KOTodoView::copyTodoToDate( QDate date )
00975 {
00976   QDateTime dt( date );
00977 
00978   if ( mActiveItem && mChanger ) {
00979     Todo *newTodo = mActiveItem->todo()->clone();
00980     newTodo->recreate();
00981 
00982    newTodo->setHasDueDate( !date.isNull() );
00983    newTodo->setDtDue( dt );
00984    newTodo->setPercentComplete( 0 );
00985 
00986    // avoid forking
00987    if ( newTodo->doesRecur() )
00988      newTodo->recurrence()->unsetRecurs();
00989 
00990    QPair<ResourceCalendar *, QString>p =
00991      CalHelper::incSubResourceCalendar( calendar(), mActiveItem->todo() );
00992 
00993    mChanger->addIncidence( newTodo, p.first, p.second, this );
00994  }
00995 }
00996 
00997 QPopupMenu *KOTodoView::getCategoryPopupMenu( KOTodoViewItem *todoItem )
00998 {
00999   QPopupMenu *tempMenu = new QPopupMenu( this );
01000   QStringList checkedCategories = todoItem->todo()->categories();
01001 
01002   tempMenu->setCheckable( true );
01003   QStringList::Iterator it;
01004   for ( it = KOPrefs::instance()->mCustomCategories.begin();
01005         it != KOPrefs::instance()->mCustomCategories.end();
01006         ++it ) {
01007     int index = tempMenu->insertItem( *it );
01008     mCategory[ index ] = *it;
01009     if ( checkedCategories.find( *it ) != checkedCategories.end() )
01010       tempMenu->setItemChecked( index, true );
01011   }
01012 
01013   connect ( tempMenu, SIGNAL( activated( int ) ),
01014             SLOT( changedCategories( int ) ) );
01015   return tempMenu;
01016 }
01017 
01018 void KOTodoView::changedCategories(int index)
01019 {
01020   if ( !mActiveItem || !mChanger ) return;
01021   Todo *todo = mActiveItem->todo();
01022   if ( !todo ) return;
01023 
01024   if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, QString() ) ) {
01025     Todo *oldTodo = todo->clone();
01026 
01027     QStringList categories = todo->categories ();
01028     if ( categories.find( mCategory[index] ) != categories.end() )
01029       categories.remove( mCategory[index] );
01030     else
01031       categories.insert( categories.end(), mCategory[index] );
01032     categories.sort();
01033     todo->setCategories( categories );
01034     mActiveItem->construct();
01035     mChanger->changeIncidence( oldTodo, todo, KOGlobals::CATEGORY_MODIFIED, this );
01036     mChanger->endChange( todo, 0, QString() );
01037     delete oldTodo;
01038   } else {
01039     kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl;
01040   }
01041 }
01042 
01043 void KOTodoView::setDocumentId( const QString &id )
01044 {
01045   kdDebug(5850) << "KOTodoView::setDocumentId()" << endl;
01046 
01047   mDocPrefs->setDoc( id );
01048 }
01049 
01050 void KOTodoView::itemStateChanged( QListViewItem *item )
01051 {
01052   if (!item) return;
01053 
01054   KOTodoViewItem *todoItem = (KOTodoViewItem *)item;
01055 
01056 //  kdDebug(5850) << "KOTodoView::itemStateChanged(): " << todoItem->todo()->summary() << endl;
01057 
01058   if( mDocPrefs ) mDocPrefs->writeEntry( todoItem->todo()->uid(), todoItem->isOpen() );
01059 }
01060 
01061 void KOTodoView::setNewPercentageDelayed( KOTodoViewItem *item, int percentage )
01062 {
01063   mPercentChangedMap.append( qMakePair( item, percentage ) );
01064 
01065   QTimer::singleShot( 0, this, SLOT( processDelayedNewPercentage() ) );
01066 }
01067 
01068 void KOTodoView::processDelayedNewPercentage()
01069 {
01070   QValueList< QPair< KOTodoViewItem *, int> >::Iterator it;
01071   for ( it = mPercentChangedMap.begin(); it != mPercentChangedMap.end(); ++it )
01072     setNewPercentage( (*it).first, (*it).second );
01073 
01074   mPercentChangedMap.clear();
01075 }
01076 
01077 void KOTodoView::saveLayout(KConfig *config, const QString &group) const
01078 {
01079   mTodoListView->saveLayout(config,group);
01080 }
01081 
01082 void KOTodoView::restoreLayout(KConfig *config, const QString &group)
01083 {
01084   mTodoListView->restoreLayout(config,group);
01085 }
01086 
01087 void KOTodoView::processSelectionChange()
01088 {
01089 //  kdDebug(5850) << "KOTodoView::processSelectionChange()" << endl;
01090 
01091   KOTodoViewItem *item =
01092     static_cast<KOTodoViewItem *>( mTodoListView->selectedItem() );
01093 
01094   if ( !item ) {
01095     emit incidenceSelected( 0, QDate() );
01096   } else {
01097     if ( selectedDates().isEmpty() ) {
01098       emit incidenceSelected( item->todo(), QDate() );
01099     } else {
01100       emit incidenceSelected( item->todo(), selectedDates().first() );
01101     }
01102   }
01103 }
01104 
01105 void KOTodoView::clearSelection()
01106 {
01107   mTodoListView->selectAll( false );
01108 }
01109 
01110 void KOTodoView::purgeCompleted()
01111 {
01112   emit purgeCompletedSignal();
01113 }
01114 
01115 void KOTodoView::addQuickTodo()
01116 {
01117   if ( ! mQuickAdd->text().stripWhiteSpace().isEmpty() ) {
01118     Todo *todo = new Todo();
01119     todo->setSummary( mQuickAdd->text() );
01120     todo->setOrganizer( Person( KOPrefs::instance()->fullName(),
01121                         KOPrefs::instance()->email() ) );
01122     if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01123       delete todo;
01124       return;
01125     }
01126     mQuickAdd->setText( QString::null );
01127   }
01128 }
01129 
01130 void KOTodoView::setIncidenceChanger( IncidenceChangerBase *changer )
01131 {
01132   mChanger = changer;
01133   mTodoListView->setIncidenceChanger( changer );
01134 }
KDE Home | KDE Accessibility Home | Description of Access Keys