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