• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

ktimetracker

timetrackerwidget.cpp

Go to the documentation of this file.
00001 /*
00002  *     Copyright (C) 2007 by Mathias Soeken <msoeken@tzi.de>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation; either version 2 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License along
00015  *   with this program; if not, write to the
00016  *      Free Software Foundation, Inc.
00017  *      51 Franklin Street, Fifth Floor
00018  *      Boston, MA  02110-1301  USA.
00019  *
00020  */
00021 
00022 #include "timetrackerwidget.h"
00023 
00024 #include <QDBusConnection>
00025 #include <QFileInfo>
00026 #include <QHBoxLayout>
00027 #include <QKeyEvent>
00028 #include <QKeySequence>
00029 #include <QMap>
00030 #include <QVBoxLayout>
00031 #include <QVector>
00032 
00033 #include <KApplication>
00034 #include <KAction>
00035 #include <KActionCollection>
00036 #include <KConfig>
00037 #include <KConfigDialog>
00038 #include <KDebug>
00039 #include <KFileDialog>
00040 #include <KGlobal>
00041 #include <KIcon>
00042 #include <KLocale>
00043 #include <KMessageBox>
00044 #include <KRecentFilesAction>
00045 #include <KStandardAction>
00046 #include <KTabWidget>
00047 #include <KTemporaryFile>
00048 #include <KTreeWidgetSearchLine>
00049 #include <KUrl>
00050 #include <KIO/Job>
00051 
00052 #include "edithistorydialog.h"
00053 #include "ktimetrackerutility.h"
00054 #include "ktimetracker.h"
00055 #include "mainadaptor.h"
00056 #include "reportcriteria.h"
00057 #include "task.h"
00058 #include "taskview.h"
00059 #include "version.h"
00060 #include "ui_cfgbehavior.h"
00061 #include "ui_cfgdisplay.h"
00062 #include "ui_cfgstorage.h"
00063 
00064 //@cond PRIVATE
00065 class TimetrackerWidget::Private {
00066   public:
00067     Private() :
00068       mLastView( 0 ), mRecentFilesAction( 0 ) {}
00069 
00070     QWidget *mSearchLine;
00071     KTabWidget *mTabWidget;
00072     KTreeWidgetSearchLine *mSearchWidget;
00073     TaskView *mLastView;
00074     QVector<TaskView*> mIsNewVector;
00075     QMap<QString, KAction*> mActions;
00076     KRecentFilesAction *mRecentFilesAction;
00077 
00078     struct ActionData {
00079       QString iconName;
00080       const char* caption;
00081       const char* slot;
00082       QString name;
00083       const char* toolTip;
00084       const char* whatsThis;
00085     };
00086 };
00087 //@endcond
00088 
00089 TimetrackerWidget::TimetrackerWidget( QWidget *parent ) : QWidget( parent ),
00090   d( new TimetrackerWidget::Private() )
00091 {
00092   kDebug(5970) << "Entering function";
00093   new MainAdaptor( this );
00094   QDBusConnection::sessionBus().registerObject( "/KTimeTracker", this );
00095 
00096   QLayout *layout = new QVBoxLayout;
00097   layout->setMargin( 0 );
00098   layout->setSpacing( 0 );
00099 
00100   QLayout *innerLayout = new QHBoxLayout;
00101   d->mSearchLine = new QWidget( this );
00102   innerLayout->setMargin( KDialog::marginHint() );
00103   innerLayout->setSpacing( KDialog::spacingHint() );
00104   d->mSearchWidget = new KTreeWidgetSearchLine( d->mSearchLine );
00105   d->mSearchWidget->setClickMessage( i18n( "Search or add task" ) );
00106   d->mSearchWidget->setWhatsThis( i18n( "This is a combined field. As long as you do not type ENTER, it acts as a filter. Then, only tasks that match your input are shown. As soon as you type ENTER, your input is used as name to create a new task." ) );
00107   d->mSearchWidget->installEventFilter( this );
00108   innerLayout->addWidget( d->mSearchWidget );
00109   d->mSearchLine->setLayout( innerLayout );
00110 
00111   d->mTabWidget = new KTabWidget( this );
00112   layout->addWidget( d->mSearchLine );
00113   layout->addWidget( d->mTabWidget );
00114   setLayout( layout );
00115 
00116   d->mTabWidget->setFocus( Qt::OtherFocusReason );
00117 
00118   connect( d->mTabWidget, SIGNAL( currentChanged( int ) ),
00119            this, SIGNAL( currentTaskViewChanged() ) );
00120   connect( d->mTabWidget, SIGNAL( currentChanged( int ) ),
00121            this, SLOT( slotCurrentChanged() ) );
00122   connect( d->mTabWidget, SIGNAL( mouseDoubleClick() ),
00123            this, SLOT( newFile() ) );
00124 
00125   showSearchBar( !KTimeTrackerSettings::configPDA() );
00126   showTabBar( false );
00127 }
00128 
00129 TimetrackerWidget::~TimetrackerWidget()
00130 {
00131   if ( d->mRecentFilesAction ) {
00132     d->mRecentFilesAction->saveEntries( KGlobal::config()->group( "Recent Files" ) );
00133   }
00134 
00135   delete d;
00136 }
00137 
00138 bool TimetrackerWidget::allEventsHaveEndTiMe()
00139 {
00140   bool result=true;
00141   TaskView *taskView;
00142   for ( int i = 0; i < d->mTabWidget->count(); ++i )
00143   {
00144     taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00145     if ( !taskView->allEventsHaveEndTiMe() ) result=false;
00146   }
00147   return result;
00148 }
00149 
00150 void TimetrackerWidget::saveProperties( KConfigGroup &cfg )
00151 {
00152   cfg.writeEntry( "WindowShown", isVisible());
00153 }
00154 
00155 void TimetrackerWidget::readProperties( const KConfigGroup &cfg )
00156 {
00157   if( cfg.readEntry( "WindowShown", true ))
00158     show();
00159 }
00160 
00161 void TimetrackerWidget::addTaskView( const QString &fileName )
00162 {
00163   kDebug(5970) << "Entering function (fileName=" << fileName << ")";
00164   bool isNew = fileName.isEmpty();
00165   QString lFileName = fileName;
00166 
00167   if ( isNew )
00168   {
00169     KTemporaryFile tempFile;
00170     tempFile.setAutoRemove( false );
00171     if ( tempFile.open() )
00172     {
00173       lFileName = tempFile.fileName();
00174       tempFile.close();
00175     }
00176     else
00177     {
00178       KMessageBox::error( this, i18n( "Cannot create new file." ) );
00179       return;
00180     }
00181   }
00182 
00183   TaskView *taskView = new TaskView( this );
00184   connect( taskView, SIGNAL( contextMenuRequested( const QPoint& ) ),
00185            this, SIGNAL( contextMenuRequested( const QPoint& ) ) );
00186   connect( taskView, SIGNAL( tasksChanged( const QList< Task* >& ) ),
00187            this, SLOT( updateTabs() ) );
00188 
00189   d->mTabWidget->addTab( taskView,
00190           isNew ? KIcon( "document-save" ) : KIcon( "ktimetracker" ),
00191           isNew ? i18n( "Untitled" ) : QFileInfo( lFileName ).fileName() );
00192   d->mTabWidget->setCurrentWidget( taskView );
00193   emit setCaption( fileName );
00194   taskView->load( lFileName );
00195   d->mSearchWidget->addTreeWidget( taskView );
00196 
00197   if ( isNew )
00198   {
00199     d->mIsNewVector.append( taskView );
00200   } else
00201   {
00202     // FIXME does not work for the startup page
00203     d->mTabWidget->setTabToolTip( d->mTabWidget->currentIndex(), lFileName );
00204   }
00205 
00206   // When adding the first tab currentChanged is not emitted, so...
00207   if ( !d->mLastView )
00208   {
00209     emit currentTaskViewChanged();
00210     slotCurrentChanged();
00211   }
00212 
00213   if ( d->mTabWidget->count() > 1 )
00214   {
00215     showTabBar( true );
00216   }
00217 }
00218 
00219 bool TimetrackerWidget::saveCurrentTaskView()
00220 {
00221   QString fileName = KFileDialog::getSaveFileName( QString(), QString(), this );
00222   if ( !fileName.isEmpty() )
00223   {
00224     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00225     taskView->stopAllTimers();
00226     taskView->save();
00227     taskView->closeStorage();
00228 
00229     QString currentFilename = taskView->storage()->icalfile();
00230     KIO::file_move( currentFilename, fileName, -1, KIO::Overwrite | KIO::HideProgressInfo );
00231     d->mIsNewVector.remove( d->mIsNewVector.indexOf( taskView ) );
00232 
00233     taskView->load( fileName );
00234     KIO::file_delete( currentFilename, KIO::HideProgressInfo );
00235 
00236     d->mTabWidget->setTabIcon( d->mTabWidget->currentIndex(), KIcon( "karm" ) );
00237     d->mTabWidget->setTabText( d->mTabWidget->currentIndex(), QFileInfo( fileName ).fileName() );
00238     d->mTabWidget->setTabToolTip( d->mTabWidget->currentIndex(), fileName );
00239 
00240     return true;
00241   }
00242 
00243   return false;
00244 }
00245 
00246 TaskView* TimetrackerWidget::currentTaskView()
00247 {
00248   return qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00249 }
00250 
00251 Task* TimetrackerWidget::currentTask()
00252 {
00253   TaskView *taskView = 0;
00254   if ( ( taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() ) ) )
00255   {
00256     return taskView->currentItem();
00257   }
00258   else
00259   {
00260     return 0;
00261   }
00262 }
00263 
00264 void TimetrackerWidget::setupActions( KActionCollection *actionCollection )
00265 {
00266   d->mActions.insert( "file_new",
00267     KStandardAction::openNew( this, SLOT( newFile() ), actionCollection ) );
00268   d->mActions[ "file_new" ]->setIcon( KIcon( "tab-new" ) );
00269 
00270   d->mActions.insert ("file_open",
00271     KStandardAction::open( this, SLOT( openFile() ), actionCollection ) );
00272 
00273   d->mRecentFilesAction = KStandardAction::openRecent(
00274       this, SLOT( openFile( const KUrl & ) ), this );
00275   actionCollection->addAction( d->mRecentFilesAction->objectName(),
00276                                d->mRecentFilesAction );
00277   d->mRecentFilesAction->loadEntries( KGlobal::config()->group( "Recent Files" ) );
00278 
00279   d->mActions.insert ("file_save",
00280     KStandardAction::save( this, SLOT( saveFile() ), actionCollection ) );
00281   d->mActions.insert ("file_close",
00282     KStandardAction::close( this, SLOT( closeFile() ), actionCollection ) );
00283   d->mActions.insert ("file_quit",
00284     KStandardAction::quit( this, SLOT( quit() ), actionCollection ) );
00285   d->mActions.insert ("configure_ktimetracker",
00286     KStandardAction::preferences( this, SLOT( showSettingsDialog() ),
00287                                   actionCollection ) );
00288 
00289   Private::ActionData actions[] = {
00290     { QString(), I18N_NOOP("Start &New Session"), SLOT( startNewSession() ),
00291       "start_new_session", I18N_NOOP("Starts a new session"), I18N_NOOP("This will reset the "
00292       "session time to 0 for all tasks, to start a new session, without "
00293       "affecting the totals.") },
00294     { "view-history", I18N_NOOP("Edit History..."), SLOT( editHistory() ), "edit_history",
00295       I18N_NOOP("Edits history of all tasks of the current document"), I18N_NOOP("A window will "
00296       "be opened where you can change start and stop times of tasks or add a "
00297       "comment to them.") },
00298     { QString(), I18N_NOOP("&Reset All Times"), SLOT( resetAllTimes() ),
00299       "reset_all_times", I18N_NOOP("Resets all times"), I18N_NOOP("This will reset the session "
00300       "and total time to 0 for all tasks, to restart from scratch.") },
00301     { "media-playback-start", I18N_NOOP("&Start"), SLOT( startCurrentTimer() ), "start",
00302       I18N_NOOP("Starts timing for selected task"), I18N_NOOP("This will start timing for the "
00303       "selected task.\nIt is even possible to time several tasks "
00304       "simultanously.\n\nYou may also start timing of tasks by double clicking "
00305       "the left mouse button on a given task. This will, however, stop timing "
00306       "of other tasks.") },
00307     { "media-playback-stop", I18N_NOOP("S&top"), SLOT( stopCurrentTimer() ), "stop",
00308       I18N_NOOP("Stops timing of the selected task"), I18N_NOOP("Stops timing of the selected task") },
00309     { QString(), I18N_NOOP("Stop &All Timers"), SLOT( stopAllTimers() ), "stopAll",
00310       I18N_NOOP("Stops all of the active timers"), I18N_NOOP("Stops all of the active timers") },
00311     { QString(), I18N_NOOP("Track Active Applications"), SLOT( focusTracking() ),
00312       "focustracking", I18N_NOOP("Auto-creates and updates tasks when the focus of the "
00313        "current window has changed"), I18N_NOOP("If the focus of a window changes for the "
00314        "first time when this action is enabled, a new task will be created "
00315        "with the title of the window as its name and will be started. If there "
00316        "already exists such an task it will be started.") },
00317     { "document-new", I18N_NOOP("&New Task..."), SLOT( newTask() ), "new_task", I18N_NOOP("Creates "
00318       "new top level task"), I18N_NOOP("This will create a new top level task.") },
00319     { "subtask-new-ktimetracker", I18N_NOOP("New &Subtask..."), SLOT( newSubTask() ),
00320       "new_sub_task", I18N_NOOP("Creates a new subtask to the current selected task"),
00321       I18N_NOOP("This will create a new subtask to the current selected task.") },
00322     { "edit-delete", I18N_NOOP("&Delete"), SLOT( deleteTask() ), "delete_task", I18N_NOOP("Deletes "
00323       "selected task"), I18N_NOOP("This will delete the selected task(s) and all "
00324       "subtasks.") },
00325     { "document-properties", I18N_NOOP("&Edit..."), SLOT( editTask() ), "edit_task",
00326       I18N_NOOP("Edits name or times for selected task"), I18N_NOOP("This will bring up a dialog "
00327       "box where you may edit the parameters for the selected task.") },
00328     { QString(), I18N_NOOP("&Mark as Complete"), SLOT( markTaskAsComplete() ),
00329       "mark_as_complete", "", "" },
00330     { QString(), I18N_NOOP("&Mark as Incomplete"), SLOT( markTaskAsIncomplete() ),
00331       "mark_as_incomplete", "", "" },
00332     { QString(), I18N_NOOP("&Export Times..."), SLOT( exportcsvFile() ), "export_times",
00333       "", "" },
00334     { QString(), I18N_NOOP("Export &History..."), SLOT( exportcsvHistory() ),
00335       "export_history", "", "" },
00336     { QString(), I18N_NOOP("Import Tasks From &Planner..."), SLOT( importPlanner() ),
00337       "import_planner", "", "" },
00338     { QString(), I18N_NOOP("Show Searchbar"), SLOT( slotSearchBar() ), "searchbar",
00339       "", "" }
00340   };
00341 
00342   for ( unsigned int i = 0;
00343         i < ( sizeof( actions ) / sizeof( Private::ActionData ) ); ++i )
00344   {
00345     Private::ActionData actionData = actions[i];
00346     KAction *action;
00347     if ( actionData.iconName.isEmpty() )
00348     {
00349       action = new KAction( i18n( actionData.caption ), this );
00350     } else
00351     {
00352       action = new KAction( KIcon( actionData.iconName ),
00353                             i18n( actionData.caption ), this );
00354     }
00355 
00356     actionCollection->addAction( actionData.name, action );
00357     connect( action, SIGNAL( triggered( bool ) ), actionData.slot );
00358     action->setToolTip( i18n( actionData.toolTip ) );
00359     action->setWhatsThis( i18n( actionData.whatsThis ) );
00360 
00361     d->mActions.insert( actionData.name, action );
00362   }
00363 
00364   // custom shortcuts
00365   d->mActions[ "stopAll" ]->setShortcut( QKeySequence( Qt::Key_Escape ) );
00366   d->mActions[ "new_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ) );
00367   d->mActions[ "new_sub_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_N ) );
00368   d->mActions[ "delete_task" ]->setShortcut( QKeySequence( Qt::Key_Delete ) );
00369   d->mActions[ "edit_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_E ) );
00370   d->mActions[ "mark_as_complete" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );
00371   d->mActions[ "mark_as_incomplete" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );
00372 
00373   d->mActions[ "mark_as_complete" ]->setIcon( UserIcon( "task-complete.xpm" ) );
00374   d->mActions[ "mark_as_incomplete" ]->setIcon( UserIcon( "task-incomplete.xpm" ) );
00375 
00376   d->mActions[ "focustracking" ]->setCheckable( true );
00377   d->mActions[ "searchbar" ]->setCheckable( true );
00378 
00379   d->mActions[ "searchbar" ]->setChecked( KTimeTrackerSettings::self()->showSearchBar() );
00380 
00381   connect( this, SIGNAL( currentTaskChanged() ),
00382            this, SLOT( slotUpdateButtons() ) );
00383   connect( this, SIGNAL( currentTaskViewChanged() ),
00384            this, SLOT( slotUpdateButtons() ) );
00385   connect( this, SIGNAL( updateButtons() ),
00386            this, SLOT( slotUpdateButtons() ) );
00387 
00388   slotUpdateButtons();
00389 }
00390 
00391 KAction* TimetrackerWidget::action( const QString &name ) const
00392 {
00393   return d->mActions.value( name );
00394 }
00395 
00396 void TimetrackerWidget::newFile()
00397 {
00398   addTaskView();
00399 }
00400 
00401 void TimetrackerWidget::openFile( const QString &fileName )
00402 {
00403   QString newFileName = fileName;
00404   if ( newFileName.isEmpty() )
00405   {
00406     newFileName = KFileDialog::getOpenFileName( QString(), QString(),
00407                                                         this );
00408     if ( newFileName.isEmpty() )
00409     {
00410       return;
00411     }
00412   }
00413 
00414   if ( d->mRecentFilesAction )
00415   {
00416     d->mRecentFilesAction->addUrl( newFileName );
00417   }
00418   addTaskView( newFileName );
00419 }
00420 
00421 void TimetrackerWidget::openFile( const KUrl &fileName )
00422 {
00423   openFile( fileName.path() );
00424 }
00425 
00426 bool TimetrackerWidget::closeFile()
00427 {
00428   kDebug(5970) << "Entering TimetrackerWidget::closeFile";
00429   TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00430 
00431   // is it an unsaved file?
00432   if ( d->mIsNewVector.contains( taskView ) )
00433   {
00434     QString message = i18n( "This document has not been saved yet. Do you want to save it?" );
00435     QString caption = i18n( "Untitled" );
00436 
00437     int result = KMessageBox::questionYesNoCancel( this, message, caption );
00438 
00439     if ( result == KMessageBox::Cancel )
00440     {
00441       return false;
00442     }
00443 
00444     if ( result == KMessageBox::Yes )
00445     {
00446       if ( !saveCurrentTaskView() )
00447       {
00448         return false;
00449       }
00450     }
00451     else
00452     { // result == No
00453       d->mIsNewVector.remove( d->mIsNewVector.indexOf( taskView ) );
00454     }
00455   }
00456 
00457   taskView->save();
00458   taskView->closeStorage();
00459 
00460   d->mTabWidget->removeTab( d->mTabWidget->currentIndex() );
00461   d->mSearchWidget->removeTreeWidget( taskView );
00462 
00463   /* emit signals and call slots since currentChanged is not emitted if
00464    * we close the last tab
00465    */
00466   if ( d->mTabWidget->count() == 0 )
00467   {
00468     emit currentTaskViewChanged();
00469     emit setCaption( QString() );
00470     slotCurrentChanged();
00471   }
00472 
00473   /* hide the tabbar if there's only one tab left */
00474   if ( d->mTabWidget->count() < 2 )
00475   {
00476     showTabBar( false );
00477   }
00478 
00479   delete taskView; // removeTab does not delete its widget.
00480   return true;
00481 }
00482 
00483 void TimetrackerWidget::saveFile()
00484 {
00485   TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00486 
00487   // is it an unsaved file?
00488   if ( d->mIsNewVector.contains( taskView ) )
00489   {
00490     saveCurrentTaskView();
00491   }
00492 
00493   taskView->save();
00494 
00495   // TODO get eventually error message from save and emit
00496   // a error message signal.
00497 }
00498 
00499 void TimetrackerWidget::showTabBar( bool visible )
00500 {
00501   d->mTabWidget->setTabBarHidden( !visible );
00502 }
00503 
00504 void TimetrackerWidget::reconfigureFiles()
00505 {
00506   kDebug(5970) << "Entering function";
00507   TaskView *taskView;
00508   for ( int i = 0; i < d->mTabWidget->count(); ++i )
00509   {
00510     taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00511 
00512     taskView->reconfigure();
00513   }
00514 }
00515 
00516 void TimetrackerWidget::showSearchBar( bool visible )
00517 {
00518   d->mSearchLine->setVisible( visible );
00519 }
00520 
00521 bool TimetrackerWidget::closeAllFiles()
00522 {
00523   kDebug(5970) << "Entering TimetrackerWidget::closeAllFiles";
00524   while ( d->mTabWidget->count() > 0 )
00525   {
00526     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( 0 ) );
00527     d->mTabWidget->setCurrentWidget( taskView );
00528     if ( !( closeFile() ) )
00529       return false;
00530   }
00531   return true;
00532 }
00533 
00534 void TimetrackerWidget::slotCurrentChanged()
00535 {
00536   kDebug() << "entering KTimetrackerWidget::slotCurrentChanged";
00537 
00538   if ( d->mLastView )
00539   {
00540     disconnect( d->mLastView, SIGNAL( totalTimesChanged( long, long ) ) );
00541     disconnect( d->mLastView, SIGNAL( reSetTimes() ) );
00542     disconnect( d->mLastView, SIGNAL( itemSelectionChanged() ) );
00543     disconnect( d->mLastView, SIGNAL( updateButtons() ) );
00544     disconnect( d->mLastView, SIGNAL( setStatusBarText( QString ) ) );
00545     disconnect( d->mLastView, SIGNAL( timersActive() ) );
00546     disconnect( d->mLastView, SIGNAL( timersInactive() ) );
00547     disconnect( d->mLastView, SIGNAL( tasksChanged( const QList< Task* >& ) ),
00548                 this, SIGNAL( tasksChanged( const QList< Task* > & ) ) );
00549   }
00550 
00551   d->mLastView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00552 
00553   if ( d->mLastView )
00554   {
00555     connect( d->mLastView, SIGNAL( totalTimesChanged( long, long ) ),
00556             this, SIGNAL( totalTimesChanged( long, long ) ) );
00557     connect( d->mLastView, SIGNAL( reSetTimes() ),
00558             this, SIGNAL( reSetTimes() ) );
00559     connect( d->mLastView, SIGNAL( itemSelectionChanged() ),
00560             this, SIGNAL( currentTaskChanged() ) );
00561     connect( d->mLastView, SIGNAL( updateButtons() ),
00562             this, SIGNAL( updateButtons() ) );
00563     connect( d->mLastView, SIGNAL( setStatusBarText( QString ) ), // FIXME signature
00564             this, SIGNAL( statusBarTextChangeRequested( const QString & ) ) );
00565     connect( d->mLastView, SIGNAL( timersActive() ),
00566             this, SIGNAL( timersActive() ) );
00567     connect( d->mLastView, SIGNAL( timersInactive() ),
00568             this, SIGNAL( timersInactive() ) );
00569     connect( d->mLastView, SIGNAL( tasksChanged( QList< Task* > ) ), // FIXME signature
00570             this, SIGNAL( tasksChanged( const QList< Task* > &) ) );
00571     emit setCaption( d->mLastView->storage()->icalfile() );
00572   }
00573 
00574   d->mSearchWidget->setEnabled( d->mLastView );
00575 }
00576 
00577 void TimetrackerWidget::updateTabs()
00578 {
00579   kDebug(5970) << "Entering function";
00580   TaskView *taskView;
00581   for ( int i = 0; i < d->mTabWidget->count(); ++i )
00582   {
00583     taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00584 
00585     if ( taskView->activeTasks().count() == 0 )
00586     {
00587       d->mTabWidget->setTabTextColor( i, palette().color( QPalette::Foreground ) );
00588     }
00589     else
00590     {
00591       d->mTabWidget->setTabTextColor( i, Qt::darkGreen );
00592     }
00593   }
00594   kDebug(5970) << "Leaving function";
00595 }
00596 
00597 bool TimetrackerWidget::eventFilter( QObject *obj, QEvent *event )
00598 {
00599   if ( obj == d->mSearchWidget )
00600   {
00601     if ( event->type() == QEvent::KeyPress )
00602     {
00603       QKeyEvent *keyEvent = static_cast< QKeyEvent* >( event );
00604       if ( keyEvent->key() == Qt::Key_Enter ||
00605            keyEvent->key() == Qt::Key_Return )
00606       {
00607         if ( !d->mSearchWidget->displayText().isEmpty() ) slotAddTask( d->mSearchWidget->displayText() );
00608         return true;
00609       }
00610     }
00611   }
00612 
00613   return QObject::eventFilter( obj, event );
00614 }
00615 
00616 void TimetrackerWidget::slotAddTask( const QString &taskName )
00617 {
00618   TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00619 
00620   taskView->addTask( taskName, 0, 0, DesktopList(), 0 );
00621 
00622   d->mSearchWidget->clear();
00623   d->mTabWidget->setFocus( Qt::OtherFocusReason );
00624 }
00625 
00626 void TimetrackerWidget::slotUpdateButtons()
00627 {
00628   kDebug(5970) << "Entering function";
00629   Task *item = currentTask();
00630 
00631   d->mActions[ "start" ]->setEnabled( item && !item->isRunning() &&
00632       !item->isComplete() );
00633   d->mActions[ "stop" ]->setEnabled( item && item->isRunning() );
00634   d->mActions[ "delete_task" ]->setEnabled( item );
00635   d->mActions[ "edit_task" ]->setEnabled( item );
00636   d->mActions[ "mark_as_complete" ]->setEnabled( item && !item->isComplete() );
00637   d->mActions[ "mark_as_incomplete" ]->setEnabled( item && item->isComplete() );
00638 
00639   d->mActions[ "new_task" ]->setEnabled( currentTaskView() );
00640   d->mActions[ "new_sub_task" ]->setEnabled( currentTaskView() && currentTaskView()->count() );
00641   d->mActions[ "focustracking" ]->setEnabled( currentTaskView() );
00642   d->mActions[ "focustracking" ]->setChecked( currentTaskView() &&
00643       currentTaskView()->isFocusTrackingActive() );
00644   d->mActions[ "start_new_session" ]->setEnabled( currentTaskView() );
00645   d->mActions[ "edit_history" ]->setEnabled( currentTaskView() );
00646   d->mActions[ "reset_all_times" ]->setEnabled( currentTaskView() );
00647   d->mActions[ "export_times" ]->setEnabled( currentTaskView() );
00648   d->mActions[ "export_history" ]->setEnabled( currentTaskView() );
00649   d->mActions[ "import_planner" ]->setEnabled( currentTaskView() );
00650   d->mActions[ "file_save" ]->setEnabled( currentTaskView() );
00651   d->mActions[ "file_close" ]->setEnabled( currentTaskView() );
00652   kDebug(5970) << "Leaving function";
00653 }
00654 
00655 void TimetrackerWidget::showSettingsDialog()
00656 {
00657   kDebug(5970) << "Entering function";
00658   /* show main window b/c if this method was started from tray icon and the window
00659      is not visible the application quits after accepting the settings dialog.
00660   */
00661   window()->show();
00662 
00663   KConfigDialog *dialog = new KConfigDialog(
00664     this, "settings", KTimeTrackerSettings::self() );
00665 
00666   Ui::BehaviorPage *behaviorUi = new Ui::BehaviorPage;
00667   QWidget *behaviorPage = new QWidget;
00668   behaviorUi->setupUi( behaviorPage );
00669   dialog->addPage( behaviorPage, i18n( "Behavior" ), "preferences-other" );
00670 
00671   Ui::DisplayPage *displayUi = new Ui::DisplayPage;
00672   QWidget *displayPage = new QWidget;
00673   displayUi->setupUi( displayPage );
00674   dialog->addPage( displayPage,
00675                    i18nc( "settings page for customizing user interface",
00676                           "Appearance" ),
00677                           "preferences-desktop-theme" );
00678 
00679   Ui::StoragePage *storageUi = new Ui::StoragePage;
00680   QWidget *storagePage = new QWidget;
00681   storageUi->setupUi( storagePage );
00682   dialog->addPage( storagePage, i18n( "Storage" ), "system-file-manager" );
00683 
00684   dialog->exec();
00685   showSearchBar( !KTimeTrackerSettings::configPDA() );
00686   reconfigureFiles();
00687 }
00688 
00689 //BEGIN wrapper slots
00690 void TimetrackerWidget::startCurrentTimer()
00691 {
00692   if ( d->mTabWidget->currentWidget() ) {
00693     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->startCurrentTimer();
00694   }
00695 }
00696 
00697 void TimetrackerWidget::stopCurrentTimer()
00698 {
00699   if ( d->mTabWidget->currentWidget() ) {
00700     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->stopCurrentTimer();
00701   }
00702 }
00703 
00704 void TimetrackerWidget::stopAllTimers( const QDateTime &when )
00705 {
00706   if ( d->mTabWidget->currentWidget() ) {
00707     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->stopAllTimers( when );
00708   }
00709 }
00710 
00711 void TimetrackerWidget::newTask()
00712 {
00713   if ( d->mTabWidget->currentWidget() ) {
00714     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->newTask();
00715   }
00716 }
00717 
00718 void TimetrackerWidget::newSubTask()
00719 {
00720   if ( d->mTabWidget->currentWidget() ) {
00721     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->newSubTask();
00722   }
00723 }
00724 
00725 void TimetrackerWidget::editTask()
00726 {
00727   if ( d->mTabWidget->currentWidget() ) {
00728     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->editTask();
00729   }
00730 }
00731 
00732 void TimetrackerWidget::deleteTask()
00733 {
00734   if ( d->mTabWidget->currentWidget() ) {
00735     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->deleteTask();
00736   }
00737 }
00738 
00739 void TimetrackerWidget::markTaskAsComplete()
00740 {
00741   if ( d->mTabWidget->currentWidget() ) {
00742     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->markTaskAsComplete();
00743   }
00744 }
00745 
00746 void TimetrackerWidget::markTaskAsIncomplete()
00747 {
00748   if ( d->mTabWidget->currentWidget() ) {
00749     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->markTaskAsIncomplete();
00750   }
00751 }
00752 
00753 void TimetrackerWidget::exportcsvFile()
00754 {
00755   if ( d->mTabWidget->currentWidget() ) {
00756     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->exportcsvFile();
00757   }
00758 }
00759 
00760 void TimetrackerWidget::exportcsvHistory()
00761 {
00762   if ( d->mTabWidget->currentWidget() ) {
00763     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->exportcsvHistory();
00764   }
00765 }
00766 
00767 void TimetrackerWidget::importPlanner( const QString &fileName )
00768 {
00769   if ( d->mTabWidget->currentWidget() ) {
00770     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->importPlanner( fileName );
00771   }
00772 }
00773 
00774 void TimetrackerWidget::startNewSession()
00775 {
00776   if ( d->mTabWidget->currentWidget() ) {
00777     qobject_cast< TaskView* >( d->mTabWidget->currentWidget() )->startNewSession();
00778   }
00779 }
00780 
00781 void TimetrackerWidget::editHistory()
00782 {
00783   if ( d->mTabWidget->currentWidget() )
00784   {
00785     EditHistoryDialog *dlg = new EditHistoryDialog( qobject_cast< TaskView* >( d->mTabWidget->currentWidget() ) );
00786     if (currentTaskView()->storage()->rawevents().count()!=0) dlg->exec();
00787     else KMessageBox::information(0, i18nc("@info in message box", "There is no history yet. Start and stop a task and you will have an entry in your history."));
00788   }
00789 }
00790 
00791 void TimetrackerWidget::resetAllTimes()
00792 {
00793   if ( d->mTabWidget->currentWidget() ) {
00794     if ( KMessageBox::warningContinueCancel( this,
00795          i18n( "Do you really want to reset the time to zero for all tasks?" ),
00796          i18n( "Confirmation Required" ), KGuiItem( i18n( "Reset All Times" ) ) ) == KMessageBox::Continue )
00797       currentTaskView()->resetTimeForAllTasks();
00798   }
00799 }
00800 
00801 void TimetrackerWidget::focusTracking()
00802 {
00803   if ( d->mTabWidget->currentWidget() ) {
00804     currentTaskView()->toggleFocusTracking();
00805     d->mActions[ "focustracking" ]->setChecked(
00806       currentTaskView()->isFocusTrackingActive() );
00807   }
00808 }
00809 
00810 void TimetrackerWidget::slotSearchBar()
00811 {
00812   bool currentVisible = KTimeTrackerSettings::self()->showSearchBar();
00813   KTimeTrackerSettings::self()->setShowSearchBar( !currentVisible );
00814   d->mActions[ "searchbar" ]->setChecked( !currentVisible );
00815   showSearchBar( !currentVisible );
00816 }
00817 //END
00818 
00819 //BEGIN dbus slots
00820 QString TimetrackerWidget::version() const
00821 {
00822   return KTIMETRACKER_VERSION;
00823 }
00824 
00825 QStringList TimetrackerWidget::taskIdsFromName( const QString &taskName ) const
00826 {
00827   QStringList result;
00828 
00829   for ( int i = 0; i < d->mTabWidget->count(); ++i) {
00830     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00831 
00832     if ( !taskView ) continue;
00833 
00834     QTreeWidgetItemIterator it( taskView );
00835     while ( *it ) {
00836       Task *task = static_cast< Task* >( *it );
00837       if ( task && task->name() == taskName ) {
00838         result << task->uid();
00839       }
00840       ++it;
00841     }
00842   }
00843 
00844   return result;
00845 }
00846 
00847 void TimetrackerWidget::addTask( const QString &taskName )
00848 {
00849   TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00850 
00851   if ( taskView )
00852   {
00853     taskView->addTask( taskName, 0, 0, DesktopList(), 0 );
00854   }
00855 }
00856 
00857 void TimetrackerWidget::addSubTask( const QString& taskName, const QString &taskId )
00858 {
00859   TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->currentWidget() );
00860 
00861   if ( taskView )
00862   {
00863     taskView->addTask( taskName, 0, 0, DesktopList(), taskView->task( taskId) );
00864     taskView->refresh();
00865   }
00866 }
00867 
00868 void TimetrackerWidget::deleteTask( const QString &taskId )
00869 {
00870   for ( int i = 0; i < d->mTabWidget->count(); ++i)
00871   {
00872     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00873 
00874     if ( !taskView ) continue;
00875 
00876     QTreeWidgetItemIterator it( taskView );
00877     while ( *it )
00878     {
00879       Task *task = static_cast< Task* >( *it );
00880       if ( task && task->uid() == taskId )
00881       {
00882         taskView->deleteTaskBatch( task );
00883       }
00884       ++it;
00885     }
00886   }
00887 }
00888 
00889 void TimetrackerWidget::setPercentComplete( const QString &taskId, int percent )
00890 {
00891   for ( int i = 0; i < d->mTabWidget->count(); ++i) {
00892     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00893 
00894     if ( !taskView ) continue;
00895 
00896     QTreeWidgetItemIterator it( taskView );
00897     while ( *it ) {
00898       Task *task = static_cast< Task* >( *it );
00899       if ( task && task->uid() == taskId ) {
00900         task->setPercentComplete( percent, taskView->storage() );
00901       }
00902       ++it;
00903     }
00904   }
00905 }
00906 
00907 int TimetrackerWidget::bookTime( const QString &taskId, const QString &dateTime, int minutes )
00908 {
00909   QDate startDate;
00910   QTime startTime;
00911   QDateTime startDateTime;
00912   Task *task = 0, *t = 0;
00913 
00914   if ( minutes <= 0 ) return KTIMETRACKER_ERR_INVALID_DURATION;
00915 
00916   // Find task
00917   for ( int i = 0; i < d->mTabWidget->count(); ++i ) {
00918     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00919 
00920     if ( !taskView ) continue;
00921 
00922     QTreeWidgetItemIterator it( taskView );
00923     while ( *it ) {
00924       t = static_cast< Task* >( *it );
00925       if ( t && t->uid() == taskId ) {
00926         task = t;
00927         break;
00928       }
00929       ++it;
00930     }
00931 
00932     if ( task ) break;
00933   }
00934 
00935   if ( !task ) return KTIMETRACKER_ERR_UID_NOT_FOUND;
00936 
00937   // Parse datetime
00938   startDate = QDate::fromString( dateTime, Qt::ISODate );
00939 
00940   if ( dateTime.length() > 10 ) { // "YYYY-MM-DD".length() = 10
00941     startTime = QTime::fromString( dateTime, Qt::ISODate );
00942   } else startTime = QTime( 12, 0 );
00943 
00944   if ( startDate.isValid() && startTime.isValid() ) {
00945     startDateTime = QDateTime( startDate, startTime );
00946   } else return KTIMETRACKER_ERR_INVALID_DATE;
00947 
00948   // Update task totals (session and total) and save to disk
00949   task->changeTotalTimes( task->sessionTime() + minutes,
00950                           task->totalTime() + minutes );
00951   if ( !( task->taskView()->storage()->bookTime( task,
00952                                                  startDateTime,
00953                                                  minutes * 60 ) ) )
00954     return KTIMETRACKER_ERR_GENERIC_SAVE_FAILED;
00955 
00956   return 0;
00957 }
00958 
00959 QString TimetrackerWidget::error( int errorCode ) const
00960 {
00961   switch ( errorCode ) {
00962     case KTIMETRACKER_ERR_GENERIC_SAVE_FAILED:
00963       return i18n( "Save failed, most likely because the file could not be locked." );
00964     case KTIMETRACKER_ERR_COULD_NOT_MODIFY_RESOURCE:
00965       return i18n( "Could not modify calendar resource." );
00966     case KTIMETRACKER_ERR_MEMORY_EXHAUSTED:
00967       return i18n( "Out of memory--could not create object." );
00968     case KTIMETRACKER_ERR_UID_NOT_FOUND:
00969       return i18n( "UID not found." );
00970     case KTIMETRACKER_ERR_INVALID_DATE:
00971       return i18n( "Invalidate date--format is YYYY-MM-DD." );
00972     case KTIMETRACKER_ERR_INVALID_TIME:
00973       return i18n( "Invalid time--format is YYYY-MM-DDTHH:MM:SS." );
00974     case KTIMETRACKER_ERR_INVALID_DURATION:
00975       return i18n( "Invalid task duration--must be greater than zero." );
00976     default:
00977       return i18n( "Invalid error number: %1", errorCode );
00978   }
00979 }
00980 
00981 int TimetrackerWidget::totalMinutesForTaskId( const QString &taskId ) const
00982 {
00983   for ( int i = 0; i < d->mTabWidget->count(); ++i ) {
00984     TaskView *taskView = qobject_cast< TaskView* >( d->mTabWidget->widget( i ) );
00985 
00986     if ( !taskView ) continue;
00987 
00988     QTreeWidgetItemIterator it( taskView );
00989     while ( *it ) {
00990       Task *task =