kde-playground/kdepim/kalarm/resourceselector.cpp
2015-04-14 21:49:29 +00:00

938 lines
37 KiB
C++

/*
* resourceselector.cpp - calendar resource selection widget
* Program: kalarm
* Copyright © 2006-2013 by David Jarvie <djarvie@kde.org>
* Based on KOrganizer's ResourceView class and KAddressBook's ResourceSelection class,
* Copyright (C) 2003,2004 Cornelius Schumacher <schumacher@kde.org>
* Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
* Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "resourceselector.h"
#include "kalarm.h"
#include "alarmcalendar.h"
#include "autoqpointer.h"
#ifdef USE_AKONADI
#include "akonadiresourcecreator.h"
#include "calendarmigrator.h"
#include "kalarmapp.h"
#else
#include "alarmresources.h"
#include "eventlistmodel.h"
#include "resourcemodelview.h"
#endif
#include "messagebox.h"
#include "packedlayout.h"
#include "preferences.h"
#include "resourceconfigdialog.h"
#ifdef USE_AKONADI
#include <akonadi/agentmanager.h>
#include <akonadi/agentinstancecreatejob.h>
#include <akonadi/agenttype.h>
#include <akonadi/collectionpropertiesdialog.h>
#include <akonadi/entitydisplayattribute.h>
#else
#include <kcal/resourcecalendar.h>
#endif
#include <kdialog.h>
#include <klocale.h>
#include <kglobal.h>
#include <kcombobox.h>
#include <kinputdialog.h>
#include <kmenu.h>
#include <kdebug.h>
#include <kicon.h>
#include <kactioncollection.h>
#include <kaction.h>
#include <ktoggleaction.h>
#include <kcolordialog.h>
#include <QLabel>
#include <QPushButton>
#include <QTimer>
#include <QResizeEvent>
#include <QApplication>
#ifdef USE_AKONADI
using namespace KCalCore;
#else
using namespace KCal;
#endif
#ifdef USE_AKONADI
using namespace Akonadi;
#endif
#ifdef USE_AKONADI
ResourceSelector::ResourceSelector(QWidget* parent)
#else
ResourceSelector::ResourceSelector(AlarmResources* calendar, QWidget* parent)
#endif
: QFrame(parent),
#ifndef USE_AKONADI
mCalendar(calendar),
#endif
mContextMenu(0),
mActionReload(0),
mActionShowDetails(0),
mActionSetColour(0),
mActionClearColour(0),
mActionEdit(0),
#ifdef USE_AKONADI
mActionUpdate(0),
#else
mActionSave(0),
#endif
mActionRemove(0),
mActionImport(0),
mActionExport(0),
mActionSetDefault(0)
{
QBoxLayout* topLayout = new QVBoxLayout(this);
topLayout->setMargin(KDialog::spacingHint()); // use spacingHint for the margin
QLabel* label = new QLabel(i18nc("@title:group", "Calendars"), this);
topLayout->addWidget(label, 0, Qt::AlignHCenter);
mAlarmType = new KComboBox(this);
mAlarmType->addItem(i18nc("@item:inlistbox", "Active Alarms"));
mAlarmType->addItem(i18nc("@item:inlistbox", "Archived Alarms"));
mAlarmType->addItem(i18nc("@item:inlistbox", "Alarm Templates"));
mAlarmType->setFixedHeight(mAlarmType->sizeHint().height());
mAlarmType->setWhatsThis(i18nc("@info:whatsthis", "Choose which type of data to show alarm calendars for"));
topLayout->addWidget(mAlarmType);
// No spacing between combo box and listview.
#ifdef USE_AKONADI
CollectionFilterCheckListModel* model = new CollectionFilterCheckListModel(this);
mListView = new CollectionView(model, this);
#else
ResourceModel* model = ResourceModel::instance();
ResourceFilterModel* filterModel = new ResourceFilterModel(model, this);
mListView = new ResourceView(this);
mListView->setModel(filterModel);
#endif
connect(mListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged()));
mListView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(mListView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(contextMenuRequested(QPoint)));
mListView->setWhatsThis(i18nc("@info:whatsthis",
"List of available calendars of the selected type. The checked state shows whether a calendar "
"is enabled (checked) or disabled (unchecked). The default calendar is shown in bold."));
topLayout->addWidget(mListView, 1);
topLayout->addSpacing(KDialog::spacingHint());
PackedLayout* blayout = new PackedLayout(Qt::AlignHCenter);
blayout->setMargin(0);
blayout->setSpacing(KDialog::spacingHint());
topLayout->addLayout(blayout);
mAddButton = new QPushButton(i18nc("@action:button", "Add..."), this);
mEditButton = new QPushButton(i18nc("@action:button", "Edit..."), this);
mDeleteButton = new QPushButton(i18nc("@action:button", "Remove"), this);
blayout->addWidget(mAddButton);
blayout->addWidget(mEditButton);
blayout->addWidget(mDeleteButton);
mEditButton->setWhatsThis(i18nc("@info:whatsthis", "Edit the highlighted calendar"));
mDeleteButton->setWhatsThis(i18nc("@info:whatsthis", "<para>Remove the highlighted calendar from the list.</para>"
"<para>The calendar itself is left intact, and may subsequently be reinstated in the list if desired.</para>"));
mEditButton->setDisabled(true);
mDeleteButton->setDisabled(true);
connect(mAddButton, SIGNAL(clicked()), SLOT(addResource()));
connect(mEditButton, SIGNAL(clicked()), SLOT(editResource()));
connect(mDeleteButton, SIGNAL(clicked()), SLOT(removeResource()));
#ifdef USE_AKONADI
connect(AkonadiModel::instance(), SIGNAL(collectionAdded(Akonadi::Collection)),
SLOT(slotCollectionAdded(Akonadi::Collection)));
#else
connect(mCalendar, SIGNAL(resourceStatusChanged(AlarmResource*,AlarmResources::Change)), SLOT(slotStatusChanged(AlarmResource*,AlarmResources::Change)));
#endif
connect(mAlarmType, SIGNAL(activated(int)), SLOT(alarmTypeSelected()));
QTimer::singleShot(0, this, SLOT(alarmTypeSelected()));
Preferences::connect(SIGNAL(archivedKeepDaysChanged(int)), this, SLOT(archiveDaysChanged(int)));
}
/******************************************************************************
* Called when an alarm type has been selected.
* Filter the resource list to show resources of the selected alarm type, and
* add appropriate whatsThis texts to the list and to the Add button.
*/
void ResourceSelector::alarmTypeSelected()
{
QString addTip;
switch (mAlarmType->currentIndex())
{
case 0:
mCurrentAlarmType = CalEvent::ACTIVE;
addTip = i18nc("@info:tooltip", "Add a new active alarm calendar");
break;
case 1:
mCurrentAlarmType = CalEvent::ARCHIVED;
addTip = i18nc("@info:tooltip", "Add a new archived alarm calendar");
break;
case 2:
mCurrentAlarmType = CalEvent::TEMPLATE;
addTip = i18nc("@info:tooltip", "Add a new alarm template calendar");
break;
}
// WORKAROUND: Switch scroll bars off to avoid crash (see explanation
// in reinstateAlarmTypeScrollBars() description).
mListView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
#ifdef USE_AKONADI
mListView->collectionModel()->setEventTypeFilter(mCurrentAlarmType);
#else
static_cast<ResourceFilterModel*>(mListView->model())->setFilter(mCurrentAlarmType);
#endif
mAddButton->setWhatsThis(addTip);
mAddButton->setToolTip(addTip);
// WORKAROUND: Switch scroll bars back on after allowing geometry to update ...
QTimer::singleShot(0, this, SLOT(reinstateAlarmTypeScrollBars()));
#ifdef USE_AKONADI
selectionChanged(); // enable/disable buttons
#endif
}
/******************************************************************************
* WORKAROUND for crash due to presumed Qt bug.
* Switch scroll bars off. This is to avoid a crash which can very occasionally
* happen when changing from a list of calendars which requires vertical scroll
* bars, to a list whose text is very slightly wider but which doesn't require
* scroll bars at all. (The suspicion is that the width is such that it would
* require horizontal scroll bars if the vertical scroll bars were still
* present.) Presumably due to a Qt bug, this can result in a recursive call to
* ResourceView::viewportEvent() with a Resize event.
*
* The crash only occurs if the ResourceSelector happens to have exactly (within
* one pixel) the "right" width to create the crash.
*/
void ResourceSelector::reinstateAlarmTypeScrollBars()
{
mListView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
mListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
/******************************************************************************
* Prompt the user for a new resource to add to the list.
*/
void ResourceSelector::addResource()
{
#ifdef USE_AKONADI
AkonadiResourceCreator* creator = new AkonadiResourceCreator(mCurrentAlarmType, this);
connect(creator, SIGNAL(finished(AkonadiResourceCreator*,bool)),
SLOT(resourceAdded(AkonadiResourceCreator*,bool)));
creator->createResource();
#else
AlarmResourceManager* manager = mCalendar->resourceManager();
QStringList descs = manager->resourceTypeDescriptions();
bool ok = false;
QString desc = KInputDialog::getItem(i18nc("@title:window", "Calendar Configuration"),
i18nc("@info", "Select storage type of new calendar:"), descs, 0, false, &ok, this);
if (!ok || descs.isEmpty())
return;
QString type = manager->resourceTypeNames()[descs.indexOf(desc)];
AlarmResource* resource = dynamic_cast<AlarmResource*>(manager->createResource(type));
if (!resource)
{
KAMessageBox::error(this, i18nc("@info", "Unable to create calendar of type <resource>%1</resource>.", type));
return;
}
resource->setResourceName(i18nc("@info/plain", "%1 calendar", type));
resource->setAlarmType(mCurrentAlarmType);
resource->setActive(false); // prevent setReadOnly() declaring it as unwritable before we've tried to load it
// Use AutoQPointer to guard against crash on application exit while
// the dialogue is still open. It prevents double deletion (both on
// deletion of ResourceSelector, and on return from this function).
AutoQPointer<ResourceConfigDialog> dlg = new ResourceConfigDialog(this, resource);
if (dlg->exec() == QDialog::Accepted)
{
resource->setEnabled(true);
resource->setTimeSpec(Preferences::timeZone());
manager->add(resource);
manager->writeConfig();
mCalendar->resourceAdded(resource); // load the resource and connect in-process change signals
}
else
{
delete resource;
resource = 0;
}
#endif
}
#ifdef USE_AKONADI
/******************************************************************************
* Called when the job started by AkonadiModel::addCollection() has completed.
*/
void ResourceSelector::resourceAdded(AkonadiResourceCreator* creator, bool success)
{
if (success)
{
AgentInstance agent = creator->agentInstance();
if (agent.isValid())
{
// Note that we're expecting the agent's Collection to be added
mAddAgents += agent;
}
}
delete creator;
}
/******************************************************************************
* Called when a collection is added to the AkonadiModel.
*/
void ResourceSelector::slotCollectionAdded(const Collection& collection)
{
if (collection.isValid())
{
AgentInstance agent = AgentManager::self()->instance(collection.resource());
if (agent.isValid())
{
int i = mAddAgents.indexOf(agent);
if (i >= 0)
{
// The collection belongs to an agent created by addResource()
CalEvent::Types types = CalEvent::types(collection.contentMimeTypes());
CollectionControlModel::setEnabled(collection, types, true);
if (!(types & mCurrentAlarmType))
{
// The user has selected alarm types for the resource
// which don't include the currently displayed type.
// Show a collection list which includes a selected type.
int index = -1;
if (types & CalEvent::ACTIVE)
index = 0;
else if (types & CalEvent::ARCHIVED)
index = 1;
else if (types & CalEvent::TEMPLATE)
index = 2;
if (index >= 0)
{
mAlarmType->setCurrentIndex(index);
alarmTypeSelected();
}
}
mAddAgents.removeAt(i);
}
}
}
}
#endif
/******************************************************************************
* Edit the currently selected resource.
*/
void ResourceSelector::editResource()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
{
AgentInstance instance = AgentManager::self()->instance(collection.resource());
if (instance.isValid())
instance.configure(this);
}
#else
AlarmResource* resource = currentResource();
if (!resource)
return;
bool readOnly = resource->readOnly();
// Use AutoQPointer to guard against crash on application exit while
// the dialogue is still open. It prevents double deletion (both on
// deletion of ResourceSelector, and on return from this function).
AutoQPointer<ResourceConfigDialog> dlg = new ResourceConfigDialog(this, resource);
if (dlg->exec() == QDialog::Accepted)
{
// Act on any changed settings.
// Read-only is handled automatically by AlarmResource::setReadOnly().
if (!readOnly && resource->readOnly() && resource->standardResource())
{
// A standard resource is being made read-only.
if (resource->alarmType() == CalEvent::ACTIVE)
{
KAMessageBox::sorry(this, i18nc("@info", "You cannot make your default active alarm calendar read-only."));
resource->setReadOnly(false);
}
else if (resource->alarmType() == CalEvent::ARCHIVED && Preferences::archivedKeepDays())
{
// Only allow the archived alarms standard resource to be made read-only
// if we're not saving archived alarms.
KAMessageBox::sorry(this, i18nc("@info", "You cannot make your default archived alarm calendar "
"read-only while expired alarms are configured to be kept."));
resource->setReadOnly(false);
}
else if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "Do you really want to make your default calendar read-only?"))
== KMessageBox::Cancel)
{
resource->setReadOnly(false);
}
}
}
#endif
}
#ifdef USE_AKONADI
/******************************************************************************
* Update the backend storage format for the currently selected resource in the
* displayed list.
*/
void ResourceSelector::updateResource()
{
Collection collection = currentResource();
if (!collection.isValid())
return;
AkonadiModel::instance()->refresh(collection); // update with latest data
CalendarMigrator::updateToCurrentFormat(collection, true, this);
}
#endif
/******************************************************************************
* Remove the currently selected resource from the displayed list.
*/
void ResourceSelector::removeResource()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (!collection.isValid())
return;
QString name = collection.name();
// Check if it's the standard or only resource for at least one type.
CalEvent::Types allTypes = AkonadiModel::types(collection);
CalEvent::Types standardTypes = CollectionControlModel::standardTypes(collection, true);
CalEvent::Type currentType = currentResourceType();
CalEvent::Type stdType = (standardTypes & CalEvent::ACTIVE) ? CalEvent::ACTIVE
: (standardTypes & CalEvent::ARCHIVED) ? CalEvent::ARCHIVED
: CalEvent::EMPTY;
#else
AlarmResource* resource = currentResource();
if (!resource)
return;
QString name = resource->resourceName();
bool std = resource->standardResource();
// Check if it's the standard resource for its type.
CalEvent::Type stdType = std ? resource->alarmType() : CalEvent::EMPTY;
#endif
if (stdType == CalEvent::ACTIVE)
{
KAMessageBox::sorry(this, i18nc("@info", "You cannot remove your default active alarm calendar."));
return;
}
if (stdType == CalEvent::ARCHIVED && Preferences::archivedKeepDays())
{
// Only allow the archived alarms standard resource to be removed if
// we're not saving archived alarms.
KAMessageBox::sorry(this, i18nc("@info", "You cannot remove your default archived alarm calendar "
"while expired alarms are configured to be kept."));
return;
}
#ifdef USE_AKONADI
QString text;
if (standardTypes)
{
// It's a standard resource for at least one alarm type
if (allTypes != currentType)
{
// It also contains alarm types other than the currently displayed type
QString stdTypes = CollectionControlModel::typeListForDisplay(standardTypes);
QString otherTypes;
CalEvent::Types nonStandardTypes(allTypes & ~standardTypes);
if (nonStandardTypes != currentType)
otherTypes = i18nc("@info", "<para>It also contains:%1</para>", CollectionControlModel::typeListForDisplay(nonStandardTypes));
text = i18nc("@info", "<para><resource>%1</resource> is the default calendar for:%2</para>%3"
"<para>Do you really want to remove it from all calendar lists?</para>", name, stdTypes, otherTypes);
}
else
text = i18nc("@info", "Do you really want to remove your default calendar (<resource>%1</resource>) from the list?", name);
}
else if (allTypes != currentType)
text = i18nc("@info", "<para><resource>%1</resource> contains:%2</para><para>Do you really want to remove it from all calendar lists?</para>",
name, CollectionControlModel::typeListForDisplay(allTypes));
else
text = i18nc("@info", "Do you really want to remove the calendar <resource>%1</resource> from the list?", name);
#else
QString text = std ? i18nc("@info", "Do you really want to remove your default calendar (<resource>%1</resource>) from the list?", name)
: i18nc("@info", "Do you really want to remove the calendar <resource>%1</resource> from the list?", name);
#endif
if (KAMessageBox::warningContinueCancel(this, text, QString(), KStandardGuiItem::remove()) == KMessageBox::Cancel)
return;
#ifdef USE_AKONADI
AkonadiModel::instance()->removeCollection(collection);
#else
// Remove resource from alarm and resource lists before deleting it, to avoid
// crashes when display updates occur immediately after it is deleted.
if (resource->alarmType() == CalEvent::TEMPLATE)
EventListModel::templates()->removeResource(resource);
else
EventListModel::alarms()->removeResource(resource);
ResourceModel::instance()->removeResource(resource);
AlarmResourceManager* manager = mCalendar->resourceManager();
manager->remove(resource);
manager->writeConfig();
#endif
}
/******************************************************************************
* Called when the current selection changes, to enable/disable the
* Delete and Edit buttons accordingly.
*/
void ResourceSelector::selectionChanged()
{
bool state = mListView->selectionModel()->selectedRows().count();
mDeleteButton->setEnabled(state);
mEditButton->setEnabled(state);
}
/******************************************************************************
* Initialise the button and context menu actions.
*/
void ResourceSelector::initActions(KActionCollection* actions)
{
mActionReload = new KAction(KIcon(QLatin1String("view-refresh")), i18nc("@action Reload calendar", "Re&load"), this);
actions->addAction(QLatin1String("resReload"), mActionReload);
connect(mActionReload, SIGNAL(triggered(bool)), SLOT(reloadResource()));
#ifndef USE_AKONADI
mActionSave = new KAction(KIcon(QLatin1String("document-save")), i18nc("@action", "&Save"), this);
actions->addAction(QLatin1String("resSave"), mActionSave);
connect(mActionSave, SIGNAL(triggered(bool)), SLOT(saveResource()));
#endif
mActionShowDetails = new KAction(KIcon(QLatin1String("help-about")), i18nc("@action", "Show &Details"), this);
actions->addAction(QLatin1String("resDetails"), mActionShowDetails);
connect(mActionShowDetails, SIGNAL(triggered(bool)), SLOT(showInfo()));
mActionSetColour = new KAction(KIcon(QLatin1String("color-picker")), i18nc("@action", "Set &Color..."), this);
actions->addAction(QLatin1String("resSetColour"), mActionSetColour);
connect(mActionSetColour, SIGNAL(triggered(bool)), SLOT(setColour()));
mActionClearColour = new KAction(i18nc("@action", "Clear C&olor"), this);
actions->addAction(QLatin1String("resClearColour"), mActionClearColour);
connect(mActionClearColour, SIGNAL(triggered(bool)), SLOT(clearColour()));
mActionEdit = new KAction(KIcon(QLatin1String("document-properties")), i18nc("@action", "&Edit..."), this);
actions->addAction(QLatin1String("resEdit"), mActionEdit);
connect(mActionEdit, SIGNAL(triggered(bool)), SLOT(editResource()));
#ifdef USE_AKONADI
mActionUpdate = new KAction(i18nc("@action", "&Update Calendar Format"), this);
actions->addAction(QLatin1String("resUpdate"), mActionUpdate);
connect(mActionUpdate, SIGNAL(triggered(bool)), SLOT(updateResource()));
#endif
mActionRemove = new KAction(KIcon(QLatin1String("edit-delete")), i18nc("@action", "&Remove"), this);
actions->addAction(QLatin1String("resRemove"), mActionRemove);
connect(mActionRemove, SIGNAL(triggered(bool)), SLOT(removeResource()));
mActionSetDefault = new KToggleAction(this);
actions->addAction(QLatin1String("resDefault"), mActionSetDefault);
connect(mActionSetDefault, SIGNAL(triggered(bool)), SLOT(setStandard()));
QAction* action = new KAction(KIcon(QLatin1String("document-new")), i18nc("@action", "&Add..."), this);
actions->addAction(QLatin1String("resAdd"), action);
connect(action, SIGNAL(triggered(bool)), SLOT(addResource()));
mActionImport = new KAction(i18nc("@action", "Im&port..."), this);
actions->addAction(QLatin1String("resImport"), mActionImport);
connect(mActionImport, SIGNAL(triggered(bool)), SLOT(importCalendar()));
mActionExport = new KAction(i18nc("@action", "E&xport..."), this);
actions->addAction(QLatin1String("resExport"), mActionExport);
connect(mActionExport, SIGNAL(triggered(bool)), SLOT(exportCalendar()));
}
void ResourceSelector::setContextMenu(KMenu* menu)
{
mContextMenu = menu;
}
/******************************************************************************
* Display the context menu for the selected calendar.
*/
void ResourceSelector::contextMenuRequested(const QPoint& viewportPos)
{
if (!mContextMenu)
return;
bool active = false;
bool writable = false;
#ifdef USE_AKONADI
bool updatable = false;
Collection collection;
#else
AlarmResource* resource = 0;
#endif
if (mListView->selectionModel()->hasSelection())
{
QModelIndex index = mListView->indexAt(viewportPos);
if (index.isValid())
#ifdef USE_AKONADI
collection = mListView->collectionModel()->collection(index);
#else
resource = static_cast<ResourceFilterModel*>(mListView->model())->resource(index);
#endif
else
mListView->clearSelection();
}
CalEvent::Type type = currentResourceType();
#ifdef USE_AKONADI
bool haveCalendar = collection.isValid();
#else
bool haveCalendar = resource;
#endif
if (haveCalendar)
{
#ifdef USE_AKONADI
// Note: the CollectionControlModel functions call AkonadiModel::refresh(collection)
active = CollectionControlModel::isEnabled(collection, type);
KACalendar::Compat compatibility;
int rw = CollectionControlModel::isWritableEnabled(collection, type, compatibility);
writable = (rw > 0);
if (!rw
&& (compatibility & ~KACalendar::Converted)
&& !(compatibility & ~(KACalendar::Convertible | KACalendar::Converted)))
updatable = true; // the calendar format is convertible to the current KAlarm format
if (!(AkonadiModel::instance()->types(collection) & type))
type = CalEvent::EMPTY;
#else
active = resource->isEnabled();
type = resource->alarmType();
writable = resource->writable();
#endif
}
mActionReload->setEnabled(active);
mActionShowDetails->setEnabled(haveCalendar);
mActionSetColour->setEnabled(haveCalendar);
mActionClearColour->setEnabled(haveCalendar);
#ifdef USE_AKONADI
mActionClearColour->setVisible(AkonadiModel::instance()->backgroundColor(collection).isValid());
#else
mActionClearColour->setVisible(resource && resource->colour().isValid());
mActionSave->setEnabled(active && writable);
#endif
mActionEdit->setEnabled(haveCalendar);
#ifdef USE_AKONADI
mActionUpdate->setEnabled(updatable);
#endif
mActionRemove->setEnabled(haveCalendar);
mActionImport->setEnabled(active && writable);
mActionExport->setEnabled(active);
QString text;
switch (type)
{
case CalEvent::ACTIVE: text = i18nc("@action", "Use as &Default for Active Alarms"); break;
case CalEvent::ARCHIVED: text = i18nc("@action", "Use as &Default for Archived Alarms"); break;
case CalEvent::TEMPLATE: text = i18nc("@action", "Use as &Default for Alarm Templates"); break;
default: break;
}
mActionSetDefault->setText(text);
#ifdef USE_AKONADI
bool standard = CollectionControlModel::isStandard(collection, type);
#else
bool standard = (resource && resource == mCalendar->getStandardResource(static_cast<CalEvent::Type>(type)) && resource->standardResource());
#endif
mActionSetDefault->setChecked(active && writable && standard);
mActionSetDefault->setEnabled(active && writable);
mContextMenu->popup(mListView->viewport()->mapToGlobal(viewportPos));
}
/******************************************************************************
* Called from the context menu to reload the selected resource.
*/
void ResourceSelector::reloadResource()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
AkonadiModel::instance()->reloadCollection(collection);
#else
AlarmResource* resource = currentResource();
if (resource)
AlarmCalendar::resources()->loadResource(resource, this);
#endif
}
/******************************************************************************
* Called from the context menu to save the selected resource.
*/
void ResourceSelector::saveResource()
{
#ifdef USE_AKONADI
// Save resource is not applicable to Akonadi
#else
AlarmResource* resource = currentResource();
if (resource)
resource->save();
#endif
}
/******************************************************************************
* Called when the length of time archived alarms are to be stored changes.
* If expired alarms are now to be stored, set any single archived alarm
* resource to be the default.
*/
void ResourceSelector::archiveDaysChanged(int days)
{
if (days)
{
#ifdef USE_AKONADI
if (!CollectionControlModel::getStandard(CalEvent::ARCHIVED).isValid())
{
Collection::List cols = CollectionControlModel::enabledCollections(CalEvent::ARCHIVED, true);
if (cols.count() == 1)
{
CollectionControlModel::setStandard(cols[0], CalEvent::ARCHIVED);
theApp()->purgeNewArchivedDefault(cols[0]);
}
}
#else
AlarmResources* resources = AlarmResources::instance();
AlarmResource* std = resources->getStandardResource(CalEvent::ARCHIVED);
if (std && !std->standardResource())
resources->setStandardResource(std);
#endif
}
}
/******************************************************************************
* Called from the context menu to set the selected resource as the default
* for its alarm type. The resource is automatically made active.
*/
void ResourceSelector::setStandard()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
{
CalEvent::Type alarmType = currentResourceType();
bool standard = mActionSetDefault->isChecked();
if (standard)
CollectionControlModel::setEnabled(collection, alarmType, true);
CollectionControlModel::setStandard(collection, alarmType, standard);
if (alarmType == CalEvent::ARCHIVED)
theApp()->purgeNewArchivedDefault(collection);
}
#else
AlarmResource* resource = currentResource();
if (resource)
{
if (mActionSetDefault->isChecked())
{
resource->setEnabled(true);
mCalendar->setStandardResource(resource);
}
else
resource->setStandardResource(false);
}
#endif
}
#ifndef USE_AKONADI
/******************************************************************************
* Called when a calendar status has changed.
*/
void ResourceSelector::slotStatusChanged(AlarmResource* resource, AlarmResources::Change change)
{
if (change == AlarmResources::WrongType && resource->isWrongAlarmType())
{
QString text;
switch (resource->alarmType())
{
case CalEvent::ACTIVE:
text = i18nc("@info/plain", "It is not an active alarm calendar.");
break;
case CalEvent::ARCHIVED:
text = i18nc("@info/plain", "It is not an archived alarm calendar.");
break;
case CalEvent::TEMPLATE:
text = i18nc("@info/plain", "It is not an alarm template calendar.");
break;
default:
return;
}
KAMessageBox::sorry(this, i18nc("@info", "<para>Calendar <resource>%1</resource> has been disabled:</para><para>%2</para>", resource->resourceName(), text));
}
}
#endif
/******************************************************************************
* Called from the context menu to merge alarms from an external calendar into
* the selected resource (if any).
*/
void ResourceSelector::importCalendar()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
AlarmCalendar::importAlarms(this, (collection.isValid() ? &collection : 0));
#else
AlarmCalendar::importAlarms(this, currentResource());
#endif
}
/******************************************************************************
* Called from the context menu to copy the selected resource's alarms to an
* external calendar.
*/
void ResourceSelector::exportCalendar()
{
#ifdef USE_AKONADI
Collection calendar = currentResource();
if (calendar.isValid())
#else
AlarmResource* calendar = currentResource();
if (calendar)
#endif
AlarmCalendar::exportAlarms(AlarmCalendar::resources()->events(calendar), this);
}
/******************************************************************************
* Called from the context menu to set a colour for the selected resource.
*/
void ResourceSelector::setColour()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
{
QColor colour = AkonadiModel::instance()->backgroundColor(collection);
if (!colour.isValid())
colour = QApplication::palette().color(QPalette::Base);
if (KColorDialog::getColor(colour, QColor(), this) == KColorDialog::Accepted)
AkonadiModel::instance()->setBackgroundColor(collection, colour);
}
#else
AlarmResource* resource = currentResource();
if (resource)
{
QColor colour = resource->colour();
if (!colour.isValid())
colour = QApplication::palette().color(QPalette::Base);
if (KColorDialog::getColor(colour, QColor(), this) == KColorDialog::Accepted)
resource->setColour(colour);
}
#endif
}
/******************************************************************************
* Called from the context menu to clear the display colour for the selected
* resource.
*/
void ResourceSelector::clearColour()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
AkonadiModel::instance()->setBackgroundColor(collection, QColor());
#else
AlarmResource* resource = currentResource();
if (resource)
resource->setColour(QColor());
#endif
}
/******************************************************************************
* Called from the context menu to display information for the selected resource.
*/
void ResourceSelector::showInfo()
{
#ifdef USE_AKONADI
Collection collection = currentResource();
if (collection.isValid())
{
const QString name = collection.displayName();
QString id = collection.resource(); // resource name
CalEvent::Type alarmType = currentResourceType();
QString calType = AgentManager::self()->instance(id).type().name();
QString storage = AkonadiModel::instance()->storageType(collection);
QString location = collection.remoteId();
KUrl url(location);
if (url.isLocalFile())
location = url.path();
CalEvent::Types altypes = AkonadiModel::instance()->types(collection);
QStringList alarmTypes;
if (altypes & CalEvent::ACTIVE)
alarmTypes << i18nc("@info/plain", "Active alarms");
if (altypes & CalEvent::ARCHIVED)
alarmTypes << i18nc("@info/plain", "Archived alarms");
if (altypes & CalEvent::TEMPLATE)
alarmTypes << i18nc("@info/plain", "Alarm templates");
QString alarmTypeString = alarmTypes.join(i18nc("@info/plain List separator", ", "));
KACalendar::Compat compat;
QString perms = AkonadiModel::readOnlyTooltip(collection);
if (perms.isEmpty())
perms = i18nc("@info/plain", "Read-write");
QString enabled = CollectionControlModel::isEnabled(collection, alarmType)
? i18nc("@info/plain", "Enabled")
: i18nc("@info/plain", "Disabled");
QString std = CollectionControlModel::isStandard(collection, alarmType)
? i18nc("@info/plain Parameter in 'Default calendar: Yes/No'", "Yes")
: i18nc("@info/plain Parameter in 'Default calendar: Yes/No'", "No");
QString text = i18nc("@info",
"<title>%1</title>"
"<para>ID: %2<nl/>"
"Calendar type: %3<nl/>"
"Contents: %4<nl/>"
"%5: <filename>%6</filename><nl/>"
"Permissions: %7<nl/>"
"Status: %8<nl/>"
"Default calendar: %9</para>",
name, id, calType, alarmTypeString, storage, location, perms, enabled, std);
// Display the collection information. Because the user requested
// the information, don't raise a KNotify event.
KAMessageBox::information(this, text, QString(), QString(), 0);
}
#else
AlarmResource* resource = currentResource();
if (resource)
{
// Display the collection information. Because the user requested
// the information, don't raise a KNotify event.
KAMessageBox::information(this, resource->infoText(), QString(), QString(), 0);
}
#endif
}
/******************************************************************************
* Return the currently selected resource in the list.
*/
#ifdef USE_AKONADI
Collection ResourceSelector::currentResource() const
{
return mListView->collection(mListView->selectionModel()->currentIndex());
}
#else
AlarmResource* ResourceSelector::currentResource() const
{
return mListView->resource(mListView->selectionModel()->currentIndex());
}
#endif
/******************************************************************************
* Return the currently selected resource type.
*/
CalEvent::Type ResourceSelector::currentResourceType() const
{
switch (mAlarmType->currentIndex())
{
case 0: return CalEvent::ACTIVE;
case 1: return CalEvent::ARCHIVED;
case 2: return CalEvent::TEMPLATE;
default: return CalEvent::EMPTY;
}
}
void ResourceSelector::resizeEvent(QResizeEvent* re)
{
emit resized(re->oldSize(), re->size());
}
// vim: et sw=4: