diff --git a/KdepimLibsConfig.cmake.in b/KdepimLibsConfig.cmake.in index 09e2a53a7..64bed9ffd 100644 --- a/KdepimLibsConfig.cmake.in +++ b/KdepimLibsConfig.cmake.in @@ -1,87 +1,87 @@ # KdepimLibs.cmake is generated by CMake from kdepimlibs/KdepimLibs.cmake.in # set the kdepimlibs version number set(KDEPIMLIBS_VERSION_MAJOR @KDEPIMLIBS_VERSION_MAJOR@) set(KDEPIMLIBS_VERSION_MINOR @KDEPIMLIBS_VERSION_MINOR@) set(KDEPIMLIBS_VERSION_PATCH @KDEPIMLIBS_VERSION_PATCH@) set(KDEPIMLIBS_VERSION "${KDEPIMLIBS_VERSION_MAJOR}.${KDEPIMLIBS_VERSION_MINOR}.${KDEPIMLIBS_VERSION_PATCH}") # set the directories if(NOT KDEPIMLIBS_INSTALL_DIR) set(KDEPIMLIBS_INSTALL_DIR "@CMAKE_INSTALL_PREFIX@") endif(NOT KDEPIMLIBS_INSTALL_DIR) set(KDEPIMLIBS_DATA_DIR "@KDEPIMLIBS_DATA_DIR@") set(KDEPIMLIBS_DBUS_INTERFACES_DIR "@KDEPIMLIBS_DBUS_INTERFACES_DIR@") set(KDEPIMLIBS_DBUS_SERVICES_DIR "@KDEPIMLIBS_DBUS_SERVICES_DIR@") set(KDEPIMLIBS_INCLUDE_DIR "@KDEPIMLIBS_INCLUDE_DIR@") set(KDEPIMLIBS_INCLUDE_DIRS "@KDEPIMLIBS_INCLUDE_DIR@" "@KDEPIMLIBS_INCLUDE_DIR@/KDE") set(KDEPIMLIBS_LIB_DIR "@KDEPIMLIBS_LIB_DIR@") set(KDEPIMLIBS_BIN_DIR "@KDEPIMLIBS_BIN_DIR@") set(KDEPIMLIBS_LIBEXEC_DIR "@KDEPIMLIBS_LIBEXEC_DIR@") set(KDEPIMLIBS_SBIN_DIR "@KDEPIMLIBS_SBIN_DIR@") set(KDEPIMLIBS_HTML_DIR "@KDEPIMLIBS_HTML_DIR@") set(KDEPIMLIBS_CONFIG_DIR "@KDEPIMLIBS_CONFIG_DIR@") set(KDEPIMLIBS_ICON_DIR "@KDEPIMLIBS_ICON_DIR@") set(KDEPIMLIBS_KCFG_DIR "@KDEPIMLIBS_KCFG_DIR@") set(KDEPIMLIBS_LOCALE_DIR "@KDEPIMLIBS_LOCALE_DIR@") set(KDEPIMLIBS_MIME_DIR "@KDEPIMLIBS_MIME_DIR@") set(KDEPIMLIBS_SOUND_DIR "@KDEPIMLIBS_SOUND_DIR@") set(KDEPIMLIBS_TEMPLATES_DIR "@KDEPIMLIBS_TEMPLATES_DIR@") set(KDEPIMLIBS_KCONF_UPDATE_DIR "@KDEPIMLIBS_KCONF_UPDATE_DIR@") set(KDEPIMLIBS_AUTOSTART_DIR "@KDEPIMLIBS_AUTOSTART_DIR@") set(KDEPIMLIBS_XDG_APPS_DIR "@KDEPIMLIBS_XDG_APPS_DIR@") set(KDEPIMLIBS_XDG_DIRECTORY_DIR "@KDEPIMLIBS_XDG_DIRECTORY_DIR@") set(KDEPIMLIBS_SYSCONF_DIR "@KDEPIMLIBS_SYSCONF_DIR@") set(KDEPIMLIBS_MAN_DIR "@KDEPIMLIBS_MAN_DIR@") set(KDEPIMLIBS_INFO_DIR "@KDEPIMLIBS_INFO_DIR@") set(KDEPIMLIBS_SERVICES_DIR "@KDEPIMLIBS_SERVICES_DIR@") set(KDEPIMLIBS_SERVICETYPES_DIR "@KDEPIMLIBS_SERVICETYPES_DIR@") # These two are just for compatibility with KDE 4.[01] set(KDEPIMLIBS4_DBUS_INTERFACES_DIR "${KDEPIMLIBS_DBUS_INTERFACES_DIR}") set(KDEPIMLIBS4_DBUS_SERVICES_DIR "${KDEPIMLIBS_DBUS_SERVICES_DIR}") -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${KDEPIMLIBS_DATA_DIR}/cmake/modules") +set(CMAKE_MODULE_PATH "${KDEPIMLIBS_DATA_DIR}/cmake/modules" "${CMAKE_MODULE_PATH}" ) # the exports file exports set(KDEPIMLIBS_TARGET_PREFIX @KDEPIMLIBS_TARGET_PREFIX@) # Make sure to load the exported targets only once # For the rest of this script it doesn't matter that much if(NOT TARGET KDEPIMLibs__kresources) get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${_currentDir}/KDEPimLibsLibraryTargetsWithPrefix.cmake") endif(NOT TARGET KDEPIMLibs__kresources) macro(_KDEPIMLibs_Set_Lib_Vars _prefix _lib) set(KDEPIMLIBS_${_prefix}_LIBRARY ${KDEPIMLIBS_TARGET_PREFIX}${_lib}) set(KDEPIMLIBS_${_prefix}_LIBS ${KDEPIMLIBS_TARGET_PREFIX}${_lib}) # these two are set for compatibility with KDE 4.[01], Alex: set(KDE4_${_prefix}_LIBRARY ${KDEPIMLIBS_TARGET_PREFIX}${_lib}) set(KDE4_${_prefix}_LIBS ${KDEPIMLIBS_TARGET_PREFIX}${_lib}) endmacro(_KDEPIMLibs_Set_Lib_Vars) _kdepimlibs_set_lib_vars(AKONADI akonadi-kde) _kdepimlibs_set_lib_vars(AKONADI_KMIME akonadi-kmime) _kdepimlibs_set_lib_vars(AKONADI_KABC akonadi-kabc) _kdepimlibs_set_lib_vars(GPGMEPP gpgmepp) _kdepimlibs_set_lib_vars(KABC kabc) _kdepimlibs_set_lib_vars(KBLOG kblog) _kdepimlibs_set_lib_vars(KCAL kcal) _kdepimlibs_set_lib_vars(KHOLIDAYS kholidays) _kdepimlibs_set_lib_vars(KIMAP kimap) _kdepimlibs_set_lib_vars(KLDAP kldap) _kdepimlibs_set_lib_vars(KMIME kmime) _kdepimlibs_set_lib_vars(KPIMIDENTITIES kpimidentities) _kdepimlibs_set_lib_vars(KPIMTEXTEDIT kpimtextedit) _kdepimlibs_set_lib_vars(KPIMUTILS kpimutils) _kdepimlibs_set_lib_vars(KRESOURCES kresources) _kdepimlibs_set_lib_vars(KTNEF ktnef) _kdepimlibs_set_lib_vars(KXMLRPCCLIENT kxmlrpcclient) _kdepimlibs_set_lib_vars(MAILTRANSPORT mailtransport) _kdepimlibs_set_lib_vars(MICROBLOG microblog) _kdepimlibs_set_lib_vars(QGPGME qgpgme) _kdepimlibs_set_lib_vars(SYNDICATION syndication) diff --git a/akonadi/standardactionmanager.cpp b/akonadi/standardactionmanager.cpp index b088c561c..46276a80a 100644 --- a/akonadi/standardactionmanager.cpp +++ b/akonadi/standardactionmanager.cpp @@ -1,422 +1,451 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "standardactionmanager.h" #include "agentmanager.h" #include "collectioncreatejob.h" #include "collectiondeletejob.h" #include "collectionmodel.h" #include "collectionutils_p.h" #include "collectionpropertiesdialog.h" #include "itemdeletejob.h" #include "itemmodel.h" #include "pastehelper_p.h" #include "subscriptiondialog_p.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; //@cond PRIVATE static const struct { const char *name; const char *label; const char *icon; int shortcut; const char* slot; } actionData[] = { { "akonadi_collection_create", I18N_NOOP("&New Folder..."), "folder-new", 0, SLOT(slotCreateCollection()) }, { "akonadi_collection_copy", 0, "edit-copy", 0, SLOT(slotCopyCollections()) }, { "akonadi_collection_delete", I18N_NOOP("&Delete Folder"), "edit-delete", 0, SLOT(slotDeleteCollection()) }, { "akonadi_collection_sync", I18N_NOOP("&Synchronize Folder"), "view-refresh", Qt::Key_F5, SLOT(slotSynchronizeCollection()) }, { "akonadi_collection_properties", I18N_NOOP("Folder &Properties"), "configure", 0, SLOT(slotCollectionProperties()) }, { "akonadi_item_copy", 0, "edit-copy", 0, SLOT(slotCopyItems()) }, { "akonadi_paste", I18N_NOOP("&Paste"), "edit-paste", Qt::CTRL + Qt::Key_V, SLOT(slotPaste()) }, { "akonadi_item_delete", 0, "edit-delete", Qt::Key_Delete, SLOT(slotDeleteItems()) }, - { "akonadi_manage_local_subscriptions", I18N_NOOP("Manage Local &Subscriptions..."), 0, 0, SLOT(slotLocalSubscription()) } + { "akonadi_manage_local_subscriptions", I18N_NOOP("Manage Local &Subscriptions..."), 0, 0, SLOT(slotLocalSubscription()) }, + { "akonadi_collection_add_to_favorites", I18N_NOOP("Add to Favorite Folders"), "bookmark-new", 0, SLOT(slotAddToFavorites()) } }; static const int numActionData = sizeof actionData / sizeof *actionData; BOOST_STATIC_ASSERT( numActionData == StandardActionManager::LastType ); static bool canCreateCollection( const Collection &collection ) { if ( !( collection.rights() & Collection::CanCreateCollection ) ) return false; if ( !collection.contentMimeTypes().contains( Collection::mimeType() ) ) return false; return true; } /** * @internal */ class StandardActionManager::Private { public: Private( StandardActionManager *parent ) : q( parent ), collectionSelectionModel( 0 ), - itemSelectionModel( 0 ) + itemSelectionModel( 0 ), + favoritesModel( 0 ) { actions.fill( 0, StandardActionManager::LastType ); pluralLabels.insert( StandardActionManager::CopyCollections, ki18np( "&Copy Folder", "&Copy %1 Folders" ) ); pluralLabels.insert( StandardActionManager::CopyItems, ki18np( "&Copy Item", "&Copy %1 Items" ) ); pluralLabels.insert( StandardActionManager::DeleteItems, ki18np( "&Delete Item", "&Delete %1 Items" ) ); } void enableAction( StandardActionManager::Type type, bool enable ) { Q_ASSERT( type >= 0 && type < StandardActionManager::LastType ); if ( actions[type] ) actions[type]->setEnabled( enable ); } void updatePluralLabel( StandardActionManager::Type type, int count ) { Q_ASSERT( type >= 0 && type < StandardActionManager::LastType ); if ( actions[type] && pluralLabels.contains( type ) && !pluralLabels.value( type ).isEmpty() ) { actions[type]->setText( pluralLabels.value( type ).subs( qMax( count, 1 ) ).toString() ); } } void copy( QItemSelectionModel* selModel ) { Q_ASSERT( selModel ); if ( selModel->selectedRows().count() <= 0 ) return; QMimeData *mimeData = selModel->model()->mimeData( selModel->selectedRows() ); QApplication::clipboard()->setMimeData( mimeData ); } void updateActions() { bool singleColSelected = false; bool multiColSelected = false; int colCount = 0; QModelIndex selectedIndex; if ( collectionSelectionModel ) { colCount = collectionSelectionModel->selectedRows().count(); singleColSelected = colCount == 1; multiColSelected = colCount > 0; if ( singleColSelected ) selectedIndex = collectionSelectionModel->selectedRows().first(); } enableAction( CopyCollections, multiColSelected ); enableAction( CollectionProperties, singleColSelected ); Collection col; if ( singleColSelected && selectedIndex.isValid() ) { col = selectedIndex.data( CollectionModel::CollectionRole ).value(); enableAction( CreateCollection, canCreateCollection( col ) ); enableAction( DeleteCollections, col.rights() & Collection::CanDeleteCollection ); enableAction( CopyCollections, multiColSelected && (col != Collection::root()) ); enableAction( CollectionProperties, singleColSelected && (col != Collection::root()) ); enableAction( SynchronizeCollections, CollectionUtils::isResource( col ) || CollectionUtils::isFolder( col ) ); enableAction( Paste, PasteHelper::canPaste( QApplication::clipboard()->mimeData(), col ) ); + //FIXME: remove the reinterpret_cast once FavoriteCollectionsModel is in kdepimlibs/akonadi + enableAction( AddToFavoriteCollections, (favoritesModel!=0) && (selectedIndex.model()!=reinterpret_cast(favoritesModel)) + && singleColSelected && (col != Collection::root()) ); } else { enableAction( CreateCollection, false ); enableAction( DeleteCollections, false ); enableAction( SynchronizeCollections, false ); enableAction( Paste, false ); + enableAction( AddToFavoriteCollections, false ); } bool multiItemSelected = false; int itemCount = 0; if ( itemSelectionModel ) { itemCount = itemSelectionModel->selectedRows().count(); multiItemSelected = itemCount > 0; } enableAction( CopyItems, multiItemSelected ); const bool canDeleteItem = !col.isValid() || (col.rights() & Collection::CanDeleteItem); enableAction( DeleteItems, multiItemSelected && canDeleteItem ); updatePluralLabel( CopyCollections, colCount ); updatePluralLabel( CopyItems, itemCount ); updatePluralLabel( DeleteItems, itemCount ); emit q->actionStateUpdated(); } void clipboardChanged( QClipboard::Mode mode ) { if ( mode == QClipboard::Clipboard ) updateActions(); } void slotCreateCollection() { Q_ASSERT( collectionSelectionModel ); if ( collectionSelectionModel->selection().indexes().isEmpty() ) return; const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); Q_ASSERT( index.isValid() ); const Collection collection = index.data( CollectionModel::CollectionRole ).value(); Q_ASSERT( collection.isValid() ); if ( !canCreateCollection( collection ) ) return; const QString name = KInputDialog::getText( i18nc( "@title:window", "New Folder"), i18nc( "@label:textbox, name of a thing", "Name"), QString(), 0, parentWidget ); if ( name.isEmpty() ) return; Collection::Id parentId = index.data( CollectionModel::CollectionIdRole ).toLongLong(); if ( parentId <= 0 ) return; Collection col; col.setName( name ); col.setParent( parentId ); CollectionCreateJob *job = new CollectionCreateJob( col ); q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionCreationResult(KJob*)) ); } void slotCopyCollections() { copy( collectionSelectionModel ); } void slotDeleteCollection() { Q_ASSERT( collectionSelectionModel ); if ( collectionSelectionModel->selection().indexes().isEmpty() ) return; const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); Q_ASSERT( index.isValid() ); const Collection collection = index.data( CollectionModel::CollectionRole ).value(); Q_ASSERT( collection.isValid() ); QString text = i18n( "Do you really want to delete folder '%1' and all its sub-folders?", index.data().toString() ); if ( CollectionUtils::isVirtual( collection ) ) text = i18n( "Do you really want to delete the search view '%1'?", index.data().toString() ); if ( KMessageBox::questionYesNo( parentWidget, text, i18n("Delete folder?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous ) != KMessageBox::Yes ) return; const Collection::Id colId = index.data( CollectionModel::CollectionIdRole ).toLongLong(); if ( colId <= 0 ) return; CollectionDeleteJob *job = new CollectionDeleteJob( Collection( colId ), q ); q->connect( job, SIGNAL(result(KJob*)), q, SLOT(collectionDeletionResult(KJob*)) ); } void slotSynchronizeCollection() { Q_ASSERT( collectionSelectionModel ); if ( collectionSelectionModel->selection().indexes().isEmpty() ) return; const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); Q_ASSERT( index.isValid() ); const Collection col = index.data( CollectionModel::CollectionRole ).value(); Q_ASSERT( col.isValid() ); AgentManager::self()->synchronizeCollection( col ); } void slotCollectionProperties() { if ( collectionSelectionModel->selection().indexes().isEmpty() ) return; const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); Q_ASSERT( index.isValid() ); Collection col = index.data( CollectionModel::CollectionRole ).value(); Q_ASSERT( col.isValid() ); CollectionPropertiesDialog* dlg = new CollectionPropertiesDialog( col, parentWidget ); dlg->show(); } void slotCopyItems() { copy( itemSelectionModel ); } void slotPaste() { Q_ASSERT( collectionSelectionModel ); if ( collectionSelectionModel->selection().indexes().isEmpty() ) return; const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); Q_ASSERT( index.isValid() ); const Collection col = index.data( CollectionModel::CollectionRole ).value(); Q_ASSERT( col.isValid() ); KJob *job = PasteHelper::paste( QApplication::clipboard()->mimeData(), col ); q->connect( job, SIGNAL(result(KJob*)), q, SLOT(pasteResult(KJob*)) ); } void slotDeleteItems() { if ( KMessageBox::questionYesNo( parentWidget, i18n( "Do you really want to delete all selected items?" ), i18n("Delete?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous ) != KMessageBox::Yes ) return; Q_ASSERT( itemSelectionModel ); // TODO: fix this once ItemModifyJob can handle item lists foreach ( const QModelIndex &index, itemSelectionModel->selectedRows() ) { bool ok; qlonglong id = index.data( ItemModel::IdRole ).toLongLong(&ok); Q_ASSERT(ok); new ItemDeleteJob( Item( id ), q ); } } void slotLocalSubscription() { SubscriptionDialog* dlg = new SubscriptionDialog( parentWidget ); dlg->show(); } + void slotAddToFavorites() + { + Q_ASSERT( collectionSelectionModel ); + Q_ASSERT( favoritesModel ); + if ( collectionSelectionModel->selection().indexes().isEmpty() ) + return; + + const QModelIndex index = collectionSelectionModel->selection().indexes().at( 0 ); + Q_ASSERT( index.isValid() ); + const Collection collection = index.data( CollectionModel::CollectionRole ).value(); + Q_ASSERT( collection.isValid() ); + + //FIXME: remove the reinterpret_cast and invokeMethod once FavoriteCollectionsModel is in kdepimlibs/akonadi + QAbstractItemModel *model = reinterpret_cast( favoritesModel ); + QMetaObject::invokeMethod( model, "addCollection", Q_ARG(Collection, collection) ); + } + void collectionCreationResult( KJob *job ) { if ( job->error() ) { KMessageBox::error( parentWidget, i18n("Could not create folder: %1", job->errorString()), i18n("Folder creation failed") ); } } void collectionDeletionResult( KJob *job ) { if ( job->error() ) { KMessageBox::error( parentWidget, i18n("Could not delete folder: %1", job->errorString()), i18n("Folder deletion failed") ); } } void pasteResult( KJob *job ) { if ( job->error() ) { KMessageBox::error( parentWidget, i18n("Could not paste data: %1", job->errorString()), i18n("Paste failed") ); } } StandardActionManager *q; KActionCollection *actionCollection; QWidget *parentWidget; QItemSelectionModel *collectionSelectionModel; QItemSelectionModel *itemSelectionModel; + FavoriteCollectionsModel *favoritesModel; QVector actions; AgentManager *agentManager; QHash pluralLabels; }; //@endcond StandardActionManager::StandardActionManager( KActionCollection * actionCollection, QWidget * parent) : QObject( parent ), d( new Private( this ) ) { d->parentWidget = parent; d->actionCollection = actionCollection; connect( QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), SLOT(clipboardChanged(QClipboard::Mode)) ); } StandardActionManager::~ StandardActionManager() { delete d; } void StandardActionManager::setCollectionSelectionModel(QItemSelectionModel * selectionModel) { d->collectionSelectionModel = selectionModel; connect( selectionModel, SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection& )), SLOT(updateActions()) ); } void StandardActionManager::setItemSelectionModel(QItemSelectionModel * selectionModel) { d->itemSelectionModel = selectionModel; connect( selectionModel, SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection& )), SLOT(updateActions()) ); } +void Akonadi::StandardActionManager::setFavoriteCollectionsModel( FavoriteCollectionsModel *favoritesModel ) +{ + d->favoritesModel = favoritesModel; +} + KAction* StandardActionManager::createAction( Type type ) { Q_ASSERT( type >= 0 && type < LastType ); Q_ASSERT( actionData[type].name ); if ( d->actions[type] ) return d->actions[type]; KAction *action = new KAction( d->parentWidget ); if ( d->pluralLabels.contains( type ) && !d->pluralLabels.value( type ).isEmpty() ) action->setText( d->pluralLabels.value( type ).subs( 1 ).toString() ); else if ( actionData[type].label ) action->setText( i18n( actionData[type].label ) ); if ( actionData[type].icon ) action->setIcon( KIcon( QString::fromLatin1( actionData[type].icon ) ) ); action->setShortcut( actionData[type].shortcut ); if ( actionData[type].slot ) connect( action, SIGNAL(triggered()), actionData[type].slot ); d->actionCollection->addAction( QString::fromLatin1(actionData[type].name), action ); d->actions[type] = action; d->updateActions(); return action; } void StandardActionManager::createAllActions() { for ( int i = 0; i < LastType; ++i ) createAction( (Type)i ); } KAction * StandardActionManager::action( Type type ) const { Q_ASSERT( type >= 0 && type < LastType ); return d->actions[type]; } void StandardActionManager::setActionText(Type type, const KLocalizedString & text) { Q_ASSERT( type >= 0 && type < LastType ); d->pluralLabels.insert( type, text ); d->updateActions(); } #include "standardactionmanager.moc" diff --git a/akonadi/standardactionmanager.h b/akonadi/standardactionmanager.h index 4a40234f6..9bde2bf80 100644 --- a/akonadi/standardactionmanager.h +++ b/akonadi/standardactionmanager.h @@ -1,201 +1,212 @@ /* Copyright (c) 2008 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_STANDARDACTIONMANAGER_H #define AKONADI_STANDARDACTIONMANAGER_H #include "akonadi_export.h" #include class KAction; class KActionCollection; class KLocalizedString; class QItemSelectionModel; class QWidget; namespace Akonadi { +class FavoriteCollectionsModel; + /** * @short Manages generic actions for collection and item views. * * Manages generic Akonadi actions common for all types. This covers * creating of the actions with appropriate labels, icons, shortcuts * etc., updating the action state depending on the current selection * as well as default implementations for the actual operations. * * If the default implementation is not appropriate for your application * you can still use the state tracking by disconnecting the triggered() * signal and re-connecting it to your implementation. The actual KAction * objects can be retrieved by calling createAction() or action() for that. * * If the default look and feel (labels, icons, shortcuts) of the actions * is not appropriate for your application, you can access them as noted * above and customize them to your needs. Additionally, you can set a * KLocalizedString which should be used as a action label with correct * plural handling for actions operating on multiple objects with * setActionText(). * * Finally, if you have special needs for the action states, connect to * the actionStateUpdated() signal and adjust the state accordingly. * * The following actions are provided (KAction name in parenthesis): * - Creation of a new collection (@c akonadi_collection_create) * - Copying of selected collections (@c akonadi_collection_copy) * - Deletion of selected collections (@c akonadi_collection_delete) * - Synchronization of selected collections (@c akonadi_collection_sync) * - Showing the collection properties dialog for the current collection (@c akonadi_collection_properties) * - Copying of selected items (@c akonadi_itemcopy) * - Pasting collections, items or raw data (@c akonadi_paste) * - Deleting of selected items (@c akonadi_item_delete) * - Managing local subscriptions (@c akonadi_manage_local_subscriptions) * * The following example shows how to use standard actions in your application: * * @code * * Akonadi::StandardActionManager *actMgr = new Akonadi::StandardActionManager( actionCollection(), this ); * actMgr->setCollectionSelectionModel( collectionView->collectionSelectionModel() ); * actMgr->createAllActions(); * * @endcode * * Additionally you have to add the actions to the KXMLGUI file of your application, * using the names listed above. * * If you only need a subset of the actions provided, you can call createAction() * instead of createAllActions() for the action types you want. * * @todo collection deleting and sync do not support multi-selection yet * * @author Volker Krause */ class AKONADI_EXPORT StandardActionManager : public QObject { Q_OBJECT public: /** * Describes the supported actions. */ enum Type { CreateCollection, ///< Creates an collection CopyCollections, ///< Copies collections DeleteCollections, ///< Deletes collections SynchronizeCollections, ///< Synchronizes collections CollectionProperties, ///< Provides collection properties CopyItems, ///< Copies items Paste, ///< Paste collections or items DeleteItems, ///< Deletes items ManageLocalSubscriptions, ///< Manages local subscriptions + AddToFavoriteCollections, ///< Add the collection to the favorite collections model LastType ///< Marks last action }; /** * Creates a new standard action manager. * * @param actionCollection The action collection to operate on. * @param parent The parent widget. */ explicit StandardActionManager( KActionCollection *actionCollection, QWidget *parent = 0 ); /** * Destroys the standard action manager. */ ~StandardActionManager(); /** * Sets the collection selection model based on which the collection * related actions should operate. If none is set, all collection actions * will be disabled. */ void setCollectionSelectionModel( QItemSelectionModel *selectionModel ); /** * Sets the item selection model based on which the item related actions * should operate. If none is set, all item actions will be disabled. */ void setItemSelectionModel( QItemSelectionModel* selectionModel ); + /** + * Sets the favorite collections model based on which the collection + * relatedactions should operate. If none is set, the "Add to Favorite Folders" action + * will be disabled. + */ + void setFavoriteCollectionsModel( FavoriteCollectionsModel *favoritesModel ); + /** * Creates the action of the given type and adds it to the action collection * specified in the constructor if it does not exist yet. The action is * connected to its default implementation provided by this class. */ KAction* createAction( Type type ); /** * Convenience method to create all standard actions. * @see createAction() */ void createAllActions(); /** * Returns the action of the given type, 0 if it has not been created (yet). */ KAction* action( Type type ) const; /** * Sets the label of the action @p type to @p text, which is used during * updating the action state and substituted according to the number of * selected objects. This is mainly useful to customize the label of actions * that can operate on multiple objects. * * Example: * @code * acctMgr->setActionText( Akonadi::StandardActionManager::CopyItems, * ki18np( "Copy Mail", "Copy %1 Mails" ) ); * @endcode */ void setActionText( Type type, const KLocalizedString &text ); Q_SIGNALS: /** * This signal is emitted whenever the action state has been updated. * In case you have special needs for changing the state of some actions, * connect to this signal and adjust the action state. */ void actionStateUpdated(); private: //@cond PRIVATE class Private; Private* const d; Q_PRIVATE_SLOT( d, void updateActions() ) Q_PRIVATE_SLOT( d, void clipboardChanged(QClipboard::Mode) ) Q_PRIVATE_SLOT( d, void slotCreateCollection() ) Q_PRIVATE_SLOT( d, void slotCopyCollections() ) Q_PRIVATE_SLOT( d, void slotDeleteCollection() ) Q_PRIVATE_SLOT( d, void slotSynchronizeCollection() ) Q_PRIVATE_SLOT( d, void slotCollectionProperties() ) Q_PRIVATE_SLOT( d, void slotCopyItems() ) Q_PRIVATE_SLOT( d, void slotPaste() ) Q_PRIVATE_SLOT( d, void slotDeleteItems() ) Q_PRIVATE_SLOT( d, void slotLocalSubscription() ) + Q_PRIVATE_SLOT( d, void slotAddToFavorites() ) Q_PRIVATE_SLOT( d, void collectionCreationResult(KJob*) ) Q_PRIVATE_SLOT( d, void collectionDeletionResult(KJob*) ) Q_PRIVATE_SLOT( d, void pasteResult(KJob*) ) //@endcond }; } #endif diff --git a/akonadi/tests/resourcetest.cpp b/akonadi/tests/resourcetest.cpp index 0ec75075c..4d9e05d65 100644 --- a/akonadi/tests/resourcetest.cpp +++ b/akonadi/tests/resourcetest.cpp @@ -1,90 +1,94 @@ /* Copyright (c) 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include using namespace Akonadi; class ResourceTest : public QObject { Q_OBJECT private slots: void testResourceManagement() { qRegisterMetaType(); QSignalSpy spyAddInstance( AgentManager::self(), SIGNAL(instanceAdded(Akonadi::AgentInstance)) ); QVERIFY( spyAddInstance.isValid() ); QSignalSpy spyRemoveInstance( AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance)) ); QVERIFY( spyRemoveInstance.isValid() ); AgentType type = AgentManager::self()->type( "akonadi_knut_resource" ); QVERIFY( type.isValid() ); QCOMPARE( type.capabilities(), QStringList( "Resource" ) ); AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type ); QVERIFY( job->exec() ); AgentInstance instance = job->instance(); QVERIFY( instance.isValid() ); QTest::qWait( 2000 ); QCOMPARE( spyAddInstance.count(), 1 ); QCOMPARE( spyAddInstance.first().at( 0 ).value(), instance ); QVERIFY( AgentManager::self()->instance( instance.identifier() ).isValid() ); job = new AgentInstanceCreateJob( type ); QVERIFY( job->exec() ); AgentInstance instance2 = job->instance(); QVERIFY( !( instance == instance2 ) ); QTest::qWait( 2000 ); QCOMPARE( spyAddInstance.count(), 2 ); AgentManager::self()->removeInstance( instance ); AgentManager::self()->removeInstance( instance2 ); QTest::qWait( 2000 ); QCOMPARE( spyRemoveInstance.count(), 2 ); QVERIFY( !AgentManager::self()->instances().contains( instance ) ); QVERIFY( !AgentManager::self()->instances().contains( instance2 ) ); } void testIllegalResourceManagement() { AgentInstanceCreateJob *job = new AgentInstanceCreateJob( AgentManager::self()->type( "non_existing_resource" ) ); QVERIFY( !job->exec() ); // unique agent + // According to vkrause the mailthreader agent is no longer started by + // default so this won't work. + /* const AgentType type = AgentManager::self()->type( "akonadi_mailthreader_agent" ); QVERIFY( type.isValid() ); job = new AgentInstanceCreateJob( type ); QVERIFY( job->exec() ); job = new AgentInstanceCreateJob( type ); QVERIFY( !job->exec() ); + */ } }; QTEST_AKONADIMAIN( ResourceTest, NoGUI ) #include "resourcetest.moc" diff --git a/gpgme++/CMakeLists.txt b/gpgme++/CMakeLists.txt index 8ea999b99..86c30bc4b 100644 --- a/gpgme++/CMakeLists.txt +++ b/gpgme++/CMakeLists.txt @@ -1,147 +1,153 @@ project( gpgmepp ) if (MINGW) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mms-bitfields") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mms-bitfields") endif (MINGW) include_directories( ${GPGME_INCLUDES} ${Boost_INCLUDE_DIR} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-gpgme++.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gpgme++.h ) ## gpgme comes in three flavours on each of the platforms: ## Windows: gpgme, gpgme-glib, gpgme-qt ## Unix: gpgme, gpgme-pthread, gpgme-pth ## We're building corresponding gpgme++ flavours set(gpgme_LIB_SRCS exception.cpp context.cpp key.cpp trustitem.cpp data.cpp callbacks.cpp eventloopinteractor.cpp editinteractor.cpp assuanresult.cpp keylistresult.cpp keygenerationresult.cpp importresult.cpp decryptionresult.cpp verificationresult.cpp signingresult.cpp encryptionresult.cpp engineinfo.cpp gpgsetexpirytimeeditinteractor.cpp gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp gpgadduserideditinteractor.cpp + defaultassuantransaction.cpp + scdgetinfoassuantransaction.cpp + gpgagentgetinfoassuantransaction.cpp ) -set( _gpgmepp_version 2.3.1 ) +set( _gpgmepp_version 2.4.0 ) set( _gpgmepp_soversion 2 ) set( GPGMEPP_INCLUDE ${INCLUDE_INSTALL_DIR} ${GPGME_INCLUDES} ${Boost_INCLUDE_DIR} ) if ( GPGME_VANILLA_FOUND ) kde4_add_library( gpgmepp SHARED ${gpgme_LIB_SRCS} context_vanilla.cpp ) target_link_libraries( gpgmepp ${GPGME_VANILLA_LIBRARIES} ) set_target_properties( gpgmepp PROPERTIES VERSION ${_gpgmepp_version} SOVERSION ${_gpgmepp_soversion} OUTPUT_NAME gpgme++ ) get_target_property( GPGMEPP_VANILLA_LIBRARY gpgmepp LOCATION ) install(TARGETS gpgmepp EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} COMPONENT Devel) endif ( GPGME_VANILLA_FOUND ) if ( GPGME_GLIB_FOUND ) kde4_add_library( gpgmepp-glib SHARED ${gpgme_LIB_SRCS} context_glib.cpp ) target_link_libraries( gpgmepp-glib ${GPGME_GLIB_LIBRARIES} ) set_target_properties( gpgmepp-glib PROPERTIES VERSION ${_gpgmepp_version} SOVERSION ${_gpgmepp_soversion} OUTPUT_NAME gpgme++-glib DEFINE_SYMBOL MAKE_GPGME___LIB ) get_target_property( GPGMEPP_GLIB_LIBRARY gpgmepp LOCATION ) install(TARGETS gpgmepp-glib EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif ( GPGME_GLIB_FOUND ) if ( GPGME_QT_FOUND ) kde4_add_library( gpgmepp-qt SHARED ${gpgme_LIB_SRCS} context_qt.cpp ) target_link_libraries( gpgmepp-qt ${GPGME_QT_LIBRARIES} ) if(WIN32) target_link_libraries( gpgmepp-qt ${GPGME_VANILLA_LIBRARIES} ) endif(WIN32) set_target_properties( gpgmepp-qt PROPERTIES VERSION ${_gpgmepp_version} SOVERSION ${_gpgmepp_soversion} OUTPUT_NAME gpgme++-qt DEFINE_SYMBOL MAKE_GPGME___LIB ) get_target_property( GPGMEPP_QT_LIBRARY gpgmepp LOCATION ) install(TARGETS gpgmepp-qt EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif ( GPGME_QT_FOUND ) if ( GPGME_PTHREAD_FOUND ) kde4_add_library( gpgmepp-pthread SHARED ${gpgme_LIB_SRCS} context_vanilla.cpp ) target_link_libraries( gpgmepp-pthread ${GPGME_PTHREAD_LIBRARIES} ) set_target_properties( gpgmepp-pthread PROPERTIES VERSION ${_gpgmepp_version} SOVERSION ${_gpgmepp_soversion} OUTPUT_NAME gpgme++-pthread DEFINE_SYMBOL MAKE_GPGME___LIB ) get_target_property( GPGMEPP_PTHREAD_LIBRARY gpgmepp LOCATION ) install(TARGETS gpgmepp-pthread EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif ( GPGME_PTHREAD_FOUND ) if ( GPGME_PTH_FOUND ) kde4_add_library( gpgmepp-pth SHARED ${gpgme_LIB_SRCS} context_vanilla.cpp ) target_link_libraries( gpgmepp-pth ${GPGME_PTH_LIBRARIES} ) set_target_properties( gpgmepp-pth PROPERTIES VERSION ${_gpgmepp_version} SOVERSION ${_gpgmepp_soversion} OUTPUT_NAME gpgme++-pth DEFINE_SYMBOL MAKE_GPGME___LIB ) get_target_property( GPGMEPP_PTH_LIBRARY gpgmepp LOCATION ) install(TARGETS gpgmepp-pth EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif ( GPGME_PTH_FOUND ) include( CMakeExportBuildSettings ) # this writes way too much, but do we care? EXPORT_LIBRARY_DEPENDENCIES( ${CMAKE_CURRENT_BINARY_DIR}/GpgmeppLibraryDepends.cmake ) ########### install files ############### configure_file(${CMAKE_CURRENT_SOURCE_DIR}/GpgmeppConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/GpgmeppConfig.cmake @ONLY ) if ( GPGME_FOUND ) add_subdirectory( interfaces ) install(FILES global.h error.h exception.h context.h key.h trustitem.h eventloopinteractor.h editinteractor.h data.h gpgmefw.h result.h assuanresult.h keylistresult.h keygenerationresult.h importresult.h decryptionresult.h verificationresult.h signingresult.h encryptionresult.h notation.h engineinfo.h gpgsetexpirytimeeditinteractor.h gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h gpgadduserideditinteractor.h + defaultassuantransaction.h + scdgetinfoassuantransaction.h + gpgagentgetinfoassuantransaction.h gpgme++_export.h DESTINATION ${INCLUDE_INSTALL_DIR}/gpgme++ COMPONENT Devel ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/GpgmeppConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/GpgmeppLibraryDepends.cmake DESTINATION ${LIB_INSTALL_DIR}/gpgmepp ) endif ( GPGME_FOUND ) diff --git a/gpgme++/context.cpp b/gpgme++/context.cpp index dabdd1f1d..9c4676a94 100644 --- a/gpgme++/context.cpp +++ b/gpgme++/context.cpp @@ -1,1243 +1,1340 @@ /* context.cpp - wraps a gpgme key context Copyright (C) 2003, 2007 Klarälvdalens Datakonsult AB This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "callbacks.h" #include "data_p.h" #include "context_p.h" #include "util.h" #include #include #include #ifndef NDEBUG #include using std::cerr; using std::endl; #endif #include namespace GpgME { + + static inline gpgme_error_t makeError( gpg_err_code_t code ) { + return gpg_err_make( (gpg_err_source_t)22, code ); + } + + static inline unsigned int xtoi_1( const char * str ) { + const unsigned int ch = *str; + const unsigned int result = + ch <= '9' ? ch - '0' : + ch <= 'F' ? ch - 'A' + 10 : + /* else */ ch - 'a' + 10 ; + return result < 16 ? result : 0 ; + } + static inline int xtoi_2( const char * str ) { + return xtoi_1( str ) * 16U + xtoi_1( str+1 ); + } + + static void percent_unescape( std::string & s, bool plus2space ) { + std::string::iterator src = s.begin(), dest = s.begin(), end = s.end(); + while ( src != end ) + if ( *src == '%' && end - src > 2 ) { + *dest++ = xtoi_2( &*++src ); + src += 2; + } else if ( *src == '+' && plus2space ) { + *dest++ = ' '; + ++src; + } else { + *dest++ = *src++; + } + s.erase( dest, end ); + } + void initializeLibrary() { gpgme_check_version( 0 ); } - static inline gpgme_error_t makeError( gpg_err_code_t code ) { - return gpg_err_make( (gpg_err_source_t)22, code ); + Error initializeLibrary( int ) { + if ( gpgme_check_version( GPGME_VERSION ) ) + return Error(); + else + return Error( gpg_error( GPG_ERR_USER_1 ) ); } static void format_error( gpgme_error_t err, std::string & str ) { char buffer[ 1024 ]; gpgme_strerror_r( err, buffer, sizeof buffer ); buffer[ sizeof buffer - 1 ] = '\0'; str = buffer; } const char * Error::source() const { return gpgme_strsource( (gpgme_error_t)mErr ); } const char * Error::asString() const { if ( mMessage.empty() ) format_error( static_cast( mErr ), mMessage ); return mMessage.c_str(); } int Error::code() const { return gpgme_err_code( mErr ); } int Error::sourceID() const { return gpgme_err_source( mErr ); } bool Error::isCanceled() const { return code() == GPG_ERR_CANCELED; } std::ostream & operator<<( std::ostream & os, const Error & err ) { return os << "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))"; } Context::Context( gpgme_ctx_t ctx ) { d = new Private( ctx ); } Context::~Context() { delete d; d = 0; } Context * Context::createForProtocol( Protocol proto ) { gpgme_ctx_t ctx = 0; if ( gpgme_new ( &ctx ) != 0 ) return 0; switch ( proto ) { case OpenPGP: if ( gpgme_set_protocol( ctx, GPGME_PROTOCOL_OpenPGP ) != 0 ) { gpgme_release( ctx ); return 0; } break; case CMS: if ( gpgme_set_protocol( ctx, GPGME_PROTOCOL_CMS ) != 0 ) { gpgme_release( ctx ); return 0; } break; default: return 0; } return new Context( ctx ); } + std::auto_ptr Context::createForEngine( Engine eng, Error * error ) { + gpgme_ctx_t ctx = 0; + if ( const gpgme_error_t err = gpgme_new( &ctx ) ) { + if ( error ) + *error = Error( err ); + return std::auto_ptr(); + } + + switch ( eng ) { + case AssuanEngine: +#ifdef HAVE_GPGME_ASSUAN_ENGINE + if ( const gpgme_error_t err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_ASSUAN ) ) { + gpgme_release( ctx ); + if ( error ) + *error = Error( err ); + return std::auto_ptr(); + } + break; +#else + if ( error ) + *error = Error( gpg_error( GPG_ERR_NOT_SUPPORTED ) ); + return std::auto_ptr(); +#endif + default: + if ( error ) + *error = Error( gpg_error( GPG_ERR_INV_ARG ) ); + return std::auto_ptr(); + } + + if ( error ) + *error = Error(); + + return std::auto_ptr( new Context( ctx ) ); + } + // // // Context::Private // // Context::Private::Private( gpgme_ctx_t c ) : ctx( c ), iocbs( 0 ), lastop( None ), lasterr( GPG_ERR_NO_ERROR ), lastAssuanInquireData( Data::null ), lastAssuanTransaction(), lastEditInteractor(), lastCardEditInteractor() { } Context::Private::~Private() { if ( ctx ) gpgme_release( ctx ); ctx = 0; delete iocbs; } // // // Context attributes: // // Protocol Context::protocol() const { gpgme_protocol_t p = gpgme_get_protocol( d->ctx ); switch ( p ) { case GPGME_PROTOCOL_OpenPGP: return OpenPGP; case GPGME_PROTOCOL_CMS: return CMS; default: return UnknownProtocol; } } void Context::setArmor( bool useArmor ) { gpgme_set_armor( d->ctx, int( useArmor ) ); } bool Context::armor() const { return gpgme_get_armor( d->ctx ); } void Context::setTextMode( bool useTextMode ) { gpgme_set_textmode( d->ctx, int( useTextMode ) ); } bool Context::textMode() const { return gpgme_get_textmode( d->ctx ); } void Context::setIncludeCertificates( int which ) { if ( which == DefaultCertificates ) { #ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT which = GPGME_INCLUDE_CERTS_DEFAULT; #else which = 1; #endif } gpgme_set_include_certs( d->ctx, which ); } int Context::includeCertificates() const { return gpgme_get_include_certs( d->ctx ); } void Context::setKeyListMode( unsigned int mode ) { gpgme_set_keylist_mode( d->ctx, add_to_gpgme_keylist_mode_t( 0, mode ) ); } void Context::addKeyListMode( unsigned int mode ) { const unsigned int cur = gpgme_get_keylist_mode( d->ctx ); gpgme_set_keylist_mode( d->ctx, add_to_gpgme_keylist_mode_t( cur, mode ) ); } unsigned int Context::keyListMode() const { return convert_from_gpgme_keylist_mode_t( gpgme_get_keylist_mode( d->ctx ) ); } void Context::setProgressProvider( ProgressProvider * provider ) { gpgme_set_progress_cb( d->ctx, provider ? &progress_callback : 0, provider ); } ProgressProvider * Context::progressProvider() const { void * pp = 0; gpgme_progress_cb_t pcb = &progress_callback; gpgme_get_progress_cb( d->ctx, &pcb, &pp ); return static_cast( pp ); } void Context::setPassphraseProvider( PassphraseProvider * provider ) { gpgme_set_passphrase_cb( d->ctx, provider ? &passphrase_callback : 0, provider ); } PassphraseProvider * Context::passphraseProvider() const { void * pp = 0; gpgme_passphrase_cb_t pcb = &passphrase_callback; gpgme_get_passphrase_cb( d->ctx, &pcb, &pp ); return static_cast( pp ); } void Context::setManagedByEventLoopInteractor( bool manage ) { if ( !EventLoopInteractor::instance() ) { #ifndef NDEBUG cerr << "Context::setManagedByEventLoopInteractor(): " "You must create an instance of EventLoopInteractor " "before using anything that needs one." << endl; #endif return; } if ( manage ) EventLoopInteractor::instance()->manage( this ); else EventLoopInteractor::instance()->unmanage( this ); } bool Context::managedByEventLoopInteractor() const { return d->iocbs != 0; } void Context::installIOCallbacks( gpgme_io_cbs * iocbs ) { if ( !iocbs ) { uninstallIOCallbacks(); return; } gpgme_set_io_cbs( d->ctx, iocbs ); delete d->iocbs; d->iocbs = iocbs; } void Context::uninstallIOCallbacks() { static gpgme_io_cbs noiocbs = { 0, 0, 0, 0, 0 }; // io.add == 0 means disable io callbacks: gpgme_set_io_cbs( d->ctx, &noiocbs ); delete d->iocbs; d->iocbs = 0; } Error Context::setLocale( int cat, const char * val ) { return Error( d->lasterr = gpgme_set_locale( d->ctx, cat, val ) ); } EngineInfo Context::engineInfo() const { #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO return EngineInfo( gpgme_ctx_get_engine_info( d->ctx ) ); #else return EngineInfo(); #endif } Error Context::setEngineFileName( const char * filename ) { #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO const char * const home_dir = engineInfo().homeDirectory(); return Error( gpgme_ctx_set_engine_info( d->ctx, gpgme_get_protocol( d->ctx ), filename, home_dir ) ); #else return Error( makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } Error Context::setEngineHomeDirectory( const char * home_dir ) { #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO const char * const filename = engineInfo().fileName(); return Error( gpgme_ctx_set_engine_info( d->ctx, gpgme_get_protocol( d->ctx ), filename, home_dir ) ); #else return Error( makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } // // // Key Management // // Error Context::startKeyListing( const char * pattern, bool secretOnly ) { d->lastop = Private::KeyList; return Error( d->lasterr = gpgme_op_keylist_start( d->ctx, pattern, int( secretOnly ) ) ); } Error Context::startKeyListing( const char * patterns[], bool secretOnly ) { d->lastop = Private::KeyList; #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN if ( !patterns || !patterns[0] || !patterns[1] ) // max. one pattern -> use the non-ext version return startKeyListing( patterns ? patterns[0] : 0, secretOnly ); #endif return Error( d->lasterr = gpgme_op_keylist_ext_start( d->ctx, patterns, int( secretOnly ), 0 ) ); } Key Context::nextKey( GpgME::Error & e ) { d->lastop = Private::KeyList; gpgme_key_t key; e = Error( d->lasterr = gpgme_op_keylist_next( d->ctx, &key ) ); return Key( key, false ); } KeyListResult Context::endKeyListing() { d->lasterr = gpgme_op_keylist_end( d->ctx ); return keyListResult(); } KeyListResult Context::keyListResult() const { return KeyListResult( d->ctx, Error(d->lasterr) ); } Key Context::key( const char * fingerprint, GpgME::Error & e , bool secret /*, bool forceUpdate*/ ) { d->lastop = Private::KeyList; gpgme_key_t key; e = Error( d->lasterr = gpgme_get_key( d->ctx, fingerprint, &key, int( secret )/*, int( forceUpdate )*/ ) ); return Key( key, false ); } KeyGenerationResult Context::generateKey( const char * parameters, Data & pubKey ) { d->lastop = Private::KeyGen; Data::Private * const dp = pubKey.impl(); d->lasterr = gpgme_op_genkey( d->ctx, parameters, dp ? dp->data : 0, 0 ); return KeyGenerationResult( d->ctx, Error(d->lasterr) ); } Error Context::startKeyGeneration( const char * parameters, Data & pubKey ) { d->lastop = Private::KeyGen; Data::Private * const dp = pubKey.impl(); return Error( d->lasterr = gpgme_op_genkey_start( d->ctx, parameters, dp ? dp->data : 0, 0 ) ); } KeyGenerationResult Context::keyGenerationResult() const { if ( d->lastop & Private::KeyGen ) return KeyGenerationResult( d->ctx, Error(d->lasterr) ); else return KeyGenerationResult(); } Error Context::exportPublicKeys( const char * pattern, Data & keyData ) { d->lastop = Private::Export; Data::Private * const dp = keyData.impl(); return Error( d->lasterr = gpgme_op_export( d->ctx, pattern, 0, dp ? dp->data : 0 ) ); } Error Context::exportPublicKeys( const char * patterns[], Data & keyData ) { d->lastop = Private::Export; #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN if ( !patterns || !patterns[0] || !patterns[1] ) // max. one pattern -> use the non-ext version return exportPublicKeys( patterns ? patterns[0] : 0, keyData ); #endif Data::Private * const dp = keyData.impl(); return Error( d->lasterr = gpgme_op_export_ext( d->ctx, patterns, 0, dp ? dp->data : 0 ) ); } Error Context::startPublicKeyExport( const char * pattern, Data & keyData ) { d->lastop = Private::Export; Data::Private * const dp = keyData.impl(); return Error( d->lasterr = gpgme_op_export_start( d->ctx, pattern, 0, dp ? dp->data : 0 ) ); } Error Context::startPublicKeyExport( const char * patterns[], Data & keyData ) { d->lastop = Private::Export; #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN if ( !patterns || !patterns[0] || !patterns[1] ) // max. one pattern -> use the non-ext version return startPublicKeyExport( patterns ? patterns[0] : 0, keyData ); #endif Data::Private * const dp = keyData.impl(); return Error( d->lasterr = gpgme_op_export_ext_start( d->ctx, patterns, 0, dp ? dp->data : 0 ) ); } ImportResult Context::importKeys( const Data & data ) { d->lastop = Private::Import; const Data::Private * const dp = data.impl(); d->lasterr = gpgme_op_import( d->ctx, dp ? dp->data : 0 ); return ImportResult( d->ctx, Error(d->lasterr) ); } ImportResult Context::importKeys( const std::vector & kk ) { d->lastop = Private::Import; d->lasterr = gpg_error( GPG_ERR_NOT_IMPLEMENTED ); bool shouldHaveResult = false; #ifdef HAVE_GPGME_OP_IMPORT_KEYS const boost::scoped_array keys( new gpgme_key_t[ kk.size() + 1 ] ); gpgme_key_t * keys_it = &keys[0]; for ( std::vector::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; d->lasterr = gpgme_op_import_keys( d->ctx, keys.get() ); shouldHaveResult = true; #endif if ( ( gpgme_err_code( d->lasterr ) == GPG_ERR_NOT_IMPLEMENTED || gpgme_err_code( d->lasterr ) == GPG_ERR_NOT_SUPPORTED ) && protocol() == CMS ) { // ok, try the workaround (export+import): std::vector fprs; for ( std::vector::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) { if ( const char * fpr = it->primaryFingerprint() ) { if ( *fpr ) fprs.push_back( fpr ); } else if ( const char * keyid = it->keyID() ) { if ( *keyid ) fprs.push_back( keyid ); } } fprs.push_back( 0 ); Data data; Data::Private * const dp = data.impl(); const gpgme_keylist_mode_t oldMode = gpgme_get_keylist_mode( d->ctx ); gpgme_set_keylist_mode( d->ctx, GPGME_KEYLIST_MODE_EXTERN ); d->lasterr = gpgme_op_export_ext( d->ctx, &fprs[0], 0, dp ? dp->data : 0 ); gpgme_set_keylist_mode( d->ctx, oldMode ); if ( !d->lasterr ) { data.seek( 0, SEEK_SET ); d->lasterr = gpgme_op_import( d->ctx, dp ? dp->data : 0 ); shouldHaveResult = true; } } if ( shouldHaveResult ) return ImportResult( d->ctx, Error(d->lasterr) ); else return ImportResult( Error( d->lasterr ) ); } Error Context::startKeyImport( const Data & data ) { d->lastop = Private::Import; const Data::Private * const dp = data.impl(); return Error( d->lasterr = gpgme_op_import_start( d->ctx, dp ? dp->data : 0 ) ); } Error Context::startKeyImport( const std::vector & kk ) { d->lastop = Private::Import; #ifdef HAVE_GPGME_OP_IMPORT_KEYS const boost::scoped_array keys( new gpgme_key_t[ kk.size() + 1 ] ); gpgme_key_t * keys_it = &keys[0]; for ( std::vector::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; return Error( d->lasterr = gpgme_op_import_keys_start( d->ctx, keys.get() ) ); #else (void)kk; return Error( d->lasterr = gpg_error( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } ImportResult Context::importResult() const { if ( d->lastop & Private::Import ) return ImportResult( d->ctx, Error(d->lasterr) ); else return ImportResult(); } Error Context::deleteKey( const Key & key, bool allowSecretKeyDeletion ) { d->lastop = Private::Delete; return Error( d->lasterr = gpgme_op_delete( d->ctx, key.impl(), int( allowSecretKeyDeletion ) ) ); } Error Context::startKeyDeletion( const Key & key, bool allowSecretKeyDeletion ) { d->lastop = Private::Delete; return Error( d->lasterr = gpgme_op_delete_start( d->ctx, key.impl(), int( allowSecretKeyDeletion ) ) ); } Error Context::edit( const Key & key, std::auto_ptr func, Data & data ) { d->lastop = Private::Edit; d->lastEditInteractor = func; Data::Private * const dp = data.impl(); return Error( d->lasterr = gpgme_op_edit( d->ctx, key.impl(), d->lastEditInteractor.get() ? edit_interactor_callback : 0, d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, dp ? dp->data : 0 ) ); } Error Context::startEditing( const Key & key, std::auto_ptr func, Data & data ) { d->lastop = Private::Edit; d->lastEditInteractor = func; Data::Private * const dp = data.impl(); return Error( d->lasterr = gpgme_op_edit_start( d->ctx, key.impl(), d->lastEditInteractor.get() ? edit_interactor_callback : 0, d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, dp ? dp->data : 0 ) ); } EditInteractor * Context::lastEditInteractor() const { return d->lastEditInteractor.get(); } + std::auto_ptr Context::takeLastEditInteractor() { + return d->lastEditInteractor; + } + Error Context::cardEdit( const Key & key, std::auto_ptr func, Data & data ) { d->lastop = Private::CardEdit; d->lastCardEditInteractor = func; Data::Private * const dp = data.impl(); return Error( d->lasterr = gpgme_op_card_edit( d->ctx, key.impl(), d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, dp ? dp->data : 0 ) ); } Error Context::startCardEditing( const Key & key, std::auto_ptr func, Data & data ) { d->lastop = Private::CardEdit; d->lastCardEditInteractor = func; Data::Private * const dp = data.impl(); return Error( d->lasterr = gpgme_op_card_edit_start( d->ctx, key.impl(), d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, dp ? dp->data : 0 ) ); } EditInteractor * Context::lastCardEditInteractor() const { return d->lastCardEditInteractor.get(); } + std::auto_ptr Context::takeLastCardEditInteractor() { + return d->lastCardEditInteractor; + } + Error Context::startTrustItemListing( const char * pattern, int maxLevel ) { d->lastop = Private::TrustList; return Error( d->lasterr = gpgme_op_trustlist_start( d->ctx, pattern, maxLevel ) ); } TrustItem Context::nextTrustItem( Error & e ) { gpgme_trust_item_t ti = 0; e = Error( d->lasterr = gpgme_op_trustlist_next( d->ctx, &ti ) ); return TrustItem( ti ); } Error Context::endTrustItemListing() { return Error( d->lasterr = gpgme_op_trustlist_end( d->ctx ) ); } #ifdef HAVE_GPGME_ASSUAN_ENGINE static gpgme_error_t assuan_transaction_data_callback( void * opaque, const void * data, size_t datalen ) { assert( opaque ); AssuanTransaction * t = static_cast( opaque ); - return t->data( data, datalen ).encodedError(); + return t->data( static_cast( data ), datalen ).encodedError(); } static gpgme_error_t assuan_transaction_inquire_callback( void * opaque, const char * name, const char * args, gpgme_data_t * r_data ) { assert( opaque ); Context::Private * p = static_cast( opaque ); AssuanTransaction * t = p->lastAssuanTransaction.get(); assert( t ); Error err; if ( name ) p->lastAssuanInquireData = t->inquire( name, args, err ); else p->lastAssuanInquireData = Data::null; if ( !p->lastAssuanInquireData.isNull() ) *r_data = p->lastAssuanInquireData.impl()->data; return err.encodedError(); } static gpgme_error_t assuan_transaction_status_callback( void * opaque, const char * status, const char * args ) { assert( opaque ); AssuanTransaction * t = static_cast( opaque ); - return t->status( status, args ).encodedError(); + std::string a = args; + percent_unescape( a, true ); // ### why doesn't gpgme do this?? + return t->status( status, a.c_str() ).encodedError(); } #endif + AssuanResult Context::assuanTransact( const char * command ) { + return assuanTransact( command, std::auto_ptr( new DefaultAssuanTransaction ) ); + } + AssuanResult Context::assuanTransact( const char * command, std::auto_ptr transaction ) { d->lastop = Private::AssuanTransact; d->lastAssuanTransaction = transaction; + if ( !d->lastAssuanTransaction.get() ) + return AssuanResult( Error( d->lasterr = gpg_error( GPG_ERR_INV_ARG ) ) ); #ifdef HAVE_GPGME_ASSUAN_ENGINE d->lasterr = gpgme_op_assuan_transact( d->ctx, command, - d->lastAssuanTransaction.get() ? assuan_transaction_data_callback : 0, + assuan_transaction_data_callback, d->lastAssuanTransaction.get(), - d->lastAssuanTransaction.get() ? assuan_transaction_inquire_callback : 0, - d->lastAssuanTransaction.get() ? d : 0, // sic! - d->lastAssuanTransaction.get() ? assuan_transaction_status_callback : 0, + assuan_transaction_inquire_callback, + d, // sic! + assuan_transaction_status_callback, d->lastAssuanTransaction.get() ); #else (void)command; d->lasterr = gpg_error( GPG_ERR_NOT_SUPPORTED ); #endif return AssuanResult( d->ctx, d->lasterr ); } + Error Context::startAssuanTransaction( const char * command ) { + return startAssuanTransaction( command, std::auto_ptr( new DefaultAssuanTransaction ) ); + } + Error Context::startAssuanTransaction( const char * command, std::auto_ptr transaction ) { d->lastop = Private::AssuanTransact; d->lastAssuanTransaction = transaction; + if ( !d->lastAssuanTransaction.get() ) + return Error( d->lasterr = gpg_error( GPG_ERR_INV_ARG ) ); #ifdef HAVE_GPGME_ASSUAN_ENGINE return Error( d->lasterr = gpgme_op_assuan_transact_start( d->ctx, command, - d->lastAssuanTransaction.get() ? assuan_transaction_data_callback : 0, + assuan_transaction_data_callback, d->lastAssuanTransaction.get(), - d->lastAssuanTransaction.get() ? assuan_transaction_inquire_callback : 0, - d->lastAssuanTransaction.get() ? d : 0, // sic! - d->lastAssuanTransaction.get() ? assuan_transaction_status_callback : 0, + assuan_transaction_inquire_callback, + d, // sic! + assuan_transaction_status_callback, d->lastAssuanTransaction.get() ) ); #else (void)command; return Error( d->lasterr = gpg_error( GPG_ERR_NOT_SUPPORTED ) ); #endif } AssuanResult Context::assuanResult() const { if ( d->lastop & Private::AssuanTransact ) return AssuanResult( d->ctx, d->lasterr ); else return AssuanResult(); } AssuanTransaction * Context::lastAssuanTransaction() const { return d->lastAssuanTransaction.get(); - } + } + + std::auto_ptr Context::takeLastAssuanTransaction() { + return d->lastAssuanTransaction; + } DecryptionResult Context::decrypt( const Data & cipherText, Data & plainText ) { d->lastop = Private::Decrypt; const Data::Private * const cdp = cipherText.impl(); Data::Private * const pdp = plainText.impl(); d->lasterr = gpgme_op_decrypt( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ); return DecryptionResult( d->ctx, Error(d->lasterr) ); } Error Context::startDecryption( const Data & cipherText, Data & plainText ) { d->lastop = Private::Decrypt; const Data::Private * const cdp = cipherText.impl(); Data::Private * const pdp = plainText.impl(); return Error( d->lasterr = gpgme_op_decrypt_start( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ) ); } DecryptionResult Context::decryptionResult() const { if ( d->lastop & Private::Decrypt ) return DecryptionResult( d->ctx, Error(d->lasterr) ); else return DecryptionResult(); } VerificationResult Context::verifyDetachedSignature( const Data & signature, const Data & signedText ) { d->lastop = Private::Verify; const Data::Private * const sdp = signature.impl(); const Data::Private * const tdp = signedText.impl(); d->lasterr = gpgme_op_verify( d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0 ); return VerificationResult( d->ctx, Error(d->lasterr) ); } VerificationResult Context::verifyOpaqueSignature( const Data & signedData, Data & plainText ) { d->lastop = Private::Verify; const Data::Private * const sdp = signedData.impl(); Data::Private * const pdp = plainText.impl(); d->lasterr = gpgme_op_verify( d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0 ); return VerificationResult( d->ctx, Error(d->lasterr) ); } Error Context::startDetachedSignatureVerification( const Data & signature, const Data & signedText ) { d->lastop = Private::Verify; const Data::Private * const sdp = signature.impl(); const Data::Private * const tdp = signedText.impl(); return Error( d->lasterr = gpgme_op_verify_start( d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0 ) ); } Error Context::startOpaqueSignatureVerification( const Data & signedData, Data & plainText ) { d->lastop = Private::Verify; const Data::Private * const sdp = signedData.impl(); Data::Private * const pdp = plainText.impl(); return Error( d->lasterr = gpgme_op_verify_start( d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0 ) ); } VerificationResult Context::verificationResult() const { if ( d->lastop & Private::Verify ) return VerificationResult( d->ctx, Error(d->lasterr) ); else return VerificationResult(); } std::pair Context::decryptAndVerify( const Data & cipherText, Data & plainText ) { d->lastop = Private::DecryptAndVerify; const Data::Private * const cdp = cipherText.impl(); Data::Private * const pdp = plainText.impl(); d->lasterr = gpgme_op_decrypt_verify( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ); return std::make_pair( DecryptionResult( d->ctx, Error(d->lasterr) ), VerificationResult( d->ctx, Error(d->lasterr) ) ); } Error Context::startCombinedDecryptionAndVerification( const Data & cipherText, Data & plainText ) { d->lastop = Private::DecryptAndVerify; const Data::Private * const cdp = cipherText.impl(); Data::Private * const pdp = plainText.impl(); return Error( d->lasterr = gpgme_op_decrypt_verify_start( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ) ); } #ifdef HAVE_GPGME_OP_GETAUDITLOG unsigned int to_auditlog_flags( unsigned int flags ) { unsigned int result = 0; if ( flags & Context::HtmlAuditLog ) result |= GPGME_AUDITLOG_HTML; if ( flags & Context::AuditLogWithHelp ) result |= GPGME_AUDITLOG_WITH_HELP; return result; } #endif // HAVE_GPGME_OP_GETAUDITLOG Error Context::startGetAuditLog( Data & output, unsigned int flags ) { d->lastop = Private::GetAuditLog; #ifdef HAVE_GPGME_OP_GETAUDITLOG Data::Private * const odp = output.impl(); return Error( d->lasterr = gpgme_op_getauditlog_start( d->ctx, odp ? odp->data : 0, to_auditlog_flags( flags ) ) ); #else (void)output; (void)flags; return Error( d->lasterr = makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } Error Context::getAuditLog( Data & output, unsigned int flags ) { d->lastop = Private::GetAuditLog; #ifdef HAVE_GPGME_OP_GETAUDITLOG Data::Private * const odp = output.impl(); return Error( d->lasterr = gpgme_op_getauditlog( d->ctx, odp ? odp->data : 0, to_auditlog_flags( flags ) ) ); #else (void)output; (void)flags; return Error( d->lasterr = makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } void Context::clearSigningKeys() { gpgme_signers_clear( d->ctx ); } Error Context::addSigningKey( const Key & key ) { return Error( d->lasterr = gpgme_signers_add( d->ctx, key.impl() ) ); } Key Context::signingKey( unsigned int idx ) const { gpgme_key_t key = gpgme_signers_enum( d->ctx, idx ); return Key( key, false ); } std::vector Context::signingKeys() const { std::vector result; gpgme_key_t key; for ( unsigned int i = 0 ; ( key = gpgme_signers_enum( d->ctx, i ) ) ; ++i ) result.push_back( Key( key, false ) ); return result; } void Context::clearSignatureNotations() { #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET gpgme_sig_notation_clear( d->ctx ); #endif } GpgME::Error Context::addSignatureNotation( const char * name, const char * value, unsigned int flags ) { #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET return Error( gpgme_sig_notation_add( d->ctx, name, value, add_to_gpgme_sig_notation_flags_t( 0, flags ) ) ); #else (void)name; (void)value; (void)flags; return Error( makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } GpgME::Error Context::addSignaturePolicyURL( const char * url, bool critical ) { #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET return Error( gpgme_sig_notation_add( d->ctx, 0, url, critical ? GPGME_SIG_NOTATION_CRITICAL : 0 ) ); #else (void)url; (void)critical; return Error( makeError( GPG_ERR_NOT_IMPLEMENTED ) ); #endif } const char * Context::signaturePolicyURL() const { #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) if ( !n->name ) return n->value; #endif return 0; } Notation Context::signatureNotation( unsigned int idx ) const { #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) if ( n->name ) if ( idx-- == 0 ) return Notation( n ); #endif return Notation(); } std::vector Context::signatureNotations() const { std::vector result; #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) if ( n->name ) result.push_back( Notation( n ) ); #endif return result; } static gpgme_sig_mode_t sigmode2sigmode( SignatureMode mode ) { switch ( mode ) { default: case NormalSignatureMode: return GPGME_SIG_MODE_NORMAL; case Detached: return GPGME_SIG_MODE_DETACH; case Clearsigned: return GPGME_SIG_MODE_CLEAR; } } SigningResult Context::sign( const Data & plainText, Data & signature, SignatureMode mode ) { d->lastop = Private::Sign; const Data::Private * const pdp = plainText.impl(); Data::Private * const sdp = signature.impl(); d->lasterr = gpgme_op_sign( d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode( mode ) ); return SigningResult( d->ctx, Error(d->lasterr) ); } Error Context::startSigning( const Data & plainText, Data & signature, SignatureMode mode ) { d->lastop = Private::Sign; const Data::Private * const pdp = plainText.impl(); Data::Private * const sdp = signature.impl(); return Error( d->lasterr = gpgme_op_sign_start( d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode( mode ) ) ); } SigningResult Context::signingResult() const { if ( d->lastop & Private::Sign ) return SigningResult( d->ctx, Error(d->lasterr) ); else return SigningResult(); } static gpgme_encrypt_flags_t encryptflags2encryptflags( Context::EncryptionFlags flags ) { unsigned int result = 0; if ( flags & Context::AlwaysTrust ) result |= GPGME_ENCRYPT_ALWAYS_TRUST; #ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO if ( flags & Context::NoEncryptTo ) result |= GPGME_ENCRYPT_NO_ENCRYPT_TO; #endif return static_cast( result ); } EncryptionResult Context::encrypt( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { d->lastop = Private::Encrypt; #ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO if ( flags & NoEncryptTo ) return EncryptionResult( Error( d->lasterr = gpg_error( GPG_ERR_NOT_IMPLEMENTED ) ) ); #endif const Data::Private * const pdp = plainText.impl(); Data::Private * const cdp = cipherText.impl(); gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; gpgme_key_t * keys_it = keys; for ( std::vector::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; d->lasterr = gpgme_op_encrypt( d->ctx, keys, encryptflags2encryptflags( flags ), pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); delete[] keys; return EncryptionResult( d->ctx, Error(d->lasterr) ); } Error Context::encryptSymmetrically( const Data & plainText, Data & cipherText ) { d->lastop = Private::Encrypt; const Data::Private * const pdp = plainText.impl(); Data::Private * const cdp = cipherText.impl(); return Error( d->lasterr = gpgme_op_encrypt( d->ctx, 0, (gpgme_encrypt_flags_t)0, pdp ? pdp->data : 0, cdp ? cdp->data : 0 ) ); } Error Context::startEncryption( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { d->lastop = Private::Encrypt; #ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO if ( flags & NoEncryptTo ) return Error( d->lasterr = gpg_error( GPG_ERR_NOT_IMPLEMENTED ) ); #endif const Data::Private * const pdp = plainText.impl(); Data::Private * const cdp = cipherText.impl(); gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; gpgme_key_t * keys_it = keys; for ( std::vector::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; d->lasterr = gpgme_op_encrypt_start( d->ctx, keys, encryptflags2encryptflags( flags ), pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); delete[] keys; return Error( d->lasterr ); } EncryptionResult Context::encryptionResult() const { if ( d->lastop & Private::Encrypt ) return EncryptionResult( d->ctx, Error(d->lasterr) ); else return EncryptionResult(); } std::pair Context::signAndEncrypt( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { d->lastop = Private::SignAndEncrypt; const Data::Private * const pdp = plainText.impl(); Data::Private * const cdp = cipherText.impl(); gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; gpgme_key_t * keys_it = keys; for ( std::vector::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; d->lasterr = gpgme_op_encrypt_sign( d->ctx, keys, encryptflags2encryptflags( flags ), pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); delete[] keys; return std::make_pair( SigningResult( d->ctx, Error(d->lasterr) ), EncryptionResult( d->ctx, Error(d->lasterr) ) ); } Error Context::startCombinedSigningAndEncryption( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { d->lastop = Private::SignAndEncrypt; const Data::Private * const pdp = plainText.impl(); Data::Private * const cdp = cipherText.impl(); gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; gpgme_key_t * keys_it = keys; for ( std::vector::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) if ( it->impl() ) *keys_it++ = it->impl(); *keys_it++ = 0; d->lasterr = gpgme_op_encrypt_sign_start( d->ctx, keys, encryptflags2encryptflags( flags ), pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); delete[] keys; return Error( d->lasterr ); } Error Context::cancelPendingOperation() { #ifdef HAVE_GPGME_CANCEL_ASYNC return Error( gpgme_cancel_async( d->ctx ) ); #else return Error( gpgme_cancel( d->ctx ) ); #endif } bool Context::poll() { gpgme_error_t e = GPG_ERR_NO_ERROR; const bool finished = gpgme_wait( d->ctx, &e, 0 ); if ( finished ) d->lasterr = e; return finished; } Error Context::wait() { gpgme_error_t e = GPG_ERR_NO_ERROR; gpgme_wait( d->ctx, &e, 1 ); return Error( d->lasterr = e ); } Error Context::lastError() const { return Error( d->lasterr ); } std::ostream & operator<<( std::ostream & os, Protocol proto ) { os << "GpgME::Protocol("; switch ( proto ) { case OpenPGP: os << "OpenPGP"; break; case CMS: os << "CMS"; break; default: case UnknownProtocol: os << "UnknownProtocol"; break; } return os << ')'; } std::ostream & operator<<( std::ostream & os, Engine eng ) { os << "GpgME::Engine("; switch ( eng ) { case GpgEngine: os << "GpgEngine"; break; case GpgSMEngine: os << "GpgSMEngine"; break; case GpgConfEngine: os << "GpgConfEngine"; break; case AssuanEngine: os << "AssuanEngine"; default: case UnknownEngine: os << "UnknownEngine"; break; } return os << ')'; } std::ostream & operator<<( std::ostream & os, Context::CertificateInclusion incl ) { os << "GpgME::Context::CertificateInclusion(" << static_cast( incl ); switch ( incl ) { case Context::DefaultCertificates: os << "(DefaultCertificates)"; break; case Context::AllCertificatesExceptRoot: os << "(AllCertificatesExceptRoot)"; break; case Context::AllCertificates: os << "(AllCertificates)"; break; case Context::NoCertificates: os << "(NoCertificates)"; break; case Context::OnlySenderCertificate: os << "(OnlySenderCertificate)"; break; } return os << ')'; } std::ostream & operator<<( std::ostream & os, KeyListMode mode ) { os << "GpgME::KeyListMode("; #define CHECK( x ) if ( !(mode & (x)) ) {} else do { os << #x " "; } while (0) CHECK( Local ); CHECK( Extern ); CHECK( Signatures ); CHECK( Validate ); CHECK( Ephemeral ); #undef CHECK return os << ')'; } std::ostream & operator<<( std::ostream & os, SignatureMode mode ) { os << "GpgME::SignatureMode("; switch ( mode ) { #define CHECK( x ) case x: os << #x; break CHECK( NormalSignatureMode ); CHECK( Detached ); CHECK( Clearsigned ); #undef CHECK default: os << "???" "(" << static_cast( mode ) << ')'; break; } return os << ')'; } std::ostream & operator<<( std::ostream & os, Context::EncryptionFlags flags ) { os << "GpgME::Context::EncryptionFlags("; -#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) +#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) CHECK( AlwaysTrust ); #undef CHECK return os << ')'; } std::ostream & operator<<( std::ostream & os, Context::AuditLogFlags flags ) { os << "GpgME::Context::AuditLogFlags("; #define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) CHECK( HtmlAuditLog ); CHECK( AuditLogWithHelp ); #undef CHECK return os << ')'; } } // namespace GpgME GpgME::Error GpgME::setDefaultLocale( int cat, const char * val ) { return Error( gpgme_set_locale( 0, cat, val ) ); } GpgME::EngineInfo GpgME::engineInfo( GpgME::Protocol proto ) { gpgme_engine_info_t ei = 0; if ( gpgme_get_engine_info( &ei ) ) return EngineInfo(); const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; for ( gpgme_engine_info_t i = ei ; i ; i = i->next ) if ( i->protocol == p ) return EngineInfo( i ); return EngineInfo(); } GpgME::Error GpgME::checkEngine( GpgME::Protocol proto ) { const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; return Error( gpgme_engine_check_version( p ) ); } static gpgme_protocol_t UNKNOWN_PROTOCOL = static_cast( 255 ); static gpgme_protocol_t engine2protocol( const GpgME::Engine engine ) { switch ( engine ) { case GpgME::GpgEngine: return GPGME_PROTOCOL_OpenPGP; case GpgME::GpgSMEngine: return GPGME_PROTOCOL_CMS; case GpgME::GpgConfEngine: #ifdef HAVE_GPGME_PROTOCOL_GPGCONF return GPGME_PROTOCOL_GPGCONF; #else break; #endif case GpgME::AssuanEngine: #ifdef HAVE_GPGME_ASSUAN_ENGINE return GPGME_PROTOCOL_ASSUAN; #else break; #endif case GpgME::UnknownEngine: ; } return UNKNOWN_PROTOCOL; } GpgME::EngineInfo GpgME::engineInfo( GpgME::Engine engine ) { gpgme_engine_info_t ei = 0; if ( gpgme_get_engine_info( &ei ) ) return EngineInfo(); const gpgme_protocol_t p = engine2protocol( engine ); for ( gpgme_engine_info_t i = ei ; i ; i = i->next ) if ( i->protocol == p ) return EngineInfo( i ); return EngineInfo(); } GpgME::Error GpgME::checkEngine( GpgME::Engine engine ) { const gpgme_protocol_t p = engine2protocol( engine ); return Error( gpgme_engine_check_version( p ) ); } static const unsigned long supported_features = 0 | GpgME::ValidatingKeylistModeFeature | GpgME::CancelOperationFeature | GpgME::WrongKeyUsageFeature #ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT | GpgME::DefaultCertificateInclusionFeature #endif #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO | GpgME::GetSetEngineInfoFeature #endif #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET | GpgME::ClearAddGetSignatureNotationsFeature #endif #ifdef HAVE_GPGME_DATA_SET_FILE_NAME | GpgME::SetDataFileNameFeeature #endif #ifdef HAVE_GPGME_KEYLIST_MODE_SIG_NOTATIONS | GpgME::SignatureNotationsKeylistModeFeature #endif #ifdef HAVE_GPGME_KEY_SIG_NOTATIONS | GpgME::KeySignatureNotationsFeature #endif #ifdef HAVE_GPGME_KEY_T_IS_QUALIFIED | GpgME::KeyIsQualifiedFeature #endif #ifdef HAVE_GPGME_SIG_NOTATION_CRITICAL | GpgME::SignatureNotationsCriticalFlagFeature #endif #ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T | GpgME::SignatureNotationsFlagsFeature #endif #ifdef HAVE_GPGME_SIG_NOTATION_HUMAN_READABLE | GpgME::SignatureNotationsHumanReadableFlagFeature #endif #ifdef HAVE_GPGME_SUBKEY_T_IS_QUALIFIED | GpgME::SubkeyIsQualifiedFeature #endif #ifdef HAVE_GPGME_ENGINE_INFO_T_HOME_DIR | GpgME::EngineInfoHomeDirFeature #endif #ifdef HAVE_GPGME_DECRYPT_RESULT_T_FILE_NAME | GpgME::DecryptionResultFileNameFeature #endif #ifdef HAVE_GPGME_DECRYPT_RESULT_T_RECIPIENTS | GpgME::DecryptionResultRecipientsFeature #endif #ifdef HAVE_GPGME_VERIFY_RESULT_T_FILE_NAME | GpgME::VerificationResultFileNameFeature #endif #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS | GpgME::SignaturePkaFieldsFeature #endif #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS | GpgME::SignatureAlgorithmFieldsFeature #endif #ifdef HAVE_GPGME_GET_FDPTR | GpgME::FdPointerFeature #endif #ifdef HAVE_GPGME_OP_GETAUDITLOG | GpgME::AuditLogFeature #endif #ifdef HAVE_GPGME_PROTOCOL_GPGCONF | GpgME::GpgConfEngineFeature #endif #ifdef HAVE_GPGME_CANCEL_ASYNC | GpgME::CancelOperationAsyncFeature #endif #ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO | GpgME::NoEncryptToEncryptionFlagFeature #endif #ifdef HAVE_GPGME_SUBKEY_T_IS_CARDKEY | GpgME::CardKeyFeature #endif #ifdef HAVE_GPGME_ASSUAN_ENGINE | GpgME::AssuanEngineFeature #endif #ifdef HAVE_GPGME_KEYLIST_MODE_EPHEMERAL | GpgME::EphemeralKeylistModeFeature #endif #ifdef HAVE_GPGME_OP_IMPORT_KEYS | GpgME::ImportFromKeyserverFeature #endif ; bool GpgME::hasFeature( unsigned long features ) { return features == ( features & supported_features ); } diff --git a/gpgme++/context.h b/gpgme++/context.h index 0ec727c29..b16b85be3 100644 --- a/gpgme++/context.h +++ b/gpgme++/context.h @@ -1,320 +1,326 @@ /* context.h - wraps a gpgme key context Copyright (C) 2003, 2007 Klarälvdalens Datakonsult AB This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // -*- c++ -*- #ifndef __GPGMEPP_CONTEXT_H__ #define __GPGMEPP_CONTEXT_H__ #include #include #include // for Signature::Notation #include #include #include #include namespace GpgME { class Key; class Data; class TrustItem; class ProgressProvider; class PassphraseProvider; class EventLoopInteractor; class EditInteractor; class AssuanTransaction; class AssuanResult; class KeyListResult; class KeyGenerationResult; class ImportResult; class DecryptionResult; class VerificationResult; class SigningResult; class EncryptionResult; class EngineInfo; class GPGMEPP_EXPORT Context { explicit Context( gpgme_ctx_t ); public: //using GpgME::Protocol; // // Creation and destruction: // static Context * createForProtocol( Protocol proto ); + static std::auto_ptr createForEngine( Engine engine, Error * err=0 ); virtual ~Context(); // // Context Attributes // Protocol protocol() const; void setArmor( bool useArmor ); bool armor() const; void setTextMode( bool useTextMode ); bool textMode() const; enum CertificateInclusion { DefaultCertificates = -256, AllCertificatesExceptRoot = -2, AllCertificates = -1, NoCertificates = 0, OnlySenderCertificate = 1 }; void setIncludeCertificates( int which ); int includeCertificates() const; //using GpgME::KeyListMode; void setKeyListMode( unsigned int keyListMode ); void addKeyListMode( unsigned int keyListMode ); unsigned int keyListMode() const; void setPassphraseProvider( PassphraseProvider * provider ); PassphraseProvider * passphraseProvider() const; void setProgressProvider( ProgressProvider * provider ); ProgressProvider * progressProvider() const; void setManagedByEventLoopInteractor( bool managed ); bool managedByEventLoopInteractor() const; GpgME::Error setLocale( int category, const char * value ); EngineInfo engineInfo() const; GpgME::Error setEngineFileName( const char * filename ); GpgME::Error setEngineHomeDirectory( const char * filename ); private: friend class ::GpgME::EventLoopInteractor; void installIOCallbacks( gpgme_io_cbs * iocbs ); void uninstallIOCallbacks(); public: // // // Key Management // // // // Key Listing // GpgME::Error startKeyListing( const char * pattern=0, bool secretOnly=false ); GpgME::Error startKeyListing( const char * patterns[], bool secretOnly=false ); Key nextKey( GpgME::Error & e ); KeyListResult endKeyListing(); KeyListResult keyListResult() const; Key key( const char * fingerprint, GpgME::Error & e, bool secret=false ); // // Key Generation // KeyGenerationResult generateKey( const char * parameters, Data & pubKey ); GpgME::Error startKeyGeneration( const char * parameters, Data & pubkey ); KeyGenerationResult keyGenerationResult() const; // // Key Export // GpgME::Error exportPublicKeys( const char * pattern, Data & keyData ); GpgME::Error exportPublicKeys( const char * pattern[], Data & keyData ); GpgME::Error startPublicKeyExport( const char * pattern, Data & keyData ); GpgME::Error startPublicKeyExport( const char * pattern[], Data & keyData ); // // Key Import // ImportResult importKeys( const Data & data ); ImportResult importKeys( const std::vector & keys ); GpgME::Error startKeyImport( const Data & data ); GpgME::Error startKeyImport( const std::vector & keys ); ImportResult importResult() const; // // Key Deletion // GpgME::Error deleteKey( const Key & key, bool allowSecretKeyDeletion=false ); GpgME::Error startKeyDeletion( const Key & key, bool allowSecretKeyDeletion=false ); // // Key Editing // GpgME::Error edit( const Key & key, std::auto_ptr function, Data & out ); GpgME::Error startEditing( const Key & key, std::auto_ptr function, Data & out ); EditInteractor * lastEditInteractor() const; + std::auto_ptr takeLastEditInteractor(); // // SmartCard Editing // GpgME::Error cardEdit( const Key & key, std::auto_ptr function, Data & out ); GpgME::Error startCardEditing( const Key & key, std::auto_ptr function, Data & out ); EditInteractor * lastCardEditInteractor() const; + std::auto_ptr takeLastCardEditInteractor(); // // Trust Item Management // GpgME::Error startTrustItemListing( const char * pattern, int maxLevel ); TrustItem nextTrustItem( GpgME::Error & e ); GpgME::Error endTrustItemListing(); // // Assuan Transactions // AssuanResult assuanTransact( const char * command, std::auto_ptr transaction ); + AssuanResult assuanTransact( const char * command ); GpgME::Error startAssuanTransaction( const char * command, std::auto_ptr transaction ); + GpgME::Error startAssuanTransaction( const char * command ); AssuanResult assuanResult() const; AssuanTransaction * lastAssuanTransaction() const; + std::auto_ptr takeLastAssuanTransaction(); // // // Crypto Operations // // // // Decryption // DecryptionResult decrypt( const Data & cipherText, Data & plainText ); GpgME::Error startDecryption( const Data & cipherText, Data & plainText ); DecryptionResult decryptionResult() const; // // Signature Verification // VerificationResult verifyDetachedSignature( const Data & signature, const Data & signedText ); VerificationResult verifyOpaqueSignature( const Data & signedData, Data & plainText ); GpgME::Error startDetachedSignatureVerification( const Data & signature, const Data & signedText ); GpgME::Error startOpaqueSignatureVerification( const Data & signedData, Data & plainText ); VerificationResult verificationResult() const; // // Combined Decryption and Signature Verification // std::pair decryptAndVerify( const Data & cipherText, Data & plainText ); GpgME::Error startCombinedDecryptionAndVerification( const Data & cipherText, Data & plainText ); // use verificationResult() and decryptionResult() to retrieve the result objects... // // Signing // void clearSigningKeys(); GpgME::Error addSigningKey( const Key & signer ); Key signingKey( unsigned int index ) const; std::vector signingKeys() const; void clearSignatureNotations(); GpgME::Error addSignatureNotation( const char * name, const char * value, unsigned int flags=0 ); GpgME::Error addSignaturePolicyURL( const char * url, bool critical=false ); const char * signaturePolicyURL() const; Notation signatureNotation( unsigned int index ) const; std::vector signatureNotations() const; //using GpgME::SignatureMode; SigningResult sign( const Data & plainText, Data & signature, SignatureMode mode ); GpgME::Error startSigning( const Data & plainText, Data & signature, SignatureMode mode ); SigningResult signingResult() const; // // Encryption // enum EncryptionFlags { None=0, AlwaysTrust=1, NoEncryptTo=2 }; EncryptionResult encrypt( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ); GpgME::Error encryptSymmetrically( const Data & plainText, Data & cipherText ); GpgME::Error startEncryption( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ); EncryptionResult encryptionResult() const; // // Combined Signing and Encryption // std::pair signAndEncrypt( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ); GpgME::Error startCombinedSigningAndEncryption( const std::vector & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ); // use encryptionResult() and signingResult() to retrieve the result objects... // // // Audit Log // // enum AuditLogFlags { HtmlAuditLog = 1, AuditLogWithHelp = 128 }; GpgME::Error startGetAuditLog( Data & output, unsigned int flags=0 ); GpgME::Error getAuditLog( Data & output, unsigned int flags=0 ); // // // Run Control // // bool poll(); GpgME::Error wait(); GpgME::Error lastError() const; GpgME::Error cancelPendingOperation(); class Private; const Private * impl() const { return d; } Private * impl() { return d; } private: Private * d; private: // disable... Context( const Context & ); const Context & operator=( const Context & ); }; GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, Context::CertificateInclusion incl ); GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, Context::EncryptionFlags flags ); GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, Context::AuditLogFlags flags ); } // namespace GpgME #endif // __GPGMEPP_CONTEXT_H__ diff --git a/gpgme++/defaultassuantransaction.cpp b/gpgme++/defaultassuantransaction.cpp new file mode 100644 index 000000000..e112830f3 --- /dev/null +++ b/gpgme++/defaultassuantransaction.cpp @@ -0,0 +1,76 @@ +/* + defaultassuantransaction.cpp - default Assuan Transaction that just stores data and status lines + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#include "defaultassuantransaction.h" +#include "error.h" +#include "data.h" + +#include +#include +#include + +#include + +using namespace GpgME; +using namespace boost; + +DefaultAssuanTransaction::DefaultAssuanTransaction() + : AssuanTransaction(), + m_status(), + m_data() +{ + +} + +DefaultAssuanTransaction::~DefaultAssuanTransaction() {} + +Error DefaultAssuanTransaction::data( const char * data, size_t len ) { + m_data.append( data, len ); + return Error(); +} + +Data DefaultAssuanTransaction::inquire( const char * name, const char * args, Error & err ) { + (void)name; (void)args; (void)err; + return Data::null; +} + +Error DefaultAssuanTransaction::status( const char * status, const char * args ) { + m_status.push_back( std::pair( status, args ) ); + return Error(); +} + +std::vector DefaultAssuanTransaction::statusLine( const char * tag ) const { + std::vector result; + for ( std::vector< std::pair >::const_iterator it = m_status.begin(), end = m_status.end() ; it != end ; ++it ) + if ( it->first == tag ) + result.push_back( it->second ); + return result; +} + +std::string DefaultAssuanTransaction::firstStatusLine( const char * tag ) const { + for ( std::vector< std::pair >::const_iterator it = m_status.begin(), end = m_status.end() ; it != end ; ++it ) + if ( it->first == tag ) + return it->second; + return std::string(); +} diff --git a/gpgme++/defaultassuantransaction.h b/gpgme++/defaultassuantransaction.h new file mode 100644 index 000000000..536960ff5 --- /dev/null +++ b/gpgme++/defaultassuantransaction.h @@ -0,0 +1,57 @@ +/* + defaultassuantransaction.h - default Assuan Transaction that just stores data and status lines + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __GPGMEPP_DEFAULTASSUANTRANSACTION_H__ +#define __GPGMEPP_DEFAULTASSUANTRANSACTION_H__ + +#include + +#include +#include +#include + +namespace GpgME { + + class GPGMEPP_EXPORT DefaultAssuanTransaction : public AssuanTransaction { + public: + explicit DefaultAssuanTransaction(); + ~DefaultAssuanTransaction(); + + const std::vector< std::pair > & statusLines() const { return m_status; } + std::vector statusLine( const char * tag ) const; + std::string firstStatusLine( const char * tag ) const; + + const std::string & data() const { return m_data; } + + private: + /* reimp */ Error data( const char * data, size_t datalen ); + /* reimp */ Data inquire( const char * name, const char * args, Error & err ); + /* reimp */ Error status( const char * status, const char * args ); + + private: + std::vector< std::pair > m_status; + std::string m_data; + }; + +} // namespace GpgME + +#endif // __GPGMEPP_DEFAULTASSUANTRANSACTION_H__ diff --git a/gpgme++/global.h b/gpgme++/global.h index ffeb46ffb..8262c1203 100644 --- a/gpgme++/global.h +++ b/gpgme++/global.h @@ -1,130 +1,135 @@ /* global.h - global gpgme functions and enums Copyright (C) 2003, 2007 Klarälvdalens Datakonsult AB This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // -*- c++ -*- #ifndef __GPGMEPP_GLOBAL_H__ #define __GPGMEPP_GLOBAL_H__ #include #include #include namespace GpgME { class Error; class EngineInfo; class Context; } struct _GIOChannel; typedef struct _GIOChannel GIOChannel; class QIODevice; namespace GpgME { GPGMEPP_EXPORT void initializeLibrary(); + /*! + Initializes the library, returns Error::code() == + GPG_ERR_USER_1 if underlying gpgme is too old. + */ + GPGMEPP_EXPORT Error initializeLibrary(int); enum Protocol { OpenPGP, CMS, UnknownProtocol }; enum Engine { GpgEngine, GpgSMEngine, GpgConfEngine, UnknownEngine, AssuanEngine }; enum KeyListMode { Local = 0x1, Extern = 0x2, Signatures = 0x4, SignatureNotations = 0x8, Validate = 0x10, Ephemeral = 0x20 }; enum SignatureMode { NormalSignatureMode, Detached, Clearsigned }; GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, Protocol proto ); GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, Engine eng ); GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, KeyListMode mode ); GPGMEPP_EXPORT std::ostream & operator<<( std::ostream & os, SignatureMode mode ); GPGMEPP_EXPORT Error setDefaultLocale( int category, const char * value ); GPGMEPP_EXPORT Context * wait( Error & e, bool hang=true ); typedef void (*IdleFunction)(void); GPGMEPP_EXPORT IdleFunction registerIdleFunction( IdleFunction idleFunction ); typedef void (*IOCallback)( void * data, int fd ); GPGMEPP_EXPORT EngineInfo engineInfo( Protocol proto ); GPGMEPP_EXPORT EngineInfo engineInfo( Engine engine ); GPGMEPP_EXPORT Error checkEngine( Protocol proto ); GPGMEPP_EXPORT Error checkEngine( Engine engine ); GPGMEPP_EXPORT GIOChannel * getGIOChannel( int fd ); GPGMEPP_EXPORT QIODevice * getQIODevice( int fd ); enum Feature { ValidatingKeylistModeFeature = 0x00000001, CancelOperationFeature = 0x00000002, WrongKeyUsageFeature = 0x00000004, DefaultCertificateInclusionFeature = 0x00000008, GetSetEngineInfoFeature = 0x00000010, EngineInfoHomeDirFeature = 0x00000020, NoEncryptToEncryptionFlagFeature = 0x00000040, EphemeralKeylistModeFeature = 0x00000080, SetDataFileNameFeeature = 0x00000100, VerificationResultFileNameFeature = 0x00000200, DecryptionResultFileNameFeature = 0x00000400, DecryptionResultRecipientsFeature = 0x00000800, AuditLogFeature = 0x00001000, GpgConfEngineFeature = 0x00002000, CancelOperationAsyncFeature = 0x00004000, AssuanEngineFeature = 0x00008000, ClearAddGetSignatureNotationsFeature = 0x00010000, SignatureNotationsKeylistModeFeature = 0x00020000, KeySignatureNotationsFeature = 0x00040000, SignatureNotationsFlagsFeature = 0x00080000, SignatureNotationsCriticalFlagFeature = 0x00100000, SignatureNotationsHumanReadableFlagFeature = 0x00200000, CardKeyFeature = 0x00400000, ImportFromKeyserverFeature = 0x00800000, KeyIsQualifiedFeature = 0x01000200, SubkeyIsQualifiedFeature = 0x02000000, SignaturePkaFieldsFeature = 0x04000000, SignatureAlgorithmFieldsFeature = 0x08000000, FdPointerFeature = 0x10000000, // reserved // reserved // unusable (max value) FeatureMaxValue = 0x80000000 }; GPGMEPP_EXPORT bool hasFeature( unsigned long feature ); } // namespace GpgME #endif // __GPGMEPP_GLOBAL_H__ diff --git a/gpgme++/gpgagentgetinfoassuantransaction.cpp b/gpgme++/gpgagentgetinfoassuantransaction.cpp new file mode 100644 index 000000000..24b51b800 --- /dev/null +++ b/gpgme++/gpgagentgetinfoassuantransaction.cpp @@ -0,0 +1,120 @@ +/* + gpgagentgetinfoassuantransaction.cpp - Assuan Transaction to get information from gpg-agent + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#include "gpgagentgetinfoassuantransaction.h" +#include "error.h" +#include "data.h" + +#include +#include +#include + +#include + +using namespace GpgME; +using namespace boost; + +GpgAgentGetInfoAssuanTransaction::GpgAgentGetInfoAssuanTransaction( InfoItem item ) + : AssuanTransaction(), + m_item( item ), + m_command(), + m_data() +{ + +} + +GpgAgentGetInfoAssuanTransaction::~GpgAgentGetInfoAssuanTransaction() {} + +static unsigned long to_pid( const std::string & s ) { + std::stringstream ss( s ); + unsigned int result; + if ( ss >> result ) + return result; + else + return 0U; +} + +std::string GpgAgentGetInfoAssuanTransaction::version() const { + if ( m_item == Version ) + return m_data; + else + return std::string(); +} + +unsigned int GpgAgentGetInfoAssuanTransaction::pid() const { + if ( m_item == Pid ) + return to_pid( m_data ); + else + return 0U; +} + +std::string GpgAgentGetInfoAssuanTransaction::socketName() const { + if ( m_item == SocketName ) + return m_data; + else + return std::string(); +} + +std::string GpgAgentGetInfoAssuanTransaction::sshSocketName() const { + if ( m_item == SshSocketName ) + return m_data; + else + return std::string(); +} + +static const char * tokens[] = { + "version", + "pid", + "socket_name", + "ssh_socket_name", + "scd_running", +}; +BOOST_STATIC_ASSERT(( sizeof tokens / sizeof *tokens == GpgAgentGetInfoAssuanTransaction::LastInfoItem )); + +void GpgAgentGetInfoAssuanTransaction::makeCommand() const { + assert( m_item >= 0 ); + assert( m_item < LastInfoItem ); + m_command = "GETINFO "; + m_command += tokens[m_item]; +} + +const char * GpgAgentGetInfoAssuanTransaction::command() const { + makeCommand(); + return m_command.c_str(); +} + +Error GpgAgentGetInfoAssuanTransaction::data( const char * data, size_t len ) { + m_data.append( data, len ); + return Error(); +} + +Data GpgAgentGetInfoAssuanTransaction::inquire( const char * name, const char * args, Error & err ) { + (void)name; (void)args; (void)err; + return Data::null; +} + +Error GpgAgentGetInfoAssuanTransaction::status( const char * status, const char * args ) { + (void)status; (void)args; + return Error(); +} diff --git a/gpgme++/gpgagentgetinfoassuantransaction.h b/gpgme++/gpgagentgetinfoassuantransaction.h new file mode 100644 index 000000000..4f389354b --- /dev/null +++ b/gpgme++/gpgagentgetinfoassuantransaction.h @@ -0,0 +1,71 @@ +/* + gpgagentgetinfoassuantransaction.h - Assuan Transaction to get information from gpg-agent + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __GPGMEPP_GPGAGENTGETINFOASSUANTRANSACTION_H__ +#define __GPGMEPP_GPGAGENTGETINFOASSUANTRANSACTION_H__ + +#include + +#include +#include + +namespace GpgME { + + class GPGMEPP_EXPORT GpgAgentGetInfoAssuanTransaction : public AssuanTransaction { + public: + enum InfoItem { + Version, // string + Pid, // unsigned long + SocketName, // string (path) + SshSocketName, // string (path) + ScdRunning, // (none, returns GPG_ERR_GENERAL when scdaemon isn't running) + //CommandHasOption, // not supported + + LastInfoItem + }; + + explicit GpgAgentGetInfoAssuanTransaction( InfoItem item ); + ~GpgAgentGetInfoAssuanTransaction(); + + std::string version() const; + unsigned int pid() const; + std::string socketName() const; + std::string sshSocketName() const; + + private: + /* reimp */ const char * command() const; + /* reimp */ Error data( const char * data, size_t datalen ); + /* reimp */ Data inquire( const char * name, const char * args, Error & err ); + /* reimp */ Error status( const char * status, const char * args ); + + private: + void makeCommand() const; + + private: + InfoItem m_item; + mutable std::string m_command; + std::string m_data; + }; + +} // namespace GpgME + +#endif // __GPGMEPP_GPGAGENTGETINFOASSUANTRANSACTION_H__ diff --git a/gpgme++/interfaces/assuantransaction.h b/gpgme++/interfaces/assuantransaction.h index e4f93cbd0..05a60b2dd 100644 --- a/gpgme++/interfaces/assuantransaction.h +++ b/gpgme++/interfaces/assuantransaction.h @@ -1,45 +1,45 @@ /* assuantransaction.h - Interface for ASSUAN transactions Copyright (C) 2009 Klarälvdalens Datakonsult AB Author: Marc Mutz This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __GPGMEPP_INTERFACES_ASSUANTRANSACTION_H__ #define __GPGMEPP_INTERFACES_ASSUANTRANSACTION_H__ #include namespace GpgME { class Error; class Data; class GPGMEPP_EXPORT AssuanTransaction { public: virtual ~AssuanTransaction() {} - virtual Error data( const void * data, size_t datalen ) = 0; + virtual Error data( const char * data, size_t datalen ) = 0; virtual Data inquire( const char * name, const char * args, Error & err ) = 0; virtual Error status( const char * status, const char * args ) = 0; }; } // namespace GpgME #endif // __GPGMEPP_INTERFACES_ASSUANTRANSACTION_H__ diff --git a/gpgme++/scdgetinfoassuantransaction.cpp b/gpgme++/scdgetinfoassuantransaction.cpp new file mode 100644 index 000000000..cac89e4c5 --- /dev/null +++ b/gpgme++/scdgetinfoassuantransaction.cpp @@ -0,0 +1,145 @@ +/* + scdgetinfoassuantransaction.cpp - Assuan Transaction to get information from scdaemon + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#include "scdgetinfoassuantransaction.h" +#include "error.h" +#include "data.h" + +#include +#include +#include + +#include + +using namespace GpgME; +using namespace boost; + +ScdGetInfoAssuanTransaction::ScdGetInfoAssuanTransaction( InfoItem item ) + : AssuanTransaction(), + m_item( item ), + m_command(), + m_data() +{ + +} + +ScdGetInfoAssuanTransaction::~ScdGetInfoAssuanTransaction() {} + +static unsigned long to_pid( const std::string & s ) { + std::stringstream ss( s ); + unsigned int result; + if ( ss >> result ) + return result; + else + return 0U; +} + +static std::vector to_reader_list( const std::string & s ) { + std::vector result; + return split( result, s, is_any_of( "\n" ), token_compress_on ); +} + +static std::vector to_app_list( const std::string & s ) { + return to_reader_list( s ); +} + +std::string ScdGetInfoAssuanTransaction::version() const { + if ( m_item == Version ) + return m_data; + else + return std::string(); +} + +unsigned int ScdGetInfoAssuanTransaction::pid() const { + if ( m_item == Pid ) + return to_pid( m_data ); + else + return 0U; +} + +std::string ScdGetInfoAssuanTransaction::socketName() const { + if ( m_item == SocketName ) + return m_data; + else + return std::string(); +} + +char ScdGetInfoAssuanTransaction::status() const { + if ( m_item == Status && !m_data.empty() ) + return m_data[0]; + else + return '\0'; +} + +std::vector ScdGetInfoAssuanTransaction::readerList() const { + if ( m_item == ReaderList ) + return to_reader_list( m_data ); + else + return std::vector(); +} + +std::vector ScdGetInfoAssuanTransaction::applicationList() const { + if ( m_item == ApplicationList ) + return to_app_list( m_data ); + else + return std::vector(); +} + +static const char * tokens[] = { + "version", + "pid", + "socket_name", + "status", + "reader_list", + "deny_admin", + "app_list", +}; +BOOST_STATIC_ASSERT(( sizeof tokens / sizeof *tokens == ScdGetInfoAssuanTransaction::LastInfoItem )); + +void ScdGetInfoAssuanTransaction::makeCommand() const { + assert( m_item >= 0 ); + assert( m_item < LastInfoItem ); + m_command = "SCD GETINFO "; + m_command += tokens[m_item]; +} + +const char * ScdGetInfoAssuanTransaction::command() const { + makeCommand(); + return m_command.c_str(); +} + +Error ScdGetInfoAssuanTransaction::data( const char * data, size_t len ) { + m_data.append( data, len ); + return Error(); +} + +Data ScdGetInfoAssuanTransaction::inquire( const char * name, const char * args, Error & err ) { + (void)name; (void)args; (void)err; + return Data::null; +} + +Error ScdGetInfoAssuanTransaction::status( const char * status, const char * args ) { + (void)status; (void)args; + return Error(); +} diff --git a/gpgme++/scdgetinfoassuantransaction.h b/gpgme++/scdgetinfoassuantransaction.h new file mode 100644 index 000000000..cb11a9b83 --- /dev/null +++ b/gpgme++/scdgetinfoassuantransaction.h @@ -0,0 +1,74 @@ +/* + scdgetinfoassuantransaction.h - Assuan Transaction to get information from scdaemon + Copyright (C) 2009 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __GPGMEPP_SCDGETINFOASSUANTRANSACTION_H__ +#define __GPGMEPP_SCDGETINFOASSUANTRANSACTION_H__ + +#include + +#include +#include + +namespace GpgME { + + class GPGMEPP_EXPORT ScdGetInfoAssuanTransaction : public AssuanTransaction { + public: + enum InfoItem { + Version, // string + Pid, // unsigned long + SocketName, // string (path) + Status, // char (status) + ReaderList, // string list + DenyAdmin, // (none, returns GPG_ERR_GENERAL when admin commands are allowed) + ApplicationList, // string list + + LastInfoItem + }; + + explicit ScdGetInfoAssuanTransaction( InfoItem item ); + ~ScdGetInfoAssuanTransaction(); + + std::string version() const; + unsigned int pid() const; + std::string socketName() const; + char status() const; + std::vector readerList() const; + std::vector applicationList() const; + + private: + /* reimp */ const char * command() const; + /* reimp */ Error data( const char * data, size_t datalen ); + /* reimp */ Data inquire( const char * name, const char * args, Error & err ); + /* reimp */ Error status( const char * status, const char * args ); + + private: + void makeCommand() const; + + private: + InfoItem m_item; + mutable std::string m_command; + std::string m_data; + }; + +} // namespace GpgME + +#endif // __GPGMEPP_SCDGETINFOASSUANTRANSACTION_H__ diff --git a/kcal/htmlexport.cpp b/kcal/htmlexport.cpp index 9d09d20df..db7f1fe79 100644 --- a/kcal/htmlexport.cpp +++ b/kcal/htmlexport.cpp @@ -1,782 +1,781 @@ /* This file is part of the kcal library. Copyright (c) 2000,2001 Cornelius Schumacher Copyright (C) 2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "htmlexport.h" #include "htmlexportsettings.h" #include "incidenceformatter.h" #include "calendar.h" #include "event.h" #include "todo.h" #ifndef KORG_NOKABC #include "kabc/stdaddressbook.h" #endif #include #include #include #include #include #include #include #include #include #include using namespace KCal; static QString cleanChars( const QString &txt ); //@cond PRIVATE class KCal::HtmlExport::Private { public: Private( Calendar *calendar, HTMLExportSettings *settings ) : mCalendar( calendar ), mSettings( settings ) {} Calendar *mCalendar; HTMLExportSettings *mSettings; QMap mHolidayMap; }; //@endcond HtmlExport::HtmlExport( Calendar *calendar, HTMLExportSettings *settings ) : d( new Private( calendar, settings ) ) { } HtmlExport::~HtmlExport() { delete d; } bool HtmlExport::save( const QString &fileName ) { QString fn( fileName ); if ( fn.isEmpty() && d->mSettings ) { fn = d->mSettings->outputFile(); } if ( !d->mSettings || fn.isEmpty() ) { return false; } QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly ) ) { return false; } QTextStream ts( &f ); bool success = save( &ts ); f.close(); return success; } bool HtmlExport::save( QTextStream *ts ) { if ( !d->mSettings ) { return false; } ts->setCodec( "UTF-8" ); - // Write HTML header *ts << "\n"; + *ts << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"<" << endl; *ts << " \n"; + *ts << "UTF-8\" />"<mSettings->pageTitle().isEmpty() ) { - *ts << " " << d->mSettings->pageTitle() << "\n"; + *ts << " " << d->mSettings->pageTitle() << ""<\n"; + *ts << " \n"; - *ts << "\n"; + *ts << " "<"<mSettings->eventView() || d->mSettings->monthView() || d->mSettings->weekView() ) { if ( !d->mSettings->eventTitle().isEmpty() ) { - *ts << "

