00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "todosummarywidget.h"
00027 #include "todoplugin.h"
00028 #include "korganizerinterface.h"
00029
00030 #include <korganizer/stdcalendar.h>
00031 #include <korganizer/koglobals.h>
00032 #include <kontactinterfaces/core.h>
00033
00034 #include <libkdepim/kpimprefs.h>
00035
00036 #include <kcal/calendar.h>
00037 #include <kcal/resourcecalendar.h>
00038 #include <kcal/resourcelocal.h>
00039 #include <kcal/todo.h>
00040 #include <kcal/incidenceformatter.h>
00041
00042 #include <kdialog.h>
00043 #include <kglobal.h>
00044 #include <kicon.h>
00045 #include <kiconloader.h>
00046 #include <klocale.h>
00047 #include <kmenu.h>
00048 #include <kstandarddirs.h>
00049 #include <kurllabel.h>
00050 #include <kparts/part.h>
00051
00052 #include <QCursor>
00053 #include <QEvent>
00054 #include <QGridLayout>
00055 #include <QLabel>
00056 #include <QLayout>
00057 #include <QPixmap>
00058 #include <QVBoxLayout>
00059 #include <QTextDocument>
00060
00061 TodoSummaryWidget::TodoSummaryWidget( TodoPlugin *plugin, QWidget *parent )
00062 : Kontact::Summary( parent ), mPlugin( plugin )
00063 {
00064 QVBoxLayout *mainLayout = new QVBoxLayout( this );
00065 mainLayout->setSpacing( 3 );
00066 mainLayout->setMargin( 3 );
00067
00068 QWidget *header = createHeader( this, "view-pim-tasks", i18n( "Pending To-dos" ) );
00069 mainLayout->addWidget( header );
00070
00071 mLayout = new QGridLayout();
00072 mainLayout->addItem( mLayout );
00073 mLayout->setSpacing( 3 );
00074 mLayout->setRowStretch( 6, 1 );
00075
00076 mCalendar = KOrg::StdCalendar::self();
00077 mCalendar->load();
00078
00079 connect( mCalendar, SIGNAL(calendarChanged()), SLOT(updateView()) );
00080 connect( mPlugin->core(), SIGNAL(dayChanged(const QDate&)), SLOT(updateView()) );
00081
00082 updateView();
00083 }
00084
00085 TodoSummaryWidget::~TodoSummaryWidget()
00086 {
00087 }
00088
00089 void TodoSummaryWidget::updateView()
00090 {
00091 qDeleteAll( mLabels );
00092 mLabels.clear();
00093
00094 KConfig config( "kcmtodosummaryrc" );
00095 KConfigGroup daysGroup( &config, "Days" );
00096 int mDaysToGo = daysGroup.readEntry( "DaysToShow", 7 );
00097
00098 KConfigGroup hideGroup( &config, "Hide" );
00099 mHideInProgress = hideGroup.readEntry( "InProgress", false );
00100 mHideOverdue = hideGroup.readEntry( "Overdue", false );
00101 mHideCompleted = hideGroup.readEntry( "Completed", true );
00102 mHideOpenEnded = hideGroup.readEntry( "OpenEnded", true );
00103 mHideNotStarted = hideGroup.readEntry( "NotStarted", false );
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117 KCal::Todo::List prList;
00118
00119 QDate currDate = QDate::currentDate();
00120 Q_FOREACH ( KCal::Todo *todo, mCalendar->todos() ) {
00121 if ( todo->hasDueDate() ) {
00122 int daysTo = currDate.daysTo( todo->dtDue().date() );
00123 if ( daysTo >= mDaysToGo ) {
00124 continue;
00125 }
00126 }
00127
00128 if ( mHideOverdue && overdue( todo ) ) {
00129 continue;
00130 }
00131 if ( mHideInProgress && inProgress( todo ) ) {
00132 continue;
00133 }
00134 if ( mHideCompleted && completed( todo ) ) {
00135 continue;
00136 }
00137 if ( mHideOpenEnded && openEnded( todo ) ) {
00138 continue;
00139 }
00140 if ( mHideNotStarted && notStarted( todo ) ) {
00141 continue;
00142 }
00143
00144 prList.append( todo );
00145 }
00146 if ( !prList.isEmpty() ) {
00147 prList = KCal::Calendar::sortTodos( &prList,
00148 KCal::TodoSortSummary,
00149 KCal::SortDirectionAscending );
00150 prList = KCal::Calendar::sortTodos( &prList,
00151 KCal::TodoSortPriority,
00152 KCal::SortDirectionAscending );
00153 prList = KCal::Calendar::sortTodos( &prList,
00154 KCal::TodoSortDueDate,
00155 KCal::SortDirectionAscending );
00156 }
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174 int counter = 0;
00175 QLabel *label = 0;
00176
00177 if ( !prList.isEmpty() ) {
00178
00179 KIconLoader loader( "korganizer" );
00180 QPixmap pm = loader.loadIcon( "view-calendar-tasks", KIconLoader::Small );
00181
00182 QString str;
00183
00184 Q_FOREACH ( KCal::Todo *todo, prList ) {
00185 bool makeBold = false;
00186 int daysTo = -1;
00187
00188
00189 label = new QLabel( this );
00190 label->setPixmap( pm );
00191 label->setMaximumWidth( label->minimumSizeHint().width() );
00192 mLayout->addWidget( label, counter, 0 );
00193 mLabels.append( label );
00194
00195
00196 str = "";
00197 if ( todo->hasDueDate() && todo->dtDue().date().isValid() ) {
00198 daysTo = currDate.daysTo( todo->dtDue().date() );
00199
00200 if ( daysTo == 0 ) {
00201 makeBold = true;
00202 str = i18n( "Today" );
00203 } else if ( daysTo == 1 ) {
00204 str = i18n( "Tomorrow" );
00205 } else {
00206 str = KGlobal::locale()->formatDate( todo->dtDue().date(), KLocale::FancyLongDate );
00207 }
00208 }
00209
00210 label = new QLabel( str, this );
00211 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
00212 mLayout->addWidget( label, counter, 1 );
00213 mLabels.append( label );
00214 if ( makeBold ) {
00215 QFont font = label->font();
00216 font.setBold( true );
00217 label->setFont( font );
00218 }
00219
00220
00221 str = "";
00222 if ( todo->hasDueDate() && todo->dtDue().date().isValid() ) {
00223 if ( daysTo > 0 ) {
00224 str = i18np( "in 1 day", "in %1 days", daysTo );
00225 } else if ( daysTo < 0 ) {
00226 str = i18np( "1 day ago", "%1 days ago", -daysTo );
00227 } else {
00228 str = i18n( "due" );
00229 }
00230 }
00231 label = new QLabel( str, this );
00232 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
00233 mLayout->addWidget( label, counter, 2 );
00234 mLabels.append( label );
00235
00236
00237 str = '[' + QString::number( todo->priority() ) + ']';
00238 label = new QLabel( str, this );
00239 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
00240 mLayout->addWidget( label, counter, 3 );
00241 mLabels.append( label );
00242
00243
00244 str = todo->summary();
00245 if ( todo->relatedTo() ) {
00246 str = todo->relatedTo()->summary() + ':' + str;
00247 }
00248 if ( !Qt::mightBeRichText( str ) ) {
00249 str = Qt::escape( str );
00250 }
00251
00252 KUrlLabel *urlLabel = new KUrlLabel( this );
00253 urlLabel->setText( str );
00254 urlLabel->setUrl( todo->uid() );
00255 urlLabel->installEventFilter( this );
00256 urlLabel->setTextFormat( Qt::RichText );
00257 mLayout->addWidget( urlLabel, counter, 4 );
00258 mLabels.append( urlLabel );
00259
00260 connect( urlLabel, SIGNAL(leftClickedUrl(const QString&)),
00261 this, SLOT(viewTodo(const QString&)) );
00262 connect( urlLabel, SIGNAL(rightClickedUrl(const QString&)),
00263 this, SLOT(popupMenu(const QString&)) );
00264
00265 QString tipText( KCal::IncidenceFormatter::toolTipString( todo, true ) );
00266 if ( !tipText.isEmpty() ) {
00267 urlLabel->setToolTip( tipText );
00268 }
00269
00270
00271 str = stateStr( todo );
00272 label = new QLabel( str, this );
00273 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
00274 mLayout->addWidget( label, counter, 5 );
00275 mLabels.append( label );
00276
00277 counter++;
00278 }
00279 }
00280
00281 if ( counter == 0 ) {
00282 QLabel *noTodos = new QLabel(
00283 i18np( "No pending to-dos due within the next day",
00284 "No pending to-dos due within the next %1 days",
00285 mDaysToGo ), this );
00286 noTodos->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter );
00287 mLayout->addWidget( noTodos, 0, 2 );
00288 mLabels.append( noTodos );
00289 }
00290
00291 Q_FOREACH( label, mLabels ) {
00292 label->show();
00293 }
00294 }
00295
00296 void TodoSummaryWidget::viewTodo( const QString &uid )
00297 {
00298 mPlugin->core()->selectPlugin( "kontact_todoplugin" );
00299 OrgKdeKorganizerKorganizerInterface korganizer(
00300 "org.kde.korganizer", "/Korganizer", QDBusConnection::sessionBus() );
00301 korganizer.editIncidence( uid );
00302 }
00303
00304 void TodoSummaryWidget::removeTodo( const QString &uid )
00305 {
00306 mPlugin->core()->selectPlugin( "kontact_todoplugin" );
00307 OrgKdeKorganizerKorganizerInterface korganizer(
00308 "org.kde.korganizer", "/Korganizer", QDBusConnection::sessionBus() );
00309 korganizer.deleteIncidence( uid, false );
00310 }
00311
00312 void TodoSummaryWidget::completeTodo( const QString &uid )
00313 {
00314 KCal::Todo *todo = mCalendar->todo( uid );
00315 if ( !todo->isReadOnly() ) {
00316 todo->setCompleted( KDateTime::currentLocalDateTime() );
00317 mCalendar->save();
00318 updateView();
00319 }
00320 }
00321
00322 void TodoSummaryWidget::popupMenu( const QString &uid )
00323 {
00324 KMenu popup( this );
00325 QAction *editIt = popup.addAction( i18n( "&Edit To-do..." ) );
00326 QAction *delIt = popup.addAction( i18n( "&Delete To-do" ) );
00327 delIt->setIcon( KIconLoader::global()->loadIcon( "edit-delete", KIconLoader::Small ) );
00328 QAction *doneIt = 0;
00329 KCal::Todo *todo = mCalendar->todo( uid );
00330 if ( !todo->isCompleted() ) {
00331 doneIt = popup.addAction( i18n( "&Mark To-do Completed" ) );
00332 doneIt->setIcon( KIconLoader::global()->loadIcon( "task-complete", KIconLoader::Small ) );
00333 }
00334
00335
00336 const QAction *selectedAction = popup.exec( QCursor::pos() );
00337 if ( selectedAction == editIt ) {
00338 viewTodo( uid );
00339 } else if ( selectedAction == delIt ) {
00340 removeTodo( uid );
00341 } else if ( doneIt && selectedAction == doneIt ) {
00342 completeTodo( uid );
00343 }
00344 }
00345
00346 bool TodoSummaryWidget::eventFilter( QObject *obj, QEvent *e )
00347 {
00348 if ( obj->inherits( "KUrlLabel" ) ) {
00349 KUrlLabel* label = static_cast<KUrlLabel*>( obj );
00350 if ( e->type() == QEvent::Enter ) {
00351 emit message( i18n( "Edit To-do: \"%1\"", label->text() ) );
00352 }
00353 if ( e->type() == QEvent::Leave ) {
00354 emit message( QString::null );
00355 }
00356 }
00357 return Kontact::Summary::eventFilter( obj, e );
00358 }
00359
00360 QStringList TodoSummaryWidget::configModules() const
00361 {
00362 return QStringList( "kcmtodosummary.desktop" );
00363 }
00364
00365 bool TodoSummaryWidget::overdue( KCal::Todo *todo )
00366 {
00367 if ( todo->hasDueDate() && !todo->isCompleted() &&
00368 todo->dtDue().date() < QDate::currentDate() ) {
00369 return true;
00370 }
00371 return false;
00372 }
00373
00374 bool TodoSummaryWidget::starts( KCal::Todo *todo )
00375 {
00376 if ( todo->hasStartDate() &&
00377 todo->dtStart().date() == QDate::currentDate() ) {
00378 return true;
00379 }
00380 return false;
00381 }
00382
00383 bool TodoSummaryWidget::completed( KCal::Todo *todo )
00384 {
00385 return todo->isCompleted();
00386 }
00387
00388 bool TodoSummaryWidget::openEnded( KCal::Todo *todo )
00389 {
00390 if ( !todo->hasDueDate() && !todo->isCompleted() ) {
00391 return true;
00392 }
00393 return false;
00394 }
00395
00396 bool TodoSummaryWidget::inProgress( KCal::Todo *todo )
00397 {
00398 if ( todo->percentComplete() > 0 ) {
00399 return true;
00400 }
00401
00402 QDate currDate = QDate::currentDate();
00403 if ( todo->hasStartDate() && todo->hasDueDate() &&
00404 todo->dtStart().date() < currDate &&
00405 currDate < todo->dtDue().date() ) {
00406 return true;
00407 }
00408
00409 return false;
00410 }
00411
00412 bool TodoSummaryWidget::notStarted( KCal::Todo *todo )
00413 {
00414 if ( todo->percentComplete() > 0 ) {
00415 return false;
00416 }
00417
00418 if ( !todo->hasStartDate() ) {
00419 return false;
00420 }
00421
00422 if ( todo->dtStart().date() >= QDate::currentDate() ) {
00423 return false;
00424 }
00425
00426 return true;
00427 }
00428
00429 const QString TodoSummaryWidget::stateStr( KCal::Todo *todo )
00430 {
00431 QString str1, str2;
00432
00433 if ( openEnded( todo ) ) {
00434 str1 = i18n( "open-ended" );
00435 } else if ( overdue( todo ) ) {
00436 str1 = "<font color=\"red\">" + i18n( "overdue" ) + "</font>";
00437 } else if ( starts( todo ) ) {
00438 str1 = i18n( "starts today" );
00439 }
00440
00441 if ( notStarted( todo ) ) {
00442 str2 += i18n( "not-started" );
00443 } else if ( completed( todo ) ) {
00444 str2 += i18n( "completed" );
00445 } else if ( inProgress( todo ) ) {
00446 str2 += i18n( "in-progress " );
00447 str2 += " (" + QString::number( todo->percentComplete() ) + "%)";
00448 }
00449
00450 if ( !str1.isEmpty() && !str2.isEmpty() ) {
00451 str1 += i18nc( "Separator for status like this: overdue, completed", "," );
00452 }
00453
00454 return str1 + str2;
00455 }
00456
00457 #include "todosummarywidget.moc"