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