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