" << d->mSettings->eventTitle() << "

\n"; + *ts << "

" << d->mSettings->eventTitle() << "

"<mSettings->weekView() ) { createWeekView( ts ); } // Write Month View if ( d->mSettings->monthView() ) { createMonthView( ts ); } // Write Event List if ( d->mSettings->eventView() ) { createEventList( ts ); } } // Write Todo List if ( d->mSettings->todoView() ) { if ( !d->mSettings->todoListTitle().isEmpty() ) { - *ts << "

" << d->mSettings->todoListTitle() << "

\n"; + *ts << "

" << d->mSettings->todoListTitle() << "

"<mSettings->journalView() ) { if ( !d->mSettings->journalTitle().isEmpty() ) { - *ts << "

" << d->mSettings->journalTitle() << "

\n"; + *ts << "

" << d->mSettings->journalTitle() << "

"<mSettings->freeBusyView() ) { if ( !d->mSettings->freeBusyTitle().isEmpty() ) { - *ts << "

" << d->mSettings->freeBusyTitle() << "

\n"; + *ts << "

" << d->mSettings->freeBusyTitle() << "

"<\n"; + *ts << ""<" << i18nc( "@title month and year", "%1 %2", hMon, hYear ) - << "\n"; + << ""<weekStartDay() == 1 ) { start = start.addDays( 1 - start.dayOfWeek() ); } else { if ( start.dayOfWeek() != 7 ) { start = start.addDays( -start.dayOfWeek() ); } } - *ts << "\n"; + *ts << "
"<"; for ( int i=0; i < 7; ++i ) { *ts << ""; } - *ts << "\n"; + *ts << ""<\n"; + *ts << " "<
" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "
"; *ts << "
mHolidayMap.contains( start ) || start.dayOfWeek() == 7 ) { *ts << "class=\"dateholiday\""; } else { *ts << "class=\"date\""; } *ts << ">" << QString::number( start.day() ); if ( d->mHolidayMap.contains( start ) ) { *ts << " " << d->mHolidayMap[start] << ""; } *ts << "
"; Event::List events = d->mCalendar->events( start, d->mCalendar->timeSpec(), EventSortStartDate, SortDirectionAscending ); if ( events.count() ) { *ts << ""; Event::List::ConstIterator it; for ( it = events.constBegin(); it != events.constEnd(); ++it ) { if ( checkSecrecy( *it ) ) { createEvent( ts, *it, start, false ); } } *ts << "
"; } else { *ts << " "; } - *ts << "
\n"; + *ts << ""<\n"; + *ts << " "<\n"; + *ts << ""< 12 ) { startyear += 1; startmonth = 1; } start.setYMD( startyear, startmonth, 1 ); end.setYMD( start.year(), start.month(), start.daysInMonth() ); } } void HtmlExport::createEventList( QTextStream *ts ) { int columns = 3; - *ts << "\n"; - *ts << " \n"; + *ts << "
"<"<" << i18nc( "@title:column event start time", - "Start Time" ) << "\n"; + "Start Time" ) << ""<" << i18nc( "@title:column event end time", - "End Time" ) << "\n"; + "End Time" ) << ""<" << i18nc( "@title:column event description", - "Event" ) << "\n"; + "Event" ) << ""<mSettings->eventLocation() ) { *ts << " \n"; + "Location" ) << ""<mSettings->eventCategories() ) { *ts << " \n"; + "Categories" ) << ""<mSettings->eventAttendees() ) { *ts << " \n"; + "Attendees" ) << ""<\n"; + *ts << " "<mCalendar->events( dt, d->mCalendar->timeSpec(), EventSortStartDate, SortDirectionAscending ); if ( events.count() ) { *ts << " \n"; + << ""<\n"; + *ts << "
" << i18nc( "@title:column event locatin", - "Location" ) << "" << i18nc( "@title:column event categories", - "Categories" ) << "" << i18nc( "@title:column event attendees", - "Attendees" ) << "
" << KGlobal::locale()->formatDate( dt ) - << "
"<summary(); - *ts << " \n"; + *ts << " "<allDay() ) { if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtStart().date() != date ) ) { - *ts << "  \n"; + *ts << "  "<" << IncidenceFormatter::timeToString( event->dtStart(), true, d->mCalendar->timeSpec() ) - << "\n"; + << ""<isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtEnd().date() != date ) ) { - *ts << "  \n"; + *ts << "  "<" << IncidenceFormatter::timeToString( event->dtEnd(), true, d->mCalendar->timeSpec() ) - << "\n"; + << ""<  \n"; + *ts << "   "<\n"; - *ts << " " << cleanChars( event->summary() ) << "\n"; + *ts << " "<" << cleanChars( event->summary() ) << ""<description().isEmpty() ) { - *ts << "

" << breakString( cleanChars( event->description() ) ) << "

\n"; + *ts << "

" << breakString( cleanChars( event->description() ) ) << "

"<\n"; + *ts << " "<mSettings->eventLocation() ) { - *ts << " \n"; + *ts << " "<\n"; + *ts << " "<mSettings->eventCategories() ) { - *ts << " \n"; + *ts << " "<\n"; + *ts << " "<mSettings->eventAttendees() ) { - *ts << " \n"; + *ts << " "<\n"; + *ts << " "<\n"; + *ts << " "<mCalendar->todos(); int index = 0; while ( index < rawTodoList.count() ) { Todo *ev = rawTodoList[ index ]; Todo *subev = ev; if ( ev->relatedTo() ) { if ( ev->relatedTo()->type() == "Todo" ) { if ( !rawTodoList.contains( static_cast( ev->relatedTo() ) ) ) { rawTodoList.append( static_cast( ev->relatedTo() ) ); } } } index = rawTodoList.indexOf( subev ); ++index; } // FIXME: Sort list by priorities. This is brute force and should be // replaced by a real sorting algorithm. Todo::List todoList; Todo::List::ConstIterator it; for ( int i = 1; i <= 9; ++i ) { for ( it = rawTodoList.constBegin(); it != rawTodoList.constEnd(); ++it ) { if ( (*it)->priority() == i && checkSecrecy( *it ) ) { todoList.append( *it ); } } } for ( it = rawTodoList.constBegin(); it != rawTodoList.constEnd(); ++it ) { if ( (*it)->priority() == 0 && checkSecrecy( *it ) ) { todoList.append( *it ); } } int columns = 3; - *ts << "\n"; - *ts << " \n"; - *ts << " \n"; - *ts << " \n"; - *ts << " \n"; + *ts << "
" << i18nc( "@title:column", "To-do" ) << "" << i18nc( "@title:column to-do priority", "Priority" ) << "" << i18nc( "@title:column to-do percent completed", "Completed" ) << "
"<"<" << i18nc( "@title:column", "To-do" ) << ""<" << i18nc( "@title:column to-do priority", "Priority" ) << ""<" << i18nc( "@title:column to-do percent completed", "Completed" ) << ""<mSettings->taskDueDate() ) { - *ts << " \n"; + *ts << " "<mSettings->taskLocation() ) { - *ts << " \n"; + *ts << " "<mSettings->taskCategories() ) { - *ts << " \n"; + *ts << " "<mSettings->taskAttendees() ) { - *ts << " \n"; + *ts << " "<\n"; + *ts << " "<relatedTo() ) { createTodo( ts, *it ); } } // Create sub-level lists for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { Incidence::List relations = (*it)->relations(); if ( relations.count() ) { // Generate sub-to-do list - *ts << " \n"; + *ts << " "<uid() << "\">" << i18nc( "@title:column sub-to-dos of the parent to-do", "Sub-To-dos of: " ) << "uid() << "\">" << cleanChars( (*it)->summary() ) - << "\n"; - *ts << " \n"; + << ""<"<( *it2 ); if ( ev3 && ev3->priority() == i ) { sortedList.append( ev3 ); } } } Incidence::List::ConstIterator it2; for ( it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2 ) { Todo *ev3 = dynamic_cast( *it2 ); if ( ev3 && ev3->priority() == 0 ) { sortedList.append( ev3 ); } } Todo::List::ConstIterator it3; for ( it3 = sortedList.constBegin(); it3 != sortedList.constEnd(); ++it3 ) { createTodo( ts, *it3 ); } } } - *ts << "
" << i18nc( "@title:column to-do due date", "Due Date" ) << "" << i18nc( "@title:column to-do due date", "Due Date" ) << "" << i18nc( "@title:column to-do location", "Location" ) << "" << i18nc( "@title:column to-do location", "Location" ) << "" << i18nc( "@title:column to-do categories", "Categories" ) << "" << i18nc( "@title:column to-do categories", "Categories" ) << "" << i18nc( "@title:column to-do attendees", "Attendees" ) << "" << i18nc( "@title:column to-do attendees", "Attendees" ) << "
\n"; + *ts << ""<isCompleted(); Incidence::List relations = todo->relations(); - *ts << "\n"; + *ts << ""<\n"; - *ts << " uid() << "\">\n"; - *ts << " " << cleanChars( todo->summary() ) << "\n"; + *ts << "\">"<uid() << "\">"<" << cleanChars( todo->summary() ) << ""<description().isEmpty() ) { - *ts << "

