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