/* This file is part of KOrganizer. Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org> Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of TQt, and distribute the resulting executable, without including the source code for TQt in the source distribution. */ #include <tqlayout.h> #include <tqheader.h> #include <tqcursor.h> #include <tqlabel.h> #include <tqtimer.h> #include <kdebug.h> #include <tdelocale.h> #include <tdeglobal.h> #include <kiconloader.h> #include <tdemessagebox.h> #include <libkcal/calhelper.h> #include <libkcal/icaldrag.h> #include <libkcal/vcaldrag.h> #include <libkcal/dndfactory.h> #include <libkcal/calendarresources.h> #include <libkcal/resourcecalendar.h> #include <libkcal/calfilter.h> #include <libkcal/incidenceformatter.h> #include <libtdepim/clicklineedit.h> #include <libtdepim/kdatepickerpopup.h> #include <libemailfunctions/email.h> #include "docprefs.h" #include "koincidencetooltip.h" #include "kodialogmanager.h" #include "kotodoview.h" #include "koprefs.h" #include "koglobals.h" using namespace KOrg; #include "kotodoviewitem.h" #include "kotodoview.moc" #ifndef KORG_NOPRINTER #include "kocorehelper.h" #include "calprinter.h" #endif KOTodoListViewToolTip::KOTodoListViewToolTip (TQWidget *parent, Calendar *calendar, KOTodoListView *lv ) :TQToolTip(parent), mCalendar( calendar ) { todolist=lv; } void KOTodoListViewToolTip::maybeTip( const TQPoint & pos) { TQRect r; int headerPos; int col=todolist->header()->sectionAt(todolist->contentsX() + pos.x()); KOTodoViewItem *i=(KOTodoViewItem *)todolist->itemAt(pos); /* Check wether a tooltip is necessary. */ if( i && KOPrefs::instance()->mEnableToolTips ) { /* Calculate the rectangle. */ r=todolist->itemRect(i); headerPos = todolist->header()->sectionPos(col)-todolist->contentsX(); r.setLeft( (headerPos < 0 ? 0 : headerPos) ); r.setRight(headerPos + todolist->header()->sectionSize(col)); /* Show the tip */ TQString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->todo(), TQDate(), true ) );; if ( !tipText.isEmpty() ) { tip(r, tipText); } } } KOTodoListView::KOTodoListView( TQWidget *parent, const char *name ) : TDEListView( parent, name ), mCalendar( 0 ), mChanger( 0 ) { mOldCurrent = 0; mMousePressed = false; } KOTodoListView::~KOTodoListView() { } void KOTodoListView::setCalendar( Calendar *cal ) { mCalendar = cal; setAcceptDrops( mCalendar ); viewport()->setAcceptDrops( mCalendar ); } bool KOTodoListView::event(TQEvent *e) { int tmp=0; KOTodoViewItem *i; /* Checks for an ApplicationPaletteChange event and updates * the small Progress bars to make therm have the right colors. */ if(e->type()==TQEvent::ApplicationPaletteChange) { TDEListView::event(e); i=(KOTodoViewItem *)itemAtIndex(tmp); while(i!=0) { i->construct(); tmp++; i=(KOTodoViewItem *)itemAtIndex(tmp); } } return (TDEListView::event(e) || e->type()==TQEvent::ApplicationPaletteChange); } void KOTodoListView::contentsDragEnterEvent(TQDragEnterEvent *e) { #ifndef KORG_NODND // kdDebug(5850) << "KOTodoListView::contentsDragEnterEvent" << endl; if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) && !TQTextDrag::canDecode( e ) ) { e->ignore(); return; } mOldCurrent = currentItem(); #endif } void KOTodoListView::contentsDragMoveEvent(TQDragMoveEvent *e) { #ifndef KORG_NODND // kdDebug(5850) << "KOTodoListView::contentsDragMoveEvent" << endl; if ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) && !TQTextDrag::canDecode( e ) ) { e->ignore(); return; } e->accept(); #endif } void KOTodoListView::contentsDragLeaveEvent( TQDragLeaveEvent * ) { #ifndef KORG_NODND // kdDebug(5850) << "KOTodoListView::contentsDragLeaveEvent" << endl; setCurrentItem(mOldCurrent); setSelected(mOldCurrent,true); #endif } void KOTodoListView::contentsDropEvent( TQDropEvent *e ) { #ifndef KORG_NODND kdDebug(5850) << "KOTodoListView::contentsDropEvent" << endl; if ( !mCalendar || !mChanger || ( !ICalDrag::canDecode( e ) && !VCalDrag::canDecode( e ) && !TQTextDrag::canDecode( e ) ) ) { e->ignore(); return; } DndFactory factory( mCalendar ); Todo *todo = factory.createDropTodo(e); if ( todo ) { e->acceptAction(); KOTodoViewItem *destination = (KOTodoViewItem *)itemAt(contentsToViewport(e->pos())); Todo *destinationEvent = 0; if (destination) destinationEvent = destination->todo(); Todo *existingTodo = mCalendar->todo(todo->uid()); if( existingTodo ) { kdDebug(5850) << "Drop existing Todo " << existingTodo << " onto " << destinationEvent << endl; Incidence *to = destinationEvent; while(to) { if (to->uid() == todo->uid()) { KMessageBox::information(this, i18n("Cannot move to-do to itself or a child of itself."), i18n("Drop To-do"), "NoDropTodoOntoItself" ); delete todo; return; } to = to->relatedTo(); } Todo*oldTodo = existingTodo->clone(); if ( mChanger->beginChange( existingTodo, 0, TQString() ) ) { existingTodo->setRelatedTo( destinationEvent ); mChanger->changeIncidence( oldTodo, existingTodo, KOGlobals::RELATION_MODIFIED, this ); mChanger->endChange( existingTodo, 0, TQString() ); } else { KMessageBox::sorry( this, i18n("Unable to change to-do's parent, " "because the to-do cannot be locked.") ); } delete oldTodo; delete todo; } else { // kdDebug(5850) << "Drop new Todo" << endl; todo->setRelatedTo(destinationEvent); if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) { KODialogManager::errorSaveIncidence( this, todo ); delete todo; return; } } } else { TQString text; KOTodoViewItem *todoi = dynamic_cast<KOTodoViewItem *>(itemAt( contentsToViewport(e->pos()) )); if ( ! todoi ) { // Not dropped on a todo item: e->ignore(); kdDebug( 5850 ) << "KOTodoListView::contentsDropEvent(): Not dropped on a todo item" << endl; kdDebug( 5850 ) << "TODO: Create a new todo with the given data" << endl; // FIXME: Create a new todo with the given text/contact/whatever } else if ( TQTextDrag::decode(e, text) ) { //TQListViewItem *qlvi = itemAt( contentsToViewport(e->pos()) ); kdDebug(5850) << "Dropped : " << text << endl; Todo*todo = todoi->todo(); if( mChanger->beginChange( todo, 0, TQString() ) ) { Todo*oldtodo = todo->clone(); if( text.startsWith( "file:" ) ) { todo->addAttachment( new Attachment( text ) ); } else { TQStringList emails = KPIM::splitEmailAddrList( text ); for(TQStringList::ConstIterator it = emails.begin();it!=emails.end();++it) { kdDebug(5850) << " Email: " << (*it) << endl; int pos = (*it).find("<"); TQString name = (*it).left(pos); TQString email = (*it).mid(pos); if (!email.isEmpty() && todoi) { todo->addAttendee( new Attendee( name, email ) ); } } } //FIXME: attendees or attachment added, so there is something modified mChanger->changeIncidence( oldtodo, todo, KOGlobals::NOTHING_MODIFIED, this ); mChanger->endChange( todo, 0, TQString() ); } else { KMessageBox::sorry( this, i18n("Unable to add attendees to the to-do, " "because the to-do cannot be locked.") ); } } else { kdDebug(5850) << "KOTodoListView::contentsDropEvent(): Todo from drop not decodable" << endl; e->ignore(); } } #endif } void KOTodoListView::contentsMousePressEvent(TQMouseEvent* e) { TQListView::contentsMousePressEvent(e); TQPoint p(contentsToViewport(e->pos())); TQListViewItem *i = itemAt(p); if (i) { // if the user clicked into the root decoration of the item, don't // try to start a drag! if (p.x() > header()->sectionPos(header()->mapToIndex(0)) + treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) + itemMargin() || p.x() < header()->sectionPos(header()->mapToIndex(0))) { if (e->button()==Qt::LeftButton) { mPressPos = e->pos(); mMousePressed = true; } } } } void KOTodoListView::contentsMouseMoveEvent(TQMouseEvent* e) { #ifndef KORG_NODND // kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent()" << endl; TQListView::contentsMouseMoveEvent(e); if (mMousePressed && (mPressPos - e->pos()).manhattanLength() > TQApplication::startDragDistance()) { mMousePressed = false; TQListViewItem *item = itemAt(contentsToViewport(mPressPos)); if ( item && mCalendar ) { // kdDebug(5850) << "Start Drag for item " << item->text(0) << endl; DndFactory factory( mCalendar ); ICalDrag *vd = factory.createDrag( ((KOTodoViewItem *)item)->todo(),viewport()); if (vd->drag()) { kdDebug(5850) << "KOTodoListView::contentsMouseMoveEvent(): Delete drag source" << endl; } /* TQString source = fullPath(item); if ( TQFile::exists(source) ) { KURL url; url.setPath(source); KURLDrag* ud = KURLDrag::newDrag(KURL::List(url), viewport()); if ( ud->drag() ) TQMessageBox::information( this, "Drag source", TQString("Delete ")+source, "Not implemented" ); */ } } #endif } void KOTodoListView::contentsMouseReleaseEvent(TQMouseEvent *e) { TQListView::contentsMouseReleaseEvent(e); mMousePressed = false; } void KOTodoListView::contentsMouseDoubleClickEvent(TQMouseEvent *e) { if (!e) return; TQPoint vp = contentsToViewport(e->pos()); TQListViewItem *item = itemAt(vp); if (!item) return; emit doubleClicked(item,vp,0); } ///////////////////////////////////////////////////////////////////////////// KOTodoView::KOTodoView( Calendar *calendar, TQWidget *parent, const char* name) : KOrg::BaseView( calendar, parent, name ) { TQBoxLayout *topLayout = new TQVBoxLayout( this ); TQLabel *title = new TQLabel( i18n("To-dos:"), this ); title->setFrameStyle( TQFrame::Panel | TQFrame::Raised ); topLayout->addWidget( title ); mQuickAdd = new KPIM::ClickLineEdit( this, i18n( "Click to add a new to-do" ) ); mQuickAdd->setAcceptDrops( false ); topLayout->addWidget( mQuickAdd ); if ( !KOPrefs::instance()->mEnableQuickTodo ) mQuickAdd->hide(); mTodoListView = new KOTodoListView( this ); topLayout->addWidget( mTodoListView ); mTodoListView->setRootIsDecorated( true ); mTodoListView->setAllColumnsShowFocus( true ); mTodoListView->setShowSortIndicator( true ); mTodoListView->addColumn( i18n("Summary") ); mTodoListView->addColumn( i18n("Recurs") ); mTodoListView->addColumn( i18n("Priority") ); mTodoListView->setColumnAlignment( ePriorityColumn, AlignHCenter ); mTodoListView->addColumn( i18n("Complete") ); mTodoListView->setColumnAlignment( ePercentColumn, AlignRight ); mTodoListView->addColumn( i18n("Due Date/Time") ); mTodoListView->setColumnAlignment( eDueDateColumn, AlignLeft ); mTodoListView->addColumn( i18n("Categories") ); mTodoListView->addColumn( i18n( "Calendar" ) ); #if 0 mTodoListView->addColumn( i18n("Sort Id") ); mTodoListView->setColumnAlignment( 4, AlignHCenter ); #endif mTodoListView->setMinimumHeight( 60 ); mTodoListView->setItemsRenameable( true ); mTodoListView->setRenameable( 0 ); mTodoListView->setColumnWidthMode( eSummaryColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( eRecurColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( ePriorityColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( ePercentColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( eDueDateColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( eCategoriesColumn, TQListView::Manual ); mTodoListView->setColumnWidthMode( eFolderColumn, TQListView::Manual ); #if 0 mTodoListView->setColumnWidthMode( eDescriptionColumn, TQListView::Manual ); #endif mPriorityPopupMenu = new TQPopupMenu( this ); mPriority[ mPriorityPopupMenu->insertItem( i18n("Unspecified priority", "unspecified") ) ] = 0; mPriority[ mPriorityPopupMenu->insertItem( i18n( "1 (highest)") ) ] = 1; mPriority[ mPriorityPopupMenu->insertItem( i18n( "2" ) ) ] = 2; mPriority[ mPriorityPopupMenu->insertItem( i18n( "3" ) ) ] = 3; mPriority[ mPriorityPopupMenu->insertItem( i18n( "4" ) ) ] = 4; mPriority[ mPriorityPopupMenu->insertItem( i18n( "5 (medium)" ) ) ] = 5; mPriority[ mPriorityPopupMenu->insertItem( i18n( "6" ) ) ] = 6; mPriority[ mPriorityPopupMenu->insertItem( i18n( "7" ) ) ] = 7; mPriority[ mPriorityPopupMenu->insertItem( i18n( "8" ) ) ] = 8; mPriority[ mPriorityPopupMenu->insertItem( i18n( "9 (lowest)" ) ) ] = 9; connect( mPriorityPopupMenu, TQT_SIGNAL( activated( int ) ), TQT_SLOT( setNewPriority( int ) )); mPercentageCompletedPopupMenu = new TQPopupMenu(this); for (int i = 0; i <= 100; i+=10) { TQString label = TQString ("%1 %").arg (i); mPercentage[mPercentageCompletedPopupMenu->insertItem (label)] = i; } connect( mPercentageCompletedPopupMenu, TQT_SIGNAL( activated( int ) ), TQT_SLOT( setNewPercentage( int ) ) ); mMovePopupMenu = new KDatePickerPopup( KDatePickerPopup::NoDate | KDatePickerPopup::DatePicker | KDatePickerPopup::Words ); mCopyPopupMenu = new KDatePickerPopup( KDatePickerPopup::NoDate | KDatePickerPopup::DatePicker | KDatePickerPopup::Words ); connect( mMovePopupMenu, TQT_SIGNAL( dateChanged( TQDate )), TQT_SLOT( setNewDate( TQDate ) ) ); connect( mCopyPopupMenu, TQT_SIGNAL( dateChanged( TQDate )), TQT_SLOT( copyTodoToDate( TQDate ) ) ); mItemPopupMenu = new TQPopupMenu(this); mItemPopupMenu->insertItem(i18n("&Show"), this, TQT_SLOT (showTodo())); mItemPopupMenu->insertItem(i18n("&Edit..."), this, TQT_SLOT (editTodo()), 0, ePopupEdit ); #ifndef KORG_NOPRINTER mItemPopupMenu->insertItem(KOGlobals::self()->smallIcon("printer"), i18n("&Print..."), this, TQT_SLOT( printTodo() ) ); #endif mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("edit-delete"), i18n("&Delete"), this, TQT_SLOT (deleteTodo()), 0, ePopupDelete ); mItemPopupMenu->insertSeparator(); mItemPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("New &To-do..."), this, TQT_SLOT (newTodo()) ); mItemPopupMenu->insertItem(i18n("New Su&b-to-do..."), this, TQT_SLOT (newSubTodo())); mItemPopupMenu->insertItem( i18n("&Make this To-do Independent"), this, TQT_SIGNAL( unSubTodoSignal() ), 0, ePopupUnSubTodo ); mItemPopupMenu->insertItem( i18n("Make all Sub-to-dos &Independent"), this, TQT_SIGNAL( unAllSubTodoSignal() ), 0, ePopupUnAllSubTodo ); mItemPopupMenu->insertSeparator(); mItemPopupMenu->insertItem( i18n("&Copy To"), mCopyPopupMenu, ePopupCopyTo ); mItemPopupMenu->insertItem(i18n("&Move To"), mMovePopupMenu, ePopupMoveTo ); mItemPopupMenu->insertSeparator(); mItemPopupMenu->insertItem(i18n("delete completed to-dos","Pur&ge Completed"), this, TQT_SLOT( purgeCompleted() ) ); connect( mMovePopupMenu, TQT_SIGNAL( dateChanged( TQDate ) ), mItemPopupMenu, TQT_SLOT( hide() ) ); connect( mCopyPopupMenu, TQT_SIGNAL( dateChanged( TQDate ) ), mItemPopupMenu, TQT_SLOT( hide() ) ); mPopupMenu = new TQPopupMenu(this); mPopupMenu->insertItem(KOGlobals::self()->smallIconSet("todo"), i18n("&New To-do..."), this, TQT_SLOT(newTodo()) ); mPopupMenu->insertItem(i18n("delete completed to-dos","&Purge Completed"), this, TQT_SLOT(purgeCompleted())); mDocPrefs = new DocPrefs( name ); // Double clicking conflicts with opening/closing the subtree connect( mTodoListView, TQT_SIGNAL( doubleClicked( TQListViewItem *, const TQPoint &, int ) ), TQT_SLOT( editItem( TQListViewItem *, const TQPoint &, int ) ) ); connect( mTodoListView, TQT_SIGNAL( returnPressed( TQListViewItem * ) ), TQT_SLOT( editItem( TQListViewItem * ) ) ); connect( mTodoListView, TQT_SIGNAL( contextMenuRequested( TQListViewItem *, const TQPoint &, int ) ), TQT_SLOT( popupMenu( TQListViewItem *, const TQPoint &, int ) ) ); connect( mTodoListView, TQT_SIGNAL( expanded( TQListViewItem * ) ), TQT_SLOT( itemStateChanged( TQListViewItem * ) ) ); connect( mTodoListView, TQT_SIGNAL( collapsed( TQListViewItem * ) ), TQT_SLOT( itemStateChanged( TQListViewItem * ) ) ); #if 0 connect(mTodoListView,TQT_SIGNAL(selectionChanged(TQListViewItem *)), TQT_SLOT(selectionChanged(TQListViewItem *))); connect(mTodoListView,TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(selectionChanged(TQListViewItem *))); connect(mTodoListView,TQT_SIGNAL(pressed(TQListViewItem *)), TQT_SLOT(selectionChanged(TQListViewItem *))); #endif connect( mTodoListView, TQT_SIGNAL(selectionChanged() ), TQT_SLOT( processSelectionChange() ) ); connect( mQuickAdd, TQT_SIGNAL( returnPressed () ), TQT_SLOT( addQuickTodo() ) ); new KOTodoListViewToolTip( mTodoListView->viewport(), calendar, mTodoListView ); } KOTodoView::~KOTodoView() { delete mDocPrefs; } void KOTodoView::setCalendar( Calendar *cal ) { BaseView::setCalendar( cal ); mTodoListView->setCalendar( cal ); } void KOTodoView::updateView() { // kdDebug(5850) << "KOTodoView::updateView()" << endl; int oldPos = mTodoListView->contentsY(); mItemsToDelete.clear(); mTodoListView->clear(); Todo::List todoList = calendar()->todos(); /* kdDebug(5850) << "KOTodoView::updateView(): Todo List:" << endl; Event *t; for(t = todoList.first(); t; t = todoList.next()) { kdDebug(5850) << " " << t->getSummary() << endl; if (t->getRelatedTo()) { kdDebug(5850) << " (related to " << t->getRelatedTo()->getSummary() << ")" << endl; } TQPtrList<Event> l = t->getRelations(); Event *c; for(c=l.first();c;c=l.next()) { kdDebug(5850) << " - relation: " << c->getSummary() << endl; } } */ // Put for each Event a KOTodoViewItem in the list view. Don't rely on a // specific order of events. That means that we have to generate parent items // recursively for proper hierarchical display of Todos. mTodoMap.clear(); Todo::List::ConstIterator it; for( it = todoList.begin(); it != todoList.end(); ++it ) { if ( !mTodoMap.contains( *it ) ) { insertTodoItem( *it ); } } // Restore opened/closed state mTodoListView->blockSignals( true ); if( mDocPrefs ) restoreItemState( mTodoListView->firstChild() ); mTodoListView->blockSignals( false ); mTodoListView->setContentsPos( 0, oldPos ); processSelectionChange(); } void KOTodoView::restoreItemState( TQListViewItem *item ) { while( item ) { KOTodoViewItem *todoItem = (KOTodoViewItem *)item; todoItem->setOpen( mDocPrefs->readBoolEntry( todoItem->todo()->uid() ) ); if( item->childCount() > 0 ) restoreItemState( item->firstChild() ); item = item->nextSibling(); } } TQMap<Todo *,KOTodoViewItem *>::ConstIterator KOTodoView::insertTodoItem(Todo *todo) { // kdDebug(5850) << "KOTodoView::insertTodoItem(): " << todo->getSummary() << endl; Incidence *incidence = todo->relatedTo(); if (incidence && incidence->type() == "Todo") { // Use dynamic_cast, because in the future the related item might also be an event Todo *relatedTodo = dynamic_cast<Todo *>(incidence); // just make sure we know we have this item already to avoid endless recursion (Bug 101696) mTodoMap.insert(todo,0); // kdDebug(5850) << " has Related" << endl; TQMap<Todo *,KOTodoViewItem *>::ConstIterator itemIterator; itemIterator = mTodoMap.find(relatedTodo); if (itemIterator == mTodoMap.end()) { // kdDebug(5850) << " related not yet in list" << endl; itemIterator = insertTodoItem (relatedTodo); } // isn't this pretty stupid? We give one Todo to the KOTodoViewItem // and one into the map. Sure finding is more easy but why? -zecke KOTodoViewItem *todoItem; // in case we found a related parent, which has no KOTodoViewItem yet, this must // be the case where 2 items refer to each other, therefore simply create item as root item if ( *itemIterator == 0 ) { todo->setRelatedTo(0); // break the recursion, else we will have troubles later todoItem = new KOTodoViewItem(mTodoListView,todo,this); } else todoItem = new KOTodoViewItem(*itemIterator,todo,this); return mTodoMap.insert(todo,todoItem); } else { // kdDebug(5850) << " no Related" << endl; // see above -zecke KOTodoViewItem *todoItem = new KOTodoViewItem(mTodoListView,todo,this); return mTodoMap.insert(todo,todoItem); } } void KOTodoView::removeTodoItems() { KOTodoViewItem *item; for ( item = mItemsToDelete.first(); item; item = mItemsToDelete.next() ) { Todo *todo = item->todo(); if ( todo && mTodoMap.contains( todo ) ) { mTodoMap.remove( todo ); } delete item; } mItemsToDelete.clear(); } bool KOTodoView::scheduleRemoveTodoItem( KOTodoViewItem *todoItem ) { if ( todoItem ) { mItemsToDelete.append( todoItem ); TQTimer::singleShot( 0, this, TQT_SLOT( removeTodoItems() ) ); return true; } else return false; } void KOTodoView::updateConfig() { mTodoListView->repaintContents(); } Incidence::List KOTodoView::selectedIncidences() { Incidence::List selected; KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem()); // if (!item) item = mActiveItem; if (item) selected.append(item->todo()); return selected; } Todo::List KOTodoView::selectedTodos() { Todo::List selected; KOTodoViewItem *item = (KOTodoViewItem *)(mTodoListView->selectedItem()); // if (!item) item = mActiveItem; if (item) selected.append(item->todo()); return selected; } void KOTodoView::changeIncidenceDisplay(Incidence *incidence, int action) { // The todo view only displays todos, so exit on all other incidences if ( incidence->type() != "Todo" ) return; CalFilter *filter = calendar()->filter(); bool isFiltered = filter && !filter->filterIncidence( incidence ); Todo *todo = static_cast<Todo *>(incidence); if ( todo ) { KOTodoViewItem *todoItem = 0; if ( mTodoMap.contains( todo ) ) { todoItem = mTodoMap[todo]; } switch ( action ) { case KOGlobals::INCIDENCEADDED: case KOGlobals::INCIDENCEEDITED: // If it's already there, edit it, otherwise just add if ( todoItem ) { if ( isFiltered ) { scheduleRemoveTodoItem( todoItem ); } else { // correctly update changes in relations Todo*parent = dynamic_cast<Todo*>( todo->relatedTo() ); KOTodoViewItem*parentItem = 0; if ( parent && mTodoMap.contains(parent) ) { parentItem = mTodoMap[ parent ]; } if ( todoItem->parent() != parentItem ) { // The relations changed if ( parentItem ) { parentItem->insertItem( todoItem ); } else { mTodoListView->insertItem( todoItem ); } } todoItem->construct(); } } else { if ( !isFiltered ) { insertTodoItem( todo ); } } mTodoListView->sort(); break; case KOGlobals::INCIDENCEDELETED: if ( todoItem ) { scheduleRemoveTodoItem( todoItem ); } break; default: TQTimer::singleShot( 0, this, TQT_SLOT( updateView() ) ); } } else { // use a TQTimer here, because when marking todos finished using // the checkbox, this slot gets called, but we cannot update the views // because we're still inside KOTodoViewItem::stateChange TQTimer::singleShot(0,this,TQT_SLOT(updateView())); } } void KOTodoView::showDates(const TQDate &, const TQDate &) { } void KOTodoView::showIncidences( const Incidence::List &, const TQDate & ) { kdDebug(5850) << "KOTodoView::showIncidences( const Incidence::List & ): not yet implemented" << endl; } CalPrinterBase::PrintType KOTodoView::printType() { return CalPrinterBase::Todolist; } void KOTodoView::editItem( TQListViewItem *item ) { if ( item ) { emit editIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate () ); } } void KOTodoView::editItem( TQListViewItem *item, const TQPoint &, int ) { editItem( item ); } void KOTodoView::showItem( TQListViewItem *item ) { if ( item ) { emit showIncidenceSignal( static_cast<KOTodoViewItem *>( item )->todo(), TQDate() ); } } void KOTodoView::showItem( TQListViewItem *item, const TQPoint &, int ) { showItem( item ); } void KOTodoView::popupMenu( TQListViewItem *item, const TQPoint &, int column ) { mActiveItem = static_cast<KOTodoViewItem *>( item ); if ( mActiveItem && mActiveItem->todo() && !mActiveItem->todo()->isReadOnly() ) { bool editable = !mActiveItem->todo()->isReadOnly(); mItemPopupMenu->setItemEnabled( ePopupEdit, editable ); mItemPopupMenu->setItemEnabled( ePopupDelete, editable ); mItemPopupMenu->setItemEnabled( ePopupMoveTo, editable ); mItemPopupMenu->setItemEnabled( ePopupCopyTo, editable ); mItemPopupMenu->setItemEnabled( ePopupUnSubTodo, editable ); mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo, editable ); if ( editable ) { TQDate date = mActiveItem->todo()->dtDue().date(); if ( mActiveItem->todo()->hasDueDate () ) { mMovePopupMenu->datePicker()->setDate( date ); } else { mMovePopupMenu->datePicker()->setDate( TQDate::currentDate() ); } switch ( column ) { case ePriorityColumn: mPriorityPopupMenu->popup( TQCursor::pos() ); break; case ePercentColumn: { mPercentageCompletedPopupMenu->popup( TQCursor::pos() ); break; } case eDueDateColumn: mMovePopupMenu->popup( TQCursor::pos() ); break; case eCategoriesColumn: getCategoryPopupMenu( mActiveItem )->popup( TQCursor::pos() ); break; default: mCopyPopupMenu->datePicker()->setDate( date ); mCopyPopupMenu->datePicker()->setDate( TQDate::currentDate() ); mItemPopupMenu->setItemEnabled( ePopupUnSubTodo, mActiveItem->todo()->relatedTo() ); mItemPopupMenu->setItemEnabled( ePopupUnAllSubTodo, !mActiveItem->todo()->relations().isEmpty() ); mItemPopupMenu->popup( TQCursor::pos() ); } } else { mItemPopupMenu->popup( TQCursor::pos() ); } } else mPopupMenu->popup( TQCursor::pos() ); } void KOTodoView::newTodo() { kdDebug() << k_funcinfo << endl; emit newTodoSignal( 0/*ResourceCalendar*/, TQString()/*subResource*/, TQDate::currentDate().addDays(7) ); } void KOTodoView::newSubTodo() { if (mActiveItem) { emit newSubTodoSignal(mActiveItem->todo()); } } void KOTodoView::editTodo() { editItem( mActiveItem ); } void KOTodoView::showTodo() { showItem( mActiveItem ); } void KOTodoView::printTodo() { #ifndef KORG_NOPRINTER KOCoreHelper helper; CalPrinter printer( this, BaseView::calendar(), &helper ); connect( this, TQT_SIGNAL(configChanged()), &printer, TQT_SLOT(updateConfig()) ); Incidence::List selectedIncidences; selectedIncidences.append( mActiveItem->todo() ); TQDateTime todoDate; if ( mActiveItem->todo() && mActiveItem->todo()->hasStartDate() ) { todoDate = mActiveItem->todo()->dtStart(); } else { todoDate = mActiveItem->todo()->dtDue(); } printer.print( KOrg::CalPrinterBase::Incidence, todoDate.date(), todoDate.date(), selectedIncidences ); #endif } void KOTodoView::deleteTodo() { if (mActiveItem) { emit deleteIncidenceSignal( mActiveItem->todo() ); } } void KOTodoView::setNewPriority(int index) { if ( !mActiveItem || !mChanger ) return; Todo *todo = mActiveItem->todo(); if ( !todo->isReadOnly () && mChanger->beginChange( todo, 0, TQString() ) ) { Todo *oldTodo = todo->clone(); todo->setPriority(mPriority[index]); mActiveItem->construct(); mChanger->changeIncidence( oldTodo, todo, KOGlobals::PRIORITY_MODIFIED, this ); mChanger->endChange( todo, 0, TQString() ); delete oldTodo; } } void KOTodoView::setNewPercentage( KOTodoViewItem *item, int percentage ) { kdDebug(5850) << "KOTodoView::setNewPercentage( " << percentage << "), item = " << item << endl; if ( !item || !mChanger ) return; Todo *todo = item->todo(); if ( !todo ) return; if ( !todo->isReadOnly () && mChanger->beginChange( todo, 0, TQString() ) ) { Todo *oldTodo = todo->clone(); /* Old code to make sub-items's percentage related to this one's: TQListViewItem *myChild = firstChild(); KOTodoViewItem *item; while( myChild ) { item = static_cast<KOTodoViewItem*>(myChild); item->stateChange(state); myChild = myChild->nextSibling(); }*/ if ( percentage == 100 ) { todo->setCompleted( TQDateTime::currentDateTime() ); // If the todo does recur, it doesn't get set as completed. However, the // item is still checked. Uncheck it again. if ( !todo->isCompleted() ) { item->setState( TQCheckListItem::Off ); } } else { todo->setPercentComplete( percentage ); } item->construct(); if ( todo->doesRecur() && percentage == 100 ) mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE, this ); else mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this ); mChanger->endChange( todo, 0, TQString() ); delete oldTodo; } else { item->construct(); kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl; } } void KOTodoView::setNewPercentage( int index ) { setNewPercentage( mActiveItem, mPercentage[index] ); } void KOTodoView::setNewDate( TQDate date ) { if ( !mActiveItem || !mChanger ) return; Todo *todo = mActiveItem->todo(); if ( !todo ) return; if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, TQString() ) ) { Todo *oldTodo = todo->clone(); TQDateTime dt; dt.setDate( date ); if ( !todo->doesFloat() ) { dt.setTime( todo->dtDue().time() ); } todo->setHasDueDate( !date.isNull() ); todo->setDtDue( dt ); mActiveItem->construct(); mChanger->changeIncidence( oldTodo, todo, KOGlobals::COMPLETION_MODIFIED, this ); mChanger->endChange( todo, 0, TQString() ); delete oldTodo; } else { kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl; } } void KOTodoView::copyTodoToDate( TQDate date ) { TQDateTime dt( date ); if ( mActiveItem && mChanger ) { Todo *oldTodo = mActiveItem->todo(); Todo *newTodo = oldTodo->clone(); newTodo->recreate(); newTodo->setHasDueDate( !date.isNull() ); if ( oldTodo->hasDueDate() && !oldTodo->doesFloat() ) { dt.setTime( oldTodo->dtDue().time() ); } newTodo->setDtDue( dt ); newTodo->setPercentComplete( 0 ); TQPair<ResourceCalendar *, TQString>p = CalHelper::incSubResourceCalendar( calendar(), mActiveItem->todo() ); mChanger->addIncidence( newTodo, p.first, p.second, this ); } } TQPopupMenu *KOTodoView::getCategoryPopupMenu( KOTodoViewItem *todoItem ) { TQPopupMenu *tempMenu = new TQPopupMenu( this ); TQStringList checkedCategories = todoItem->todo()->categories(); tempMenu->setCheckable( true ); TQStringList::Iterator it; for ( it = KOPrefs::instance()->mCustomCategories.begin(); it != KOPrefs::instance()->mCustomCategories.end(); ++it ) { int index = tempMenu->insertItem( *it ); mCategory[ index ] = *it; if ( checkedCategories.find( *it ) != checkedCategories.end() ) tempMenu->setItemChecked( index, true ); } connect ( tempMenu, TQT_SIGNAL( activated( int ) ), TQT_SLOT( changedCategories( int ) ) ); return tempMenu; } void KOTodoView::changedCategories(int index) { if ( !mActiveItem || !mChanger ) return; Todo *todo = mActiveItem->todo(); if ( !todo ) return; if ( !todo->isReadOnly() && mChanger->beginChange( todo, 0, TQString() ) ) { Todo *oldTodo = todo->clone(); TQStringList categories = todo->categories (); if ( categories.find( mCategory[index] ) != categories.end() ) categories.remove( mCategory[index] ); else categories.insert( categories.end(), mCategory[index] ); categories.sort(); todo->setCategories( categories ); mActiveItem->construct(); mChanger->changeIncidence( oldTodo, todo, KOGlobals::CATEGORY_MODIFIED, this ); mChanger->endChange( todo, 0, TQString() ); delete oldTodo; } else { kdDebug(5850) << "No active item, active item is read-only, or locking failed" << endl; } } void KOTodoView::setDocumentId( const TQString &id ) { kdDebug(5850) << "KOTodoView::setDocumentId()" << endl; mDocPrefs->setDoc( id ); } void KOTodoView::itemStateChanged( TQListViewItem *item ) { if (!item) return; KOTodoViewItem *todoItem = (KOTodoViewItem *)item; // kdDebug(5850) << "KOTodoView::itemStateChanged(): " << todoItem->todo()->summary() << endl; if( mDocPrefs ) mDocPrefs->writeEntry( todoItem->todo()->uid(), todoItem->isOpen() ); } void KOTodoView::setNewPercentageDelayed( KOTodoViewItem *item, int percentage ) { mPercentChangedMap.append( tqMakePair( item, percentage ) ); TQTimer::singleShot( 0, this, TQT_SLOT( processDelayedNewPercentage() ) ); } void KOTodoView::processDelayedNewPercentage() { TQValueList< TQPair< KOTodoViewItem *, int> >::Iterator it; for ( it = mPercentChangedMap.begin(); it != mPercentChangedMap.end(); ++it ) setNewPercentage( (*it).first, (*it).second ); mPercentChangedMap.clear(); } void KOTodoView::saveLayout(TDEConfig *config, const TQString &group) const { mTodoListView->saveLayout(config,group); } void KOTodoView::restoreLayout(TDEConfig *config, const TQString &group) { mTodoListView->restoreLayout(config,group); } void KOTodoView::processSelectionChange() { // kdDebug(5850) << "KOTodoView::processSelectionChange()" << endl; KOTodoViewItem *item = static_cast<KOTodoViewItem *>( mTodoListView->selectedItem() ); if ( !item ) { emit incidenceSelected( 0, TQDate() ); } else { if ( selectedIncidenceDates().isEmpty() ) { emit incidenceSelected( item->todo(), TQDate() ); } else { emit incidenceSelected( item->todo(), selectedIncidenceDates().first() ); } } } void KOTodoView::clearSelection() { mTodoListView->selectAll( false ); } void KOTodoView::purgeCompleted() { emit purgeCompletedSignal(); } void KOTodoView::addQuickTodo() { if ( ! mQuickAdd->text().stripWhiteSpace().isEmpty() ) { Todo *todo = new Todo(); todo->setSummary( mQuickAdd->text() ); todo->setOrganizer( Person( KOPrefs::instance()->fullName(), KOPrefs::instance()->email() ) ); if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) { delete todo; return; } mQuickAdd->setText( TQString() ); } } void KOTodoView::setIncidenceChanger( IncidenceChangerBase *changer ) { mChanger = changer; mTodoListView->setIncidenceChanger( changer ); }