" << breakString( cleanChars( todo->description() ) ) << "

\n"; + *ts << "

" << breakString( cleanChars( todo->description() ) ) << "

"<uid() << "\">" << i18nc( "@title:column sub-to-dos of the parent to-do", - "Sub-To-dos" ) << "\n"; + "Sub-To-dos" ) << ""<\n"; + *ts << " "<\n"; - *ts << " " << todo->priority() << "\n"; - *ts << " \n"; + *ts << ">"<priority() <"<\n"; + *ts << ">"<percentComplete() ) << "\n"; - *ts << " \n"; + "%1 %", todo->percentComplete() ) <"<mSettings->taskDueDate() ) { *ts << " \n"; + *ts << ">"<hasDueDate() ) { - *ts << " " << IncidenceFormatter::dateToString(todo->dtDue(true)) << "\n"; + *ts << " " << IncidenceFormatter::dateToString(todo->dtDue(true)) <\n"; + *ts << " "<mSettings->taskLocation() ) { *ts << " \n"; + *ts << ">"<\n"; + *ts << " "<mSettings->taskCategories() ) { *ts << " \n"; + *ts << ">"<\n"; + *ts << " "<mSettings->taskAttendees() ) { *ts << " \n"; + *ts << ">"<\n"; + *ts << " "<\n"; + *ts << ""<mCalendar->journals(); // FIXME: Implement this! } void HtmlExport::createFreeBusyView( QTextStream *ts ) { Q_UNUSED( ts ); // FIXME: Implement this! } bool HtmlExport::checkSecrecy( Incidence *incidence ) { int secrecy = incidence->secrecy(); if ( secrecy == Incidence::SecrecyPublic ) { return true; } if ( secrecy == Incidence::SecrecyPrivate && !d->mSettings->excludePrivate() ) { return true; } if ( secrecy == Incidence::SecrecyConfidential && !d->mSettings->excludeConfidential() ) { return true; } return false; } void HtmlExport::formatLocation( QTextStream *ts, Incidence *incidence ) { if ( !incidence->location().isEmpty() ) { - *ts << " " << cleanChars( incidence->location() ) << "\n"; + *ts << " " << cleanChars( incidence->location() ) <categoriesStr().isEmpty() ) { - *ts << " " << cleanChars( incidence->categoriesStr() ) << "\n"; + *ts << " " << cleanChars( incidence->categoriesStr() ) <attendees(); if ( attendees.count() ) { *ts << ""; #ifndef KORG_NOKABC KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); KABC::Addressee::List addressList; addressList = add_book->findByEmail( incidence->organizer().email() ); if ( !addressList.isEmpty() ) { KABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { *ts << "organizer().email() << "\">"; - *ts << cleanChars( o.formattedName() ) << "\n"; + *ts << cleanChars( o.formattedName() ) << ""<organizer().fullName(); } } #else *ts << incidence->organizer().fullName(); #endif *ts << "
"; Attendee::List::ConstIterator it; for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { Attendee *a = *it; if ( !a->email().isEmpty() ) { *ts << "email(); *ts << "\">" << cleanChars( a->name() ) << ""; } else { *ts << " " << cleanChars( a->name() ); } - *ts << "
" << "\n"; + *ts << "
" <"; } return out; } } void HtmlExport::createFooter( QTextStream *ts ) { // FIXME: Implement this in a translatable way! QString trailer = i18nc( "@info/plain", "This page was created " ); /* bool hasPerson = false; bool hasCredit = false; bool hasCreditURL = false; QString mail, name, credit, creditURL;*/ if ( !d->mSettings->eMail().isEmpty() ) { if ( !d->mSettings->name().isEmpty() ) { trailer += i18nc( "@info/plain page creator email link with name", "by %2", d->mSettings->eMail(), d->mSettings->name() ); } else { trailer += i18nc( "@info/plain page creator email link", "by %2", d->mSettings->eMail(), d->mSettings->eMail() ); } } else { if ( !d->mSettings->name().isEmpty() ) { trailer += i18nc( "@info/plain page creator name only", "by %1 ", d->mSettings->name() ); } } if ( !d->mSettings->creditName().isEmpty() ) { if ( !d->mSettings->creditURL().isEmpty() ) { trailer += i18nc( "@info/plain page credit with name and link", "with %2", d->mSettings->creditURL(), d->mSettings->creditName() ); } else { trailer += i18nc( "@info/plain page credit name only", "with %1", d->mSettings->creditName() ); } } - *ts << "

" << trailer << "

\n"; + *ts << "

" << trailer << "

"<', ">" ); txt = txt.replace( '\"', """ ); txt = txt.replace( QString::fromUtf8( "ä" ), "ä" ); txt = txt.replace( QString::fromUtf8( "Ă„" ), "Ä" ); txt = txt.replace( QString::fromUtf8( "ö" ), "ö" ); txt = txt.replace( QString::fromUtf8( "Ă–" ), "Ö" ); txt = txt.replace( QString::fromUtf8( "ĂĽ" ), "ü" ); txt = txt.replace( QString::fromUtf8( "Ăś" ), "Ü" ); txt = txt.replace( QString::fromUtf8( "Ăź" ), "ß" ); txt = txt.replace( QString::fromUtf8( "€" ), "€" ); txt = txt.replace( QString::fromUtf8( "Ă©" ), "é" ); return txt; } QString HtmlExport::styleSheet() const { if ( !d->mSettings->styleSheet().isEmpty() ) { return d->mSettings->styleSheet(); } QString css; if ( QApplication::isRightToLeft() ) { css += " body { background-color:white; color:black; direction: rtl }\n"; css += " td { text-align:center; background-color:#eee }\n"; css += " th { text-align:center; background-color:#228; color:white }\n"; css += " td.sumdone { background-color:#ccc }\n"; css += " td.done { background-color:#ccc }\n"; css += " td.subhead { text-align:center; background-color:#ccf }\n"; css += " td.datehead { text-align:center; background-color:#ccf }\n"; css += " td.space { background-color:white }\n"; css += " td.dateholiday { color:red }\n"; } else { css += " body { background-color:white; color:black }\n"; css += " td { text-align:center; background-color:#eee }\n"; css += " th { text-align:center; background-color:#228; color:white }\n"; css += " td.sum { text-align:left }\n"; css += " td.sumdone { text-align:left; background-color:#ccc }\n"; css += " td.done { background-color:#ccc }\n"; css += " td.subhead { text-align:center; background-color:#ccf }\n"; css += " td.datehead { text-align:center; background-color:#ccf }\n"; css += " td.space { background-color:white }\n"; css += " td.date { text-align:left }\n"; css += " td.dateholiday { text-align:left; color:red }\n"; } return css; } void HtmlExport::addHoliday( const QDate &date, const QString &name ) { if ( d->mHolidayMap[date].isEmpty() ) { d->mHolidayMap[date] = name; } else { d->mHolidayMap[date] = i18nc( "@info/plain holiday by date and name", "%1, %2", d->mHolidayMap[date], name ); } } QDate HtmlExport::fromDate() const { return d->mSettings->dateStart().date(); } QDate HtmlExport::toDate() const { return d->mSettings->dateEnd().date(); } diff --git a/kcal/incidenceformatter.cpp b/kcal/incidenceformatter.cpp index ee592f673..f2c46b2d1 100644 --- a/kcal/incidenceformatter.cpp +++ b/kcal/incidenceformatter.cpp @@ -1,2743 +1,2756 @@ /* This file is part of the kcal library. Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2004 Reinhold Kainhofer Copyright (c) 2005 Rafal Rzepecki Copyright (c) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** @file This file is part of the API for handling calendar data and provides static functions for formatting Incidences for various purposes. @brief Provides methods to format Incidences in various ways for display purposes. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "incidenceformatter.h" #include "attachment.h" #include "event.h" #include "todo.h" #include "journal.h" #include "calendar.h" #include "calendarlocal.h" #include "icalformat.h" #include "freebusy.h" #include "calendarresources.h" #include "kpimutils/email.h" #include "kabc/phonenumber.h" #include "kabc/vcardconverter.h" #include "kabc/stdaddressbook.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KCal; /******************************************************************* * Helper functions for the extensive display (event viewer) *******************************************************************/ //@cond PRIVATE static QString eventViewerAddLink( const QString &ref, const QString &text, bool newline = true ) { QString tmpStr( "" + text + "" ); if ( newline ) { tmpStr += '\n'; } return tmpStr; } static QString eventViewerAddTag( const QString &tag, const QString &text ) { int numLineBreaks = text.count( "\n" ); QString str = '<' + tag + '>'; QString tmpText = text; QString tmpStr = str; if( numLineBreaks >= 0 ) { if ( numLineBreaks > 0 ) { int pos = 0; QString tmp; for ( int i = 0; i <= numLineBreaks; ++i ) { pos = tmpText.indexOf( "\n" ); tmp = tmpText.left( pos ); tmpText = tmpText.right( tmpText.length() - pos - 1 ); tmpStr += tmp + "
"; } } else { tmpStr += tmpText; } } tmpStr += "'; return tmpStr; } static QString eventViewerFormatCategories( Incidence *event ) { QString tmpStr; if ( !event->categoriesStr().isEmpty() ) { if ( event->categories().count() == 1 ) { tmpStr = eventViewerAddTag( "h3", i18n( "Category" ) ); } else { tmpStr = eventViewerAddTag( "h3", i18n( "Categories" ) ); } tmpStr += eventViewerAddTag( "p", event->categoriesStr() ); } return tmpStr; } static QString linkPerson( const QString &email, QString name, QString uid, const QString &iconPath ) { // Make the search, if there is an email address to search on, // and either name or uid is missing if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); KABC::Addressee::List addressList = add_book->findByEmail( email ); KABC::Addressee o = ( !addressList.isEmpty() ? addressList.first() : KABC::Addressee() ); if ( !o.isEmpty() && addressList.size() < 2 ) { if ( name.isEmpty() ) { // No name set, so use the one from the addressbook name = o.formattedName(); } uid = o.uid(); } else { // Email not found in the addressbook. Don't make a link uid.clear(); } } // Show the attendee QString tmpString = "
  • "; if ( !uid.isEmpty() ) { // There is a UID, so make a link to the addressbook if ( name.isEmpty() ) { // Use the email address for text tmpString += eventViewerAddLink( "uid:" + uid, email ); } else { tmpString += eventViewerAddLink( "uid:" + uid, name ); } } else { // No UID, just show some text tmpString += ( name.isEmpty() ? email : name ); } tmpString += '\n'; // Make the mailto link if ( !email.isEmpty() && !iconPath.isNull() ) { KUrl mailto; mailto.setProtocol( "mailto" ); mailto.setPath( email ); tmpString += eventViewerAddLink( mailto.url(), "" ); } tmpString += "
  • \n"; return tmpString; } static QString eventViewerFormatAttendees( Incidence *event ) { QString tmpStr; Attendee::List attendees = event->attendees(); if ( attendees.count() ) { KIconLoader *iconLoader = KIconLoader::global(); const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small ); // Add organizer link tmpStr += eventViewerAddTag( "h4", i18n( "Organizer" ) ); tmpStr += "
      "; tmpStr += linkPerson( event->organizer().email(), event->organizer().name(), QString(), iconPath ); tmpStr += "
    "; // Add attendees links tmpStr += eventViewerAddTag( "h4", i18n( "Attendees" ) ); tmpStr += "
      "; Attendee::List::ConstIterator it; for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { Attendee *a = *it; tmpStr += linkPerson( a->email(), a->name(), a->uid(), iconPath ); if ( !a->delegator().isEmpty() ) { tmpStr += i18n( " (delegated by %1)", a->delegator() ); } if ( !a->delegate().isEmpty() ) { tmpStr += i18n( " (delegated to %1)", a->delegate() ); } } tmpStr += "
    "; } return tmpStr; } static QString eventViewerFormatAttachments( Incidence *i ) { QString tmpStr; Attachment::List as = i->attachments(); if ( as.count() > 0 ) { Attachment::List::ConstIterator it; for ( it = as.constBegin(); it != as.constEnd(); ++it ) { if ( (*it)->isUri() ) { tmpStr += eventViewerAddLink( (*it)->uri(), (*it)->label() ); tmpStr += "
    "; } } } return tmpStr; } /* FIXME:This function depends of kaddressbook. Is necessary a new type of event? */ static QString eventViewerFormatBirthday( Event *event ) { if ( !event ) { return QString(); } if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" ) { return QString(); } QString uid_1 = event->customProperty( "KABC", "UID-1" ); QString name_1 = event->customProperty( "KABC", "NAME-1" ); QString email_1= event->customProperty( "KABC", "EMAIL-1" ); KIconLoader *iconLoader = KIconLoader::global(); const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small ); //TODO: add a tart icon QString tmpString = "
      "; tmpString += linkPerson( email_1, name_1, uid_1, iconPath ); if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { QString uid_2 = event->customProperty( "KABC", "UID-2" ); QString name_2 = event->customProperty( "KABC", "NAME-2" ); QString email_2= event->customProperty( "KABC", "EMAIL-2" ); tmpString += linkPerson( email_2, name_2, uid_2, iconPath ); } tmpString += "
    "; return tmpString; } static QString eventViewerFormatHeader( Incidence *incidence ) { QString tmpStr = ""; // show icons KIconLoader *iconLoader = KIconLoader::global(); tmpStr += ""; tmpStr += ""; tmpStr += "
    "; // TODO: KDE5. Make the function QString Incidence::getPixmap() so we don't // need downcasting. if ( incidence->type() == "Todo" ) { tmpStr += "( incidence ); if ( !todo->isCompleted() ) { tmpStr += iconLoader->iconPath( "view-calendar-tasks", KIconLoader::Small ); } else { tmpStr += iconLoader->iconPath( "task-complete", KIconLoader::Small ); } tmpStr += "\">"; } if ( incidence->type() == "Event" ) { tmpStr += "iconPath( "view-calendar-day", KIconLoader::Small ) + "\">"; } if ( incidence->type() == "Journal" ) { tmpStr += "iconPath( "view-pim-journal", KIconLoader::Small ) + "\">"; } if ( incidence->isAlarmEnabled() ) { tmpStr += "iconPath( "preferences-desktop-notification-bell", KIconLoader::Small ) + "\">"; } if ( incidence->recurs() ) { tmpStr += "iconPath( "edit-redo", KIconLoader::Small ) + "\">"; } if ( incidence->isReadOnly() ) { tmpStr += "iconPath( "object-locked", KIconLoader::Small ) + "\">"; } tmpStr += "" + eventViewerAddTag( "h2", incidence->richSummary() ) + "
    "; return tmpStr; } static QString eventViewerFormatEvent( Event *event, KDateTime::Spec spec ) { if ( !event ) { return QString(); } QString tmpStr = eventViewerFormatHeader( event ); tmpStr += ""; if ( !event->location().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } tmpStr += ""; if ( event->allDay() ) { if ( event->isMultiDay() ) { tmpStr += ""; tmpStr += ""; } else { tmpStr += ""; tmpStr += ""; } } else { if ( event->isMultiDay() ) { tmpStr += ""; tmpStr += ""; } else { tmpStr += ""; if ( event->hasEndDate() && event->dtStart() != event->dtEnd() ) { tmpStr += ""; } else { tmpStr += ""; } tmpStr += ""; tmpStr += ""; tmpStr += ""; } } tmpStr += ""; if ( event->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += "
    " + i18n( "Location" ) + "" + event->richLocation() + "
    " + i18n( "Time" ) + "" + i18nc( " - ","%1 - %2", IncidenceFormatter::dateToString( event->dtStart(), true, spec ), IncidenceFormatter::dateToString( event->dtEnd(), true, spec ) ) + "" + i18n( "Date" ) + "" + i18nc( "date as string","%1", IncidenceFormatter::dateToString( event->dtStart(), true, spec ) ) + "" + i18n( "Time" ) + "" + i18nc( " - ","%1 - %2", IncidenceFormatter::dateToString( event->dtStart(), true, spec ), IncidenceFormatter::dateToString( event->dtEnd(), true, spec ) ) + "" + i18n( "Time" ) + "" + i18nc( " - ","%1 - %2", IncidenceFormatter::timeToString( event->dtStart(), true, spec ), IncidenceFormatter::timeToString( event->dtEnd(), true, spec ) ) + "" + IncidenceFormatter::timeToString( event->dtStart(), true, spec ) + "
    " + i18n( "Date" ) + "" + i18nc( "date as string","%1", IncidenceFormatter::dateToString( event->dtStart(), true, spec ) ) + "
    " + i18n( "Birthday" ) + "" + eventViewerFormatBirthday( event ) + "
    "; return tmpStr; } if ( !event->description().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += "" + eventViewerAddTag( "p", event->richDescription() ) + ""; tmpStr += ""; } if ( event->categories().count() > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += i18np( "1 category", "%1 categories", event->categories().count() ) + ""; tmpStr += "" + event->categoriesStr() + ""; tmpStr += ""; } if ( event->recurs() ) { KDateTime dt = event->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() ); tmpStr += ""; tmpStr += "" + i18n( "Next Occurrence" )+ ""; tmpStr += "" + ( dt.isValid() ? KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) : i18nc( "no date", "none" ) ) + ""; tmpStr += ""; } tmpStr += ""; tmpStr += eventViewerFormatAttendees( event ); tmpStr += ""; int attachmentCount = event->attachments().count(); if ( attachmentCount > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += i18np( "1 attachment", "%1 attachments", attachmentCount )+ ""; tmpStr += "" + eventViewerFormatAttachments( event ) + ""; tmpStr += ""; } KDateTime kdt = event->created().toTimeSpec( spec ); tmpStr += ""; tmpStr += "

    " + i18n( "Creation date: %1", KGlobal::locale()->formatDateTime( kdt.dateTime(), KLocale::ShortDate ) ) + ""; return tmpStr; } static QString eventViewerFormatTodo( Todo *todo, KDateTime::Spec spec ) { if ( !todo ) { return QString(); } QString tmpStr = eventViewerFormatHeader( todo ); if ( !todo->location().isEmpty() ) { tmpStr += eventViewerAddTag( "b", i18n(" Location: %1", todo->richLocation() ) ); tmpStr += "
    "; } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { tmpStr += i18n( "Due on: %1", IncidenceFormatter::dateTimeToString( todo->dtDue(), todo->allDay(), true, spec ) ); } if ( !todo->description().isEmpty() ) { tmpStr += eventViewerAddTag( "p", todo->richDescription() ); } tmpStr += eventViewerFormatCategories( todo ); if ( todo->priority() > 0 ) { tmpStr += i18n( "

    Priority: %1

    ", todo->priority() ); } else { tmpStr += i18n( "

    Priority: %1

    ", i18n( "Unspecified" ) ); } tmpStr += i18n( "

    %1 % completed

    ", todo->percentComplete() ); if ( todo->recurs() ) { KDateTime dt = todo->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() ); tmpStr += eventViewerAddTag( "p", "" + i18n( "This is a recurring to-do. The next occurrence will be on %1.", KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) ) + "" ); } tmpStr += eventViewerFormatAttendees( todo ); tmpStr += eventViewerFormatAttachments( todo ); KDateTime kdt = todo->created().toTimeSpec( spec ); tmpStr += "

    " + i18n( "Creation date: %1", KGlobal::locale()->formatDateTime( kdt.dateTime(), KLocale::ShortDate ) ) + ""; return tmpStr; } static QString eventViewerFormatJournal( Journal *journal, KDateTime::Spec spec ) { if ( !journal ) { return QString(); } QString tmpStr; if ( !journal->summary().isEmpty() ) { tmpStr+= eventViewerAddTag( "h2", journal->richSummary() ); } tmpStr += eventViewerAddTag( "h3", i18n( "Journal for %1", IncidenceFormatter::dateToString( journal->dtStart(), false, spec ) ) ); if ( !journal->description().isEmpty() ) { tmpStr += eventViewerAddTag( "p", journal->richDescription() ); } return tmpStr; } static QString eventViewerFormatFreeBusy( FreeBusy *fb, KDateTime::Spec spec ) { Q_UNUSED( spec ); if ( !fb ) { return QString(); } QString tmpStr( eventViewerAddTag( "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) ); tmpStr += eventViewerAddTag( "h4", i18n( "Busy times in date range %1 - %2:", KGlobal::locale()->formatDate( fb->dtStart().date(), KLocale::ShortDate ), KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) ) ); QList periods = fb->busyPeriods(); QString text = eventViewerAddTag( "em", eventViewerAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) ); QList::iterator it; for ( it = periods.begin(); it != periods.end(); ++it ) { Period per = *it; if ( per.hasDuration() ) { int dur = per.duration().asSeconds(); QString cont; if ( dur >= 3600 ) { cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 ); dur %= 3600; } if ( dur >= 60 ) { cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 ); dur %= 60; } if ( dur > 0 ) { cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur ); } text += i18nc( "startDate for duration", "%1 for %2", KGlobal::locale()->formatDateTime( per.start().dateTime(), KLocale::LongDate ), cont ); text += "
    "; } else { if ( per.start().date() == per.end().date() ) { text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3", KGlobal::locale()->formatDate( per.start().date() ), KGlobal::locale()->formatTime( per.start().time() ), KGlobal::locale()->formatTime( per.end().time() ) ); } else { text += i18nc( "fromDateTime - toDateTime", "%1 - %2", KGlobal::locale()->formatDateTime( per.start().dateTime(), KLocale::LongDate ), KGlobal::locale()->formatDateTime( per.end().dateTime(), KLocale::LongDate ) ); } text += "
    "; } } tmpStr += eventViewerAddTag( "p", text ); return tmpStr; } //@endcond //@cond PRIVATE class KCal::IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor { public: EventViewerVisitor() : mSpec( KDateTime::Spec() ), mResult( "" ) {} bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() ) { mSpec = spec; mResult = ""; return incidence->accept( *this ); } QString result() const { return mResult; } protected: bool visit( Event *event ) { mResult = eventViewerFormatEvent( event, mSpec ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = eventViewerFormatTodo( todo, mSpec ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = eventViewerFormatJournal( journal, mSpec ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = eventViewerFormatFreeBusy( fb, mSpec ); return !mResult.isEmpty(); } protected: KDateTime::Spec mSpec; QString mResult; }; //@endcond QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence ) { return extensiveDisplayStr( incidence, KDateTime::Spec() ); } QString IncidenceFormatter::extensiveDisplayStr( IncidenceBase *incidence, KDateTime::Spec spec ) { if ( !incidence ) { return QString(); } EventViewerVisitor v; if ( v.act( incidence, spec ) ) { return v.result(); } else { return QString(); } } /******************************************************************* * Helper functions for the body part formatter of kmail *******************************************************************/ //@cond PRIVATE static QString string2HTML( const QString &str ) { return Qt::escape( str ); } static QString cleanHtml( const QString &html ) { QRegExp rx( "]*>(.*)", Qt::CaseInsensitive ); rx.indexIn( html ); QString body = rx.cap( 1 ); return Qt::escape( body.remove( QRegExp( "<[^>]*>" ) ).trimmed() ); } static QString eventStartTimeStr( Event *event ) { QString tmp; if ( !event->allDay() ) { tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2", IncidenceFormatter::dateToString( event->dtStart(), true, KSystemTimeZones::local() ), IncidenceFormatter::timeToString( event->dtStart(), true, KSystemTimeZones::local() ) ); } else { tmp = i18nc( "%1: Start Date", "%1 (all day)", IncidenceFormatter::dateToString( event->dtStart(), true, KSystemTimeZones::local() ) ); } return tmp; } static QString eventEndTimeStr( Event *event ) { QString tmp; if ( event->hasEndDate() && event->dtEnd().isValid() ) { if ( !event->allDay() ) { tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2", IncidenceFormatter::dateToString( event->dtEnd(), true, KSystemTimeZones::local() ), IncidenceFormatter::timeToString( event->dtEnd(), true, KSystemTimeZones::local() ) ); } else { tmp = i18nc( "%1: End Date", "%1 (all day)", IncidenceFormatter::dateToString( event->dtEnd(), true, KSystemTimeZones::local() ) ); } } return tmp; } static QString invitationRow( const QString &cell1, const QString &cell2 ) { return "" + cell1 + "" + cell2 + "\n"; } static bool iamOrganizer( Incidence *incidence ) { // Check if I'm the organizer for this incidence if ( !incidence ) { return false; } bool iam = false; KEMailSettings settings; QStringList profiles = settings.profiles(); for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) { settings.setProfile( *it ); if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) { iam = true; break; } } return iam; } static bool iamAttendee( Attendee *attendee ) { // Check if I'm this attendee bool iam = false; KEMailSettings settings; QStringList profiles = settings.profiles(); for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) { settings.setProfile( *it ); if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) { iam = true; break; } } return iam; } static Attendee *findMyAttendee( Incidence *incidence ) { // Return the attendee for the incidence that is probably me Attendee *attendee = 0; if ( !incidence ) { return attendee; } KEMailSettings settings; QStringList profiles = settings.profiles(); for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) { settings.setProfile( *it ); Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it2; for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) { Attendee *a = *it2; if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) { attendee = a; break; } } } return attendee; } static Attendee *findAttendee( Incidence *incidence, const QString &email ) { // Search for an attendee by email address Attendee *attendee = 0; if ( !incidence ) { return attendee; } Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it; for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { Attendee *a = *it; if ( email == a->email() ) { attendee = a; break; } } return attendee; } static bool rsvpRequested( Incidence *incidence ) { //use a heuristic to determine if a response is requested. bool rsvp = true; // better send superfluously than not at all Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it; for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { if ( it == attendees.constBegin() ) { rsvp = (*it)->RSVP(); // use what the first one has } else { if ( (*it)->RSVP() != rsvp ) { rsvp = true; // they differ, default break; } } } return rsvp; } static QString rsvpRequestedStr( bool rsvpRequested ) { if ( rsvpRequested ) { return i18n( "Your response is requested" ); } else { return i18n( "A response is not necessary" ); } } static QString invitationPerson( const QString &email, QString name, QString uid ) { // Make the search, if there is an email address to search on, // and either name or uid is missing if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); KABC::Addressee::List addressList = add_book->findByEmail( email ); if ( !addressList.isEmpty() ) { KABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { if ( name.isEmpty() ) { // No name set, so use the one from the addressbook name = o.formattedName(); } uid = o.uid(); } else { // Email not found in the addressbook. Don't make a link uid.clear(); } } } // Show the attendee QString tmpString; if ( !uid.isEmpty() ) { // There is a UID, so make a link to the addressbook if ( name.isEmpty() ) { // Use the email address for text tmpString += eventViewerAddLink( "uid:" + uid, email ); } else { tmpString += eventViewerAddLink( "uid:" + uid, name ); } } else { // No UID, just show some text tmpString += ( name.isEmpty() ? email : name ); } tmpString += '\n'; // Make the mailto link if ( !email.isEmpty() ) { KCal::Person person( name, email ); KUrl mailto; mailto.setProtocol( "mailto" ); mailto.setPath( person.fullName() ); const QString iconPath = KIconLoader::global()->iconPath( "mail-message-new", KIconLoader::Small ); tmpString += eventViewerAddLink( mailto.url(), "" ); } tmpString += '\n'; return tmpString; } static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode ) { // if description and comment -> use both // if description, but no comment -> use the desc as the comment (and no desc) // if comment, but no description -> use the comment and no description QString html; QString descr; QStringList comments; if ( incidence->comments().isEmpty() ) { if ( !incidence->description().isEmpty() ) { // use description as comments if ( !incidence->descriptionIsRich() ) { comments << string2HTML( incidence->description() ); } else { comments << incidence->richDescription(); if ( noHtmlMode ) { comments[0] = cleanHtml( comments[0] ); } comments[0] = eventViewerAddTag( "p", comments[0] ); } } //else desc and comments are empty } else { // non-empty comments foreach ( const QString &c, incidence->comments() ) { if ( !c.isEmpty() ) { comments += string2HTML( c ); } } if ( !incidence->description().isEmpty() ) { // use description too if ( !incidence->descriptionIsRich() ) { descr = string2HTML( incidence->description() ); } else { descr = incidence->richDescription(); if ( noHtmlMode ) { descr = cleanHtml( descr ); } descr = eventViewerAddTag( "p", descr ); } } } if( !descr.isEmpty() ) { html += "

    "; html += ""; html += ""; html += ""; html += "
    " + eventViewerAddTag( "u", i18n( "Description:" ) ) + "
    " + descr + "
    "; } if ( !comments.isEmpty() ) { html += "

    "; html += ""; html += ""; html += ""; html += "
    " + eventViewerAddTag( "u", i18n( "Comments:" ) ) + "
    "; if ( comments.count() > 1 ) { html += "
      "; for ( int i=0; i < comments.count(); ++i ) { html += "
    • " + comments[i] + "
    • "; } html += "
    "; } else { html += comments[0]; } html += "
    "; } return html; } static QString invitationDetailsEvent( Event *event, bool noHtmlMode, KDateTime::Spec spec ) { // Invitation details are formatted into an HTML table if ( !event ) { return QString(); } QString sSummary = i18n( "Summary unspecified" ); if ( !event->summary().isEmpty() ) { if ( !event->summaryIsRich() ) { sSummary = string2HTML( event->summary() ); } else { sSummary = event->richSummary(); if ( noHtmlMode ) { sSummary = cleanHtml( sSummary ); } } } QString sLocation = i18n( "Location unspecified" ); if ( !event->location().isEmpty() ) { if ( !event->locationIsRich() ) { sLocation = string2HTML( event->location() ); } else { sLocation = event->richLocation(); if ( noHtmlMode ) { sLocation = cleanHtml( sLocation ); } } } QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" ); QString html = QString( "

    \n" ).arg( dir ); html += ""; // Invitation summary & location rows html += invitationRow( i18n( "What:" ), sSummary ); html += invitationRow( i18n( "Where:" ), sLocation ); // If a 1 day event if ( event->dtStart().date() == event->dtEnd().date() ) { html += invitationRow( i18n( "Date:" ), IncidenceFormatter::dateToString( event->dtStart(), false, spec ) ); if ( !event->allDay() ) { html += invitationRow( i18n( "Time:" ), IncidenceFormatter::timeToString( event->dtStart(), false, spec ) + " - " + IncidenceFormatter::timeToString( event->dtEnd(), false, spec ) ); } } else { html += invitationRow( i18nc( "starting date", "From:" ), IncidenceFormatter::dateToString( event->dtStart(), false, spec ) ); if ( !event->allDay() ) { html += invitationRow( i18nc( "starting time", "At:" ), IncidenceFormatter::timeToString( event->dtStart(), false, spec ) ); } if ( event->hasEndDate() ) { html += invitationRow( i18nc( "ending date", "To:" ), IncidenceFormatter::dateToString( event->dtEnd(), false, spec ) ); if ( !event->allDay() ) { html += invitationRow( i18nc( "ending time", "At:" ), IncidenceFormatter::timeToString( event->dtEnd(), false, spec ) ); } } else { html += invitationRow( i18nc( "ending date", "To:" ), i18n( "no end date specified" ) ); } } // Invitation Duration Row if ( !event->allDay() && event->hasEndDate() && event->dtEnd().isValid() ) { QString tmp; QTime sDuration( 0, 0, 0 ), t; int secs = event->dtStart().secsTo( event->dtEnd() ); t = sDuration.addSecs( secs ); if ( t.hour() > 0 ) { tmp += i18np( "1 hour ", "%1 hours ", t.hour() ); } if ( t.minute() > 0 ) { tmp += i18np( "1 minute ", "%1 minutes ", t.minute() ); } html += invitationRow( i18n( "Duration:" ), tmp ); } if ( event->recurs() ) { html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) ); } html += "
    \n"; html += invitationsDetailsIncidence( event, noHtmlMode ); return html; } static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode, KDateTime::Spec spec ) { // To-do details are formatted into an HTML table if ( !todo ) { return QString(); } QString sSummary = i18n( "Summary unspecified" ); - QString sDescr = i18n( "Description unspecified" ); - if ( ! todo->summary().isEmpty() ) { - sSummary = todo->richSummary(); - if ( noHtmlMode ) { - sSummary = cleanHtml( sSummary ); + if ( !todo->summary().isEmpty() ) { + if ( !todo->summaryIsRich() ) { + sSummary = string2HTML( todo->summary() ); + } else { + sSummary = todo->richSummary(); + if ( noHtmlMode ) { + sSummary = cleanHtml( sSummary ); + } } } - if ( ! todo->description().isEmpty() ) { - sDescr = todo->description(); - if ( noHtmlMode ) { - sDescr = cleanHtml( sDescr ); + + QString sLocation = i18n( "Location unspecified" ); + if ( !todo->location().isEmpty() ) { + if ( !todo->locationIsRich() ) { + sLocation = string2HTML( todo->location() ); + } else { + sLocation = todo->richLocation(); + if ( noHtmlMode ) { + sLocation = cleanHtml( sLocation ); + } } } - QString html( "\n" ); - html += invitationRow( i18n( "Summary:" ), sSummary ); + + QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" ); + QString html = QString( "
    \n" ).arg( dir ); + html += "
    "; + + // Invitation summary & location rows + html += invitationRow( i18n( "What:" ), sSummary ); + html += invitationRow( i18n( "Where:" ), sLocation ); + if ( todo->hasStartDate() && todo->dtStart().isValid() ) { html += invitationRow( i18n( "Start Date:" ), IncidenceFormatter::dateToString( todo->dtStart(), false, spec ) ); - } else { - html += invitationRow( i18n( "Start Date:" ), - i18nc( "no to-do start date", "None" ) ); } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { html += invitationRow( i18n( "Due Date:" ), IncidenceFormatter::dateToString( todo->dtDue(), false, spec ) ); } else { html += invitationRow( i18n( "Due Date:" ), i18nc( "no to-do due date", "None" ) ); } - html += invitationRow( i18n( "Description:" ), sDescr ); - html += "
    \n"; + + html += "\n"; html += invitationsDetailsIncidence( todo, noHtmlMode ); return html; } static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode, KDateTime::Spec spec ) { if ( !journal ) { return QString(); } QString sSummary = i18n( "Summary unspecified" ); QString sDescr = i18n( "Description unspecified" ); if ( ! journal->summary().isEmpty() ) { sSummary = journal->richSummary(); if ( noHtmlMode ) { sSummary = cleanHtml( sSummary ); } } if ( ! journal->description().isEmpty() ) { sDescr = journal->richDescription(); if ( noHtmlMode ) { sDescr = cleanHtml( sDescr ); } } QString html( "\n" ); html += invitationRow( i18n( "Summary:" ), sSummary ); html += invitationRow( i18n( "Date:" ), IncidenceFormatter::dateToString( journal->dtStart(), false, spec ) ); html += invitationRow( i18n( "Description:" ), sDescr ); html += "
    \n"; html += invitationsDetailsIncidence( journal, noHtmlMode ); return html; } static QString invitationDetailsFreeBusy( FreeBusy *fb, bool noHtmlMode, KDateTime::Spec spec ) { Q_UNUSED( noHtmlMode ); if ( !fb ) { return QString(); } QString html( "\n" ); html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() ); html += invitationRow( i18n( "Start date:" ), IncidenceFormatter::dateToString( fb->dtStart(), true, spec ) ); html += invitationRow( i18n( "End date:" ), IncidenceFormatter::dateToString( fb->dtEnd(), true, spec ) ); html += "\n"; html += "\n"; QList periods = fb->busyPeriods(); QList::iterator it; for ( it = periods.begin(); it != periods.end(); ++it ) { Period per = *it; if ( per.hasDuration() ) { int dur = per.duration().asSeconds(); QString cont; if ( dur >= 3600 ) { cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 ); dur %= 3600; } if ( dur >= 60 ) { cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 ); dur %= 60; } if ( dur > 0 ) { cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur ); } html += invitationRow( QString(), i18nc( "startDate for duration", "%1 for %2", KGlobal::locale()->formatDateTime( per.start().dateTime(), KLocale::LongDate ), cont ) ); } else { QString cont; if ( per.start().date() == per.end().date() ) { cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3", KGlobal::locale()->formatDate( per.start().date() ), KGlobal::locale()->formatTime( per.start().time() ), KGlobal::locale()->formatTime( per.end().time() ) ); } else { cont = i18nc( "fromDateTime - toDateTime", "%1 - %2", KGlobal::locale()->formatDateTime( per.start().dateTime(), KLocale::LongDate ), KGlobal::locale()->formatDateTime( per.end().dateTime(), KLocale::LongDate ) ); } html += invitationRow( QString(), cont ); } } html += "

    Busy periods given in this free/busy object:
    \n"; return html; } static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg ) { if ( !msg || !event ) { return QString(); } switch ( msg->method() ) { case iTIPPublish: return i18n( "This event has been published" ); case iTIPRequest: if ( event->revision() > 0 ) { return i18n( "This invitation has been updated" ); } if ( iamOrganizer( event ) ) { return i18n( "I sent this invitation" ); } else { if ( !event->organizer().fullName().isEmpty() ) { return i18n( "You received an invitation from %1", event->organizer().fullName() ); } else { return i18n( "You received an invitation" ); } } case iTIPRefresh: return i18n( "This invitation was refreshed" ); case iTIPCancel: return i18n( "This invitation has been canceled" ); case iTIPAdd: return i18n( "Addition to the invitation" ); case iTIPReply: { Attendee::List attendees = event->attendees(); if( attendees.count() == 0 ) { kDebug() << "No attendees in the iCal reply!"; return QString(); } if ( attendees.count() != 1 ) { kDebug() << "Warning: attendeecount in the reply should be 1" << "but is" << attendees.count(); } Attendee *attendee = *attendees.begin(); QString attendeeName = attendee->name(); if ( attendeeName.isEmpty() ) { attendeeName = attendee->email(); } if ( attendeeName.isEmpty() ) { attendeeName = i18n( "Sender" ); } QString delegatorName, dummy; KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName ); if ( delegatorName.isEmpty() ) { delegatorName = attendee->delegator(); } switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "%1 indicates this invitation still needs some action", attendeeName ); case Attendee::Accepted: if ( delegatorName.isEmpty() ) { return i18n( "%1 accepts this invitation", attendeeName ); } else { return i18n( "%1 accepts this invitation on behalf of %2", attendeeName, delegatorName ); } case Attendee::Tentative: if ( delegatorName.isEmpty() ) { return i18n( "%1 tentatively accepts this invitation", attendeeName ); } else { return i18n( "%1 tentatively accepts this invitation on behalf of %2", attendeeName, delegatorName ); } case Attendee::Declined: if ( delegatorName.isEmpty() ) { return i18n( "%1 declines this invitation", attendeeName ); } else { return i18n( "%1 declines this invitation on behalf of %2", attendeeName, delegatorName ); } case Attendee::Delegated: { QString delegate, dummy; KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate ); if ( delegate.isEmpty() ) { delegate = attendee->delegate(); } if ( !delegate.isEmpty() ) { return i18n( "%1 has delegated this invitation to %2", attendeeName, delegate ); } else { return i18n( "%1 has delegated this invitation", attendeeName ); } } case Attendee::Completed: return i18n( "This invitation is now completed" ); case Attendee::InProcess: return i18n( "%1 is still processing the invitation", attendeeName ); default: return i18n( "Unknown response to this invitation" ); } break; } case iTIPCounter: return i18n( "Sender makes this counter proposal" ); case iTIPDeclineCounter: return i18n( "Sender declines the counter proposal" ); case iTIPNoMethod: return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() ); } return QString(); } static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg ) { if ( !msg || !todo ) { return QString(); } switch ( msg->method() ) { case iTIPPublish: return i18n( "This to-do has been published" ); case iTIPRequest: if ( todo->revision() > 0 ) { return i18n( "This to-do has been updated" ); } else { return i18n( "You have been assigned this to-do" ); } case iTIPRefresh: return i18n( "This to-do was refreshed" ); case iTIPCancel: return i18n( "This to-do was canceled" ); case iTIPAdd: return i18n( "Addition to the to-do" ); case iTIPReply: { Attendee::List attendees = todo->attendees(); if ( attendees.count() == 0 ) { kDebug() << "No attendees in the iCal reply!"; return QString(); } if ( attendees.count() != 1 ) { kDebug() << "Warning: attendeecount in the reply should be 1" << "but is" << attendees.count(); } Attendee *attendee = *attendees.begin(); switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "Sender indicates this to-do assignment still needs some action" ); case Attendee::Accepted: return i18n( "Sender accepts this to-do" ); case Attendee::Tentative: return i18n( "Sender tentatively accepts this to-do" ); case Attendee::Declined: return i18n( "Sender declines this to-do" ); case Attendee::Delegated: { QString delegate, dummy; KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate ); if ( delegate.isEmpty() ) { delegate = attendee->delegate(); } if ( !delegate.isEmpty() ) { return i18n( "Sender has delegated this request for the to-do to %1", delegate ); } else { return i18n( "Sender has delegated this request for the to-do " ); } } case Attendee::Completed: return i18n( "The request for this to-do is now completed" ); case Attendee::InProcess: return i18n( "Sender is still processing the invitation" ); default: return i18n( "Unknown response to this to-do" ); } break; } case iTIPCounter: return i18n( "Sender makes this counter proposal" ); case iTIPDeclineCounter: return i18n( "Sender declines the counter proposal" ); case iTIPNoMethod: return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() ); } return QString(); } static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg ) { // TODO: Several of the methods are not allowed for journals, so remove them. if ( !msg || !journal ) { return QString(); } switch ( msg->method() ) { case iTIPPublish: return i18n( "This journal has been published" ); case iTIPRequest: return i18n( "You have been assigned this journal" ); case iTIPRefresh: return i18n( "This journal was refreshed" ); case iTIPCancel: return i18n( "This journal was canceled" ); case iTIPAdd: return i18n( "Addition to the journal" ); case iTIPReply: { Attendee::List attendees = journal->attendees(); if ( attendees.count() == 0 ) { kDebug() << "No attendees in the iCal reply!"; return QString(); } if( attendees.count() != 1 ) { kDebug() << "Warning: attendeecount in the reply should be 1 " << "but is " << attendees.count(); } Attendee *attendee = *attendees.begin(); switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "Sender indicates this journal assignment still needs some action" ); case Attendee::Accepted: return i18n( "Sender accepts this journal" ); case Attendee::Tentative: return i18n( "Sender tentatively accepts this journal" ); case Attendee::Declined: return i18n( "Sender declines this journal" ); case Attendee::Delegated: return i18n( "Sender has delegated this request for the journal" ); case Attendee::Completed: return i18n( "The request for this journal is now completed" ); case Attendee::InProcess: return i18n( "Sender is still processing the invitation" ); default: return i18n( "Unknown response to this journal" ); } break; } case iTIPCounter: return i18n( "Sender makes this counter proposal" ); case iTIPDeclineCounter: return i18n( "Sender declines the counter proposal" ); case iTIPNoMethod: return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() ); } return QString(); } static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg ) { if ( !msg || !fb ) { return QString(); } switch ( msg->method() ) { case iTIPPublish: return i18n( "This free/busy list has been published" ); case iTIPRequest: return i18n( "The free/busy list has been requested" ); case iTIPRefresh: return i18n( "This free/busy list was refreshed" ); case iTIPCancel: return i18n( "This free/busy list was canceled" ); case iTIPAdd: return i18n( "Addition to the free/busy list" ); case iTIPNoMethod: default: return i18n( "Error: Free/Busy iMIP message with unknown method: '%1'", msg->method() ); } } //@endcond static QString invitationAttendees( Incidence *incidence ) { QString tmpStr; if ( !incidence ) { return tmpStr; } tmpStr += i18n( "Invitation List" ); int count=0; Attendee::List attendees = incidence->attendees(); if ( !attendees.isEmpty() ) { Attendee::List::ConstIterator it; for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { Attendee *a = *it; if ( !iamAttendee( a ) ) { count++; if ( count == 1 ) { tmpStr += ""; } tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } } } if ( count ) { tmpStr += "
    "; tmpStr += invitationPerson( a->email(), a->name(), QString() ); if ( !a->delegator().isEmpty() ) { tmpStr += i18n( " (delegated by %1)", a->delegator() ); } if ( !a->delegate().isEmpty() ) { tmpStr += i18n( " (delegated to %1)", a->delegate() ); } tmpStr += "" + a->statusStr() + "
    "; } else { tmpStr += "" + i18nc( "no attendees", "None" ) + ""; } return tmpStr; } //@cond PRIVATE class KCal::IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor { public: ScheduleMessageVisitor() : mMessage(0) { mResult = ""; } bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); } QString result() const { return mResult; } protected: QString mResult; ScheduleMessage *mMessage; }; class KCal::IncidenceFormatter::InvitationHeaderVisitor : public IncidenceFormatter::ScheduleMessageVisitor { protected: bool visit( Event *event ) { mResult = invitationHeaderEvent( event, mMessage ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = invitationHeaderTodo( todo, mMessage ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = invitationHeaderJournal( journal, mMessage ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = invitationHeaderFreeBusy( fb, mMessage ); return !mResult.isEmpty(); } }; class KCal::IncidenceFormatter::InvitationBodyVisitor : public IncidenceFormatter::ScheduleMessageVisitor { public: InvitationBodyVisitor( bool noHtmlMode, KDateTime::Spec spec ) : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {} protected: bool visit( Event *event ) { mResult = invitationDetailsEvent( event, mNoHtmlMode, mSpec ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = invitationDetailsTodo( todo, mNoHtmlMode, mSpec ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = invitationDetailsJournal( journal, mNoHtmlMode, mSpec ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode, mSpec ); return !mResult.isEmpty(); } private: bool mNoHtmlMode; KDateTime::Spec mSpec; }; //@endcond QString InvitationFormatterHelper::generateLinkURL( const QString &id ) { return id; } //@cond PRIVATE class IncidenceFormatter::IncidenceCompareVisitor : public IncidenceBase::Visitor { public: IncidenceCompareVisitor() : mExistingIncidence( 0 ) {} bool act( IncidenceBase *incidence, Incidence *existingIncidence ) { if ( !existingIncidence ) { return false; } Incidence *inc = dynamic_cast( incidence ); if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) { return false; } mExistingIncidence = existingIncidence; return incidence->accept( *this ); } QString result() const { if ( mChanges.isEmpty() ) { return QString(); } QString html = "
    • "; html += mChanges.join( "
    • " ); html += "
      "; return html; } protected: bool visit( Event *event ) { compareEvents( event, dynamic_cast( mExistingIncidence ) ); compareIncidences( event, mExistingIncidence ); return !mChanges.isEmpty(); } bool visit( Todo *todo ) { compareIncidences( todo, mExistingIncidence ); return !mChanges.isEmpty(); } bool visit( Journal *journal ) { compareIncidences( journal, mExistingIncidence ); return !mChanges.isEmpty(); } bool visit( FreeBusy *fb ) { Q_UNUSED( fb ); return !mChanges.isEmpty(); } private: void compareEvents( Event *newEvent, Event *oldEvent ) { if ( !oldEvent || !newEvent ) { return; } if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->allDay() != newEvent->allDay() ) { mChanges += i18n( "The invitation starting time has been changed from %1 to %2", eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) ); } if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->allDay() != newEvent->allDay() ) { mChanges += i18n( "The invitation ending time has been changed from %1 to %2", eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) ); } } void compareIncidences( Incidence *newInc, Incidence *oldInc ) { if ( !oldInc || !newInc ) { return; } if ( oldInc->summary() != newInc->summary() ) { mChanges += i18n( "The summary has been changed to: \"%1\"", newInc->richSummary() ); } if ( oldInc->location() != newInc->location() ) { mChanges += i18n( "The location has been changed to: \"%1\"", newInc->richLocation() ); } if ( oldInc->description() != newInc->description() ) { mChanges += i18n( "The description has been changed to: \"%1\"", newInc->richDescription() ); } Attendee::List oldAttendees = oldInc->attendees(); Attendee::List newAttendees = newInc->attendees(); for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) { Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() ); if ( !oldAtt ) { mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() ); } else { if ( oldAtt->status() != (*it)->status() ) { mChanges += i18n( "The status of attendee %1 has been changed to: %2", (*it)->fullName(), (*it)->statusStr() ); } } } for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) { Attendee *newAtt = newInc->attendeeByMail( (*it)->email() ); if ( !newAtt ) { mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() ); } } } private: Incidence *mExistingIncidence; QStringList mChanges; }; //@endcond QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text ) { QString res( "%2" ); return res.arg( generateLinkURL( id ) ).arg( text ); return res; } Calendar *InvitationFormatterHelper::calendar() const { return 0; } static QString formatICalInvitationHelper( QString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper, bool noHtmlMode, KDateTime::Spec spec ) { if ( invitation.isEmpty() ) { return QString(); } ICalFormat format; // parseScheduleMessage takes the tz from the calendar, // no need to set it manually here for the format! ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation ); if( !msg ) { kDebug() << "Failed to parse the scheduling message"; Q_ASSERT( format.exception() ); kDebug() << format.exception()->message(); return QString(); } IncidenceBase *incBase = msg->event(); incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() ); // Determine if this incidence is in my calendar Incidence *existingIncidence = 0; if ( incBase && helper->calendar() ) { existingIncidence = helper->calendar()->incidence( incBase->uid() ); if ( !existingIncidence ) { const Incidence::List list = helper->calendar()->incidences(); for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) { if ( (*it)->schedulingID() == incBase->uid() ) { existingIncidence = *it; break; } } } } // First make the text of the message QString html; html += "
      "; IncidenceFormatter::InvitationHeaderVisitor headerVisitor; // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled if ( !headerVisitor.act( incBase, msg ) ) { return QString(); } html += eventViewerAddTag( "h3", headerVisitor.result() ); IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec ); if ( !bodyVisitor.act( incBase, msg ) ) { return QString(); } html += bodyVisitor.result(); if ( msg->method() == iTIPRequest ) { // ### Scheduler::Publish/Refresh/Add as well? IncidenceFormatter::IncidenceCompareVisitor compareVisitor; if ( compareVisitor.act( incBase, existingIncidence ) ) { html += i18n( "

      The following changes have been made by the organizer:

      " ); html += compareVisitor.result(); } } Incidence *inc = dynamic_cast( incBase ); // determine if I am the organizer for this invitation bool myInc = iamOrganizer( inc ); // determine if the invitation response has already been recorded bool rsvpRec = false; Attendee *ea = 0; if ( !myInc ) { if ( existingIncidence ) { ea = findMyAttendee( existingIncidence ); } if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined ) ) { rsvpRec = true; } } // Print if RSVP needed, not-needed, or response already recorded bool rsvpReq = rsvpRequested( inc ); if ( !myInc ) { html += "
      "; html += ""; if ( rsvpRec && ( inc && inc->revision() == 0 ) ) { html += i18n( "Your response has already been recorded [%1]", ea->statusStr() ); rsvpReq = false; } else { html += rsvpRequestedStr( rsvpReq ); } html += "
      "; } // Add groupware links html += "

      "; html += ""; const QString tdOpen = ""; switch ( msg->method() ) { case iTIPPublish: case iTIPRequest: case iTIPRefresh: case iTIPAdd: { if ( inc && inc->revision() > 0 && existingIncidence ) { if ( inc->type() == "Todo" ) { html += helper->makeLink( "reply", i18n( "[Record invitation into my to-do list]" ) ); } else { html += helper->makeLink( "reply", i18n( "[Record invitation into my calendar]" ) ); } } if ( !myInc ) { if ( rsvpReq ) { // Accept html += tdOpen; html += helper->makeLink( "accept", i18nc( "accept invitation", "Accept" ) ); html += tdClose; // Accept conditionally html += tdOpen; html += helper->makeLink( "accept_conditionally", i18nc( "Accept invitation conditionally", "Accept cond." ) ); html += tdClose; } if ( rsvpReq ) { // Counter proposal html += tdOpen; html += helper->makeLink( "counter", i18nc( "invitation counter proposal", "Counter proposal" ) ); html += tdClose; } if ( rsvpReq ) { // Decline html += tdOpen; html += helper->makeLink( "decline", i18nc( "decline invitation", "Decline" ) ); html += tdClose; } if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) { // Delegate html += tdOpen; html += helper->makeLink( "delegate", i18nc( "delegate inviation to another", "Delegate" ) ); html += tdClose; // Forward html += tdOpen; html += helper->makeLink( "forward", i18nc( "forward request to another", "Forward" ) ); html += tdClose; // Check calendar if ( incBase->type() == "Event" ) { html += tdOpen; html += helper->makeLink( "check_calendar", i18nc( "look for scheduling conflicts", "Check my calendar" ) ); html += tdClose; } } } break; } case iTIPCancel: // Remove invitation if ( existingIncidence ) { html += tdOpen; if ( inc->type() == "Todo" ) { html += helper->makeLink( "cancel", i18n( "Remove invitation from my task list" ) ); } else { html += helper->makeLink( "cancel", i18n( "Remove invitation from my calendar" ) ); } html += tdClose; } break; case iTIPReply: { // Record invitation response Attendee *a = 0; Attendee *ea = 0; if ( inc ) { a = inc->attendees().first(); if ( a && helper->calendar() ) { ea = findAttendee( existingIncidence, a->email() ); } } if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) { html += tdOpen; html += eventViewerAddTag( "i", i18n( "The response has already been recorded" ) ); html += tdClose; } else { if ( inc->type() == "Todo" ) { html += helper->makeLink( "reply", i18n( "[Record response into my to-do list]" ) ); } else { html += helper->makeLink( "reply", i18n( "[Record response into my calendar]" ) ); } } break; } case iTIPCounter: // Counter proposal html += tdOpen; html += helper->makeLink( "accept_counter", i18n( "Accept" ) ); html += tdClose; html += tdOpen; html += helper->makeLink( "decline_counter", i18n( "Decline" ) ); html += tdClose; html += tdOpen; html += helper->makeLink( "check_calendar", i18n( "Check my calendar" ) ); html += tdClose; break; case iTIPDeclineCounter: case iTIPNoMethod: break; } // close the groupware table html += "
      "; const QString tdClose = "
      "; // Add the attendee list if I am the organizer if ( myInc && helper->calendar() ) { html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) ); } // close the top-level html += "

      "; return html; } //@endcond QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper ) { return formatICalInvitationHelper( invitation, mCalendar, helper, false, KSystemTimeZones::local() ); } QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper ) { return formatICalInvitationHelper( invitation, mCalendar, helper, true, KSystemTimeZones::local() ); } /******************************************************************* * Helper functions for the Incidence tooltips *******************************************************************/ //@cond PRIVATE class KCal::IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor { public: ToolTipVisitor() : mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {} bool act( IncidenceBase *incidence, bool richText=true, KDateTime::Spec spec=KDateTime::Spec() ) { mRichText = richText; mSpec = spec; mResult = ""; return incidence ? incidence->accept( *this ) : false; } QString result() const { return mResult; } protected: bool visit( Event *event ); bool visit( Todo *todo ); bool visit( Journal *journal ); bool visit( FreeBusy *fb ); QString dateRangeText( Event *event ); QString dateRangeText( Todo *todo ); QString dateRangeText( Journal *journal ); QString dateRangeText( FreeBusy *fb ); QString generateToolTip( Incidence *incidence, QString dtRangeText ); protected: bool mRichText; KDateTime::Spec mSpec; QString mResult; }; QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event ) { //FIXME: support mRichText==false QString ret; QString tmp; if ( event->isMultiDay() ) { tmp = IncidenceFormatter::dateToString( event->dtStart(), true, mSpec ); ret += "
      " + i18nc( "Event start", "From: %1", tmp ); tmp = IncidenceFormatter::dateToString( event->dtEnd(), true, mSpec ); ret += "
      " + i18nc( "Event end","To: %1", tmp ); } else { ret += "
      " + i18n( "Date: %1", IncidenceFormatter::dateToString( event->dtStart(), true, mSpec ) ); if ( !event->allDay() ) { const QString dtStartTime = IncidenceFormatter::timeToString( event->dtStart(), true, mSpec ); const QString dtEndTime = IncidenceFormatter::timeToString( event->dtEnd(), true, mSpec ); if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00' tmp = "
      " + i18nc( "time for event", "Time: %1", dtStartTime ); } else { tmp = "
      " + i18nc( "time range for event", "Time: %1 - %2", dtStartTime, dtEndTime ); } ret += tmp; } } return ret.replace( ' ', " " ); } QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo ) { //FIXME: support mRichText==false QString ret; if ( todo->hasStartDate() && todo->dtStart().isValid() ) { // No need to add here. This is separated issue and each line // is very visible on its own. On the other hand... Yes, I like it // italics here :) ret += "
      " + i18n( "Start: %1", IncidenceFormatter::dateToString( todo->dtStart( false ), true, mSpec ) ); } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { ret += "
      " + i18n( "Due: %1", IncidenceFormatter::dateTimeToString( todo->dtDue(), todo->allDay(), true, mSpec ) ); } if ( todo->isCompleted() ) { ret += "
      " + i18n( "Completed: %1", todo->completedStr() ); } else { ret += "
      " + i18nc( "percent complete", "%1 % completed", todo->percentComplete() ); } return ret.replace( ' ', " " ); } QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal ) { //FIXME: support mRichText==false QString ret; if ( journal->dtStart().isValid() ) { ret += "
      " + i18n( "Date: %1", IncidenceFormatter::dateToString( journal->dtStart(), false, mSpec ) ); } return ret.replace( ' ', " " ); } QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb ) { //FIXME: support mRichText==false QString ret; ret = "
      " + i18n( "Period start: %1", KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) ); ret += "
      " + i18n( "Period start: %1", KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) ); return ret.replace( ' ', " " ); } bool IncidenceFormatter::ToolTipVisitor::visit( Event *event ) { mResult = generateToolTip( event, dateRangeText( event ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo ) { mResult = generateToolTip( todo, dateRangeText( todo ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal ) { mResult = generateToolTip( journal, dateRangeText( journal ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb ) { //FIXME: support mRichText==false mResult = "" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + ""; mResult += dateRangeText( fb ); mResult += ""; return !mResult.isEmpty(); } QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence, QString dtRangeText ) { //FIXME: support mRichText==false if ( !incidence ) { return QString(); } QString tmp = ""+ incidence->richSummary() + ""; tmp += dtRangeText; if ( !incidence->location().isEmpty() ) { // Put Location: in italics tmp += "
      " + i18n( "Location: %1", incidence->richLocation() ); } if ( !incidence->description().isEmpty() ) { QString desc( incidence->description() ); if ( !incidence->descriptionIsRich() ) { if ( desc.length() > 120 ) { desc = desc.left( 120 ) + "..."; } desc = Qt::escape( desc ).replace( '\n', "
      " ); } else { // TODO: truncate the description when it's rich text } tmp += "
      ----------
      " + i18n( "Description:" ) + "
      " + desc; } tmp += "
      "; return tmp; } //@endcond QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText ) { return toolTipStr( incidence, richText, KDateTime::Spec() ); } QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence, bool richText, KDateTime::Spec spec ) { ToolTipVisitor v; if ( v.act( incidence, richText, spec ) ) { return v.result(); } else { return QString(); } } /******************************************************************* * Helper functions for the Incidence tooltips *******************************************************************/ //@cond PRIVATE static QString mailBodyIncidence( Incidence *incidence ) { QString body; if ( !incidence->summary().isEmpty() ) { body += i18n( "Summary: %1\n", incidence->richSummary() ); } if ( !incidence->organizer().isEmpty() ) { body += i18n( "Organizer: %1\n", incidence->organizer().fullName() ); } if ( !incidence->location().isEmpty() ) { body += i18n( "Location: %1\n", incidence->richLocation() ); } return body; } //@endcond //@cond PRIVATE class KCal::IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor { public: MailBodyVisitor() : mSpec( KDateTime::Spec() ), mResult( "" ) {} bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() ) { mSpec = spec; mResult = ""; return incidence ? incidence->accept( *this ) : false; } QString result() const { return mResult; } protected: bool visit( Event *event ); bool visit( Todo *todo ); bool visit( Journal *journal ); bool visit( FreeBusy * ) { mResult = i18n( "This is a Free Busy Object" ); return !mResult.isEmpty(); } protected: KDateTime::Spec mSpec; QString mResult; }; bool IncidenceFormatter::MailBodyVisitor::visit( Event *event ) { QString recurrence[]= { i18nc( "no recurrence", "None" ), i18nc( "event recurs by minutes", "Minutely" ), i18nc( "event recurs by hours", "Hourly" ), i18nc( "event recurs by days", "Daily" ), i18nc( "event recurs by weeks", "Weekly" ), i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ), i18nc( "event recurs same day each month", "Monthly Same Day" ), i18nc( "event recurs same month each year", "Yearly Same Month" ), i18nc( "event recurs same day each year", "Yearly Same Day" ), i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" ) }; mResult = mailBodyIncidence( event ); mResult += i18n( "Start Date: %1\n", IncidenceFormatter::dateToString( event->dtStart(), true, mSpec ) ); if ( !event->allDay() ) { mResult += i18n( "Start Time: %1\n", IncidenceFormatter::timeToString( event->dtStart(), true, mSpec ) ); } if ( event->dtStart() != event->dtEnd() ) { mResult += i18n( "End Date: %1\n", IncidenceFormatter::dateToString( event->dtEnd(), true, mSpec ) ); } if ( !event->allDay() ) { mResult += i18n( "End Time: %1\n", IncidenceFormatter::timeToString( event->dtEnd(), true, mSpec ) ); } if ( event->recurs() ) { Recurrence *recur = event->recurrence(); // TODO: Merge these two to one of the form "Recurs every 3 days" mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] ); mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() ); if ( recur->duration() > 0 ) { mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() ); mResult += '\n'; } else { if ( recur->duration() != -1 ) { // TODO_Recurrence: What to do with all-day QString endstr; if ( event->allDay() ) { endstr = KGlobal::locale()->formatDate( recur->endDate() ); } else { endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() ); } mResult += i18n( "Repeat until: %1\n", endstr ); } else { mResult += i18n( "Repeats forever\n" ); } } } QString details = event->richDescription(); if ( !details.isEmpty() ) { mResult += i18n( "Details:\n%1\n", details ); } return !mResult.isEmpty(); } bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo ) { mResult = mailBodyIncidence( todo ); if ( todo->hasStartDate() && todo->dtStart().isValid() ) { mResult += i18n( "Start Date: %1\n", IncidenceFormatter::dateToString( todo->dtStart(false), true, mSpec ) ); if ( !todo->allDay() ) { mResult += i18n( "Start Time: %1\n", IncidenceFormatter::timeToString( todo->dtStart(false), true, mSpec ) ); } } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { mResult += i18n( "Due Date: %1\n", IncidenceFormatter::dateToString( todo->dtDue(), true, mSpec ) ); if ( !todo->allDay() ) { mResult += i18n( "Due Time: %1\n", IncidenceFormatter::timeToString( todo->dtDue(), true, mSpec ) ); } } QString details = todo->richDescription(); if ( !details.isEmpty() ) { mResult += i18n( "Details:\n%1\n", details ); } return !mResult.isEmpty(); } bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal ) { mResult = mailBodyIncidence( journal ); mResult += i18n( "Date: %1\n", IncidenceFormatter::dateToString( journal->dtStart(), true, mSpec ) ); if ( !journal->allDay() ) { mResult += i18n( "Time: %1\n", IncidenceFormatter::timeToString( journal->dtStart(), true, mSpec ) ); } if ( !journal->description().isEmpty() ) { mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() ); } return !mResult.isEmpty(); } //@endcond QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence ) { return mailBodyStr( incidence, KDateTime::Spec() ); } QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence, KDateTime::Spec spec ) { if ( !incidence ) { return QString(); } MailBodyVisitor v; if ( v.act( incidence, spec ) ) { return v.result(); } return QString(); } //@cond PRIVATE static QString recurEnd( Incidence *incidence ) { QString endstr; if ( incidence->allDay() ) { endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() ); } else { endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() ); } return endstr; } //@endcond QString IncidenceFormatter::recurrenceString( Incidence *incidence ) { if ( !incidence->recurs() ) { return i18n( "No recurrence" ); } QStringList dayList; dayList.append( i18n( "31st Last" ) ); dayList.append( i18n( "30th Last" ) ); dayList.append( i18n( "29th Last" ) ); dayList.append( i18n( "28th Last" ) ); dayList.append( i18n( "27th Last" ) ); dayList.append( i18n( "26th Last" ) ); dayList.append( i18n( "25th Last" ) ); dayList.append( i18n( "24th Last" ) ); dayList.append( i18n( "23rd Last" ) ); dayList.append( i18n( "22nd Last" ) ); dayList.append( i18n( "21st Last" ) ); dayList.append( i18n( "20th Last" ) ); dayList.append( i18n( "19th Last" ) ); dayList.append( i18n( "18th Last" ) ); dayList.append( i18n( "17th Last" ) ); dayList.append( i18n( "16th Last" ) ); dayList.append( i18n( "15th Last" ) ); dayList.append( i18n( "14th Last" ) ); dayList.append( i18n( "13th Last" ) ); dayList.append( i18n( "12th Last" ) ); dayList.append( i18n( "11th Last" ) ); dayList.append( i18n( "10th Last" ) ); dayList.append( i18n( "9th Last" ) ); dayList.append( i18n( "8th Last" ) ); dayList.append( i18n( "7th Last" ) ); dayList.append( i18n( "6th Last" ) ); dayList.append( i18n( "5th Last" ) ); dayList.append( i18n( "4th Last" ) ); dayList.append( i18n( "3rd Last" ) ); dayList.append( i18n( "2nd Last" ) ); dayList.append( i18nc( "last day of the month", "Last" ) ); dayList.append( i18nc( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI dayList.append( i18n( "1st" ) ); dayList.append( i18n( "2nd" ) ); dayList.append( i18n( "3rd" ) ); dayList.append( i18n( "4th" ) ); dayList.append( i18n( "5th" ) ); dayList.append( i18n( "6th" ) ); dayList.append( i18n( "7th" ) ); dayList.append( i18n( "8th" ) ); dayList.append( i18n( "9th" ) ); dayList.append( i18n( "10th" ) ); dayList.append( i18n( "11th" ) ); dayList.append( i18n( "12th" ) ); dayList.append( i18n( "13th" ) ); dayList.append( i18n( "14th" ) ); dayList.append( i18n( "15th" ) ); dayList.append( i18n( "16th" ) ); dayList.append( i18n( "17th" ) ); dayList.append( i18n( "18th" ) ); dayList.append( i18n( "19th" ) ); dayList.append( i18n( "20th" ) ); dayList.append( i18n( "21st" ) ); dayList.append( i18n( "22nd" ) ); dayList.append( i18n( "23rd" ) ); dayList.append( i18n( "24th" ) ); dayList.append( i18n( "25th" ) ); dayList.append( i18n( "26th" ) ); dayList.append( i18n( "27th" ) ); dayList.append( i18n( "28th" ) ); dayList.append( i18n( "29th" ) ); dayList.append( i18n( "30th" ) ); dayList.append( i18n( "31st" ) ); int weekStart = KGlobal::locale()->weekStartDay(); QString dayNames; QString txt; const KCalendarSystem *calSys = KGlobal::locale()->calendar(); Recurrence *recur = incidence->recurrence(); switch ( recur->recurrenceType() ) { case Recurrence::rNone: return i18n( "No recurrence" ); case Recurrence::rMinutely: if ( recur->duration() != -1 ) { txt = i18np( "Recurs every minute until %2", "Recurs every %1 minutes until %2", recur->frequency(), recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18np( "Recurs every minute", "Recurs every %1 minutes", recur->frequency() ); case Recurrence::rHourly: if ( recur->duration() != -1 ) { txt = i18np( "Recurs hourly until %2", "Recurs every %1 hours until %2", recur->frequency(), recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() ); case Recurrence::rDaily: if ( recur->duration() != -1 ) { txt = i18np( "Recurs daily until %2", "Recurs every %1 days until %2", recur->frequency(), recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() ); case Recurrence::rWeekly: { bool addSpace = false; for ( int i = 0; i < 7; ++i ) { if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) { if ( addSpace ) { dayNames.append( i18nc( "separator for list of days", ", " ) ); } dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, KCalendarSystem::ShortDayName ) ); addSpace = true; } } if ( dayNames.isEmpty() ) { dayNames = i18nc( "Recurs weekly on no days", "no days" ); } if ( recur->duration() != -1 ) { txt = i18ncp( "Recurs weekly on [list of days] until end-date", "Recurs weekly on %2 until %3", "Recurs every %1 weeks on %2 until %3", recur->frequency(), dayNames, recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18ncp( "Recurs weekly on [list of days]", "Recurs weekly on %2", "Recurs every %1 weeks on %2", recur->frequency(), dayNames ); } case Recurrence::rMonthlyPos: { KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0]; if ( recur->duration() != -1 ) { txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]" " weekdayname until end-date", "Recurs every month on the %2 %3 until %4", "Recurs every %1 months on the %2 %3 until %4", recur->frequency(), dayList[rule.pos() + 31], calSys->weekDayName( rule.day(),KCalendarSystem::LongDayName ), recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname", "Recurs every month on the %2 %3", "Recurs every %1 months on the %2 %3", recur->frequency(), dayList[rule.pos() + 31], calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) ); } case Recurrence::rMonthlyDay: { int days = recur->monthDays()[0]; if ( recur->duration() != -1 ) { txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date", "Recurs monthly on the %2 day until %3", "Recurs every %1 months on the %2 day until %3", recur->frequency(), dayList[days + 31], recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18ncp( "Recurs monthly on the [1st|2nd|...] day", "Recurs monthly on the %2 day", "Recurs every %1 month on the %2 day", recur->frequency(), dayList[days + 31] ); } case Recurrence::rYearlyMonth: { if ( recur->duration() != -1 ) { txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]" " until end-date", "Recurs yearly on %2 %3 until %4", "Recurs every %1 years on %2 %3 until %4", recur->frequency(), calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ), dayList[ recur->yearDates()[0] + 31 ], recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } if ( !recur->yearDates().isEmpty() ) { return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]", "Recurs yearly on %2 %3", "Recurs every %1 years on %2 %3", recur->frequency(), calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ), dayList[ recur->yearDates()[0] + 31 ] ); } else { if (!recur->yearMonths().isEmpty() ) { return i18nc( "Recurs Every year on month-name [1st|2nd|...]", "Recurs yearly on %1 %2", calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ), dayList[ recur->startDate().day() + 31 ] ); } else { return i18nc( "Recurs Every year on month-name [1st|2nd|...]", "Recurs yearly on %1 %2", calSys->monthName( recur->startDate().month(), recur->startDate().year() ), dayList[ recur->startDate().day() + 31 ] ); } } } case Recurrence::rYearlyDay: if ( recur->duration() != -1 ) { txt = i18ncp( "Recurs every N years on day N until end-date", "Recurs every year on day %2 until %3", "Recurs every %1 years" " on day %2 until %3", recur->frequency(), recur->yearDays()[0], recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18ncp( "Recurs every N YEAR[S] on day N", "Recurs every year on day %2", "Recurs every %1 years" " on day %2", recur->frequency(), recur->yearDays()[0] ); case Recurrence::rYearlyPos: { KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0]; if ( recur->duration() != -1 ) { txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname " "of monthname until end-date", "Every year on the %2 %3 of %4 until %5", "Every %1 years on the %2 %3 of %4" " until %5", recur->frequency(), dayList[rule.pos() + 31], calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ), calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ), recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18nc( "number of occurrences", " (%1 occurrences)", recur->duration() ); } return txt; } return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname " "of monthname", "Every year on the %2 %3 of %4", "Every %1 years on the %2 %3 of %4", recur->frequency(), dayList[rule.pos() + 31], calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ), calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ); } default: return i18n( "Incidence recurs" ); } } QString IncidenceFormatter::timeToString( const KDateTime &date, bool shortfmt, const KDateTime::Spec &spec ) { if ( spec.isValid() ) { QString timeZone; if ( spec.timeZone() != KSystemTimeZones::local() ) { timeZone = ' ' + spec.timeZone().name(); } return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone; } else { return KGlobal::locale()->formatTime( date.time(), !shortfmt ); } } QString IncidenceFormatter::dateToString( const KDateTime &date, bool shortfmt, const KDateTime::Spec &spec ) { if ( spec.isValid() ) { QString timeZone; if ( spec.timeZone() != KSystemTimeZones::local() ) { timeZone = ' ' + spec.timeZone().name(); } return KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(), ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; } else { return KGlobal::locale()->formatDate( date.date(), ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); } } QString IncidenceFormatter::dateTimeToString( const KDateTime &date, bool allDay, bool shortfmt, const KDateTime::Spec &spec ) { if ( allDay ) { return dateToString( date, shortfmt, spec ); } if ( spec.isValid() ) { QString timeZone; if ( spec.timeZone() != KSystemTimeZones::local() ) { timeZone = ' ' + spec.timeZone().name(); } return KGlobal::locale()->formatDateTime( date.toTimeSpec( spec ).dateTime(), ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone; } else { return KGlobal::locale()->formatDateTime( date.dateTime(), ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ); } }