Commit cf9f8b32 authored by David Faure's avatar David Faure
Browse files

Merge remote-tracking branch 'origin/release/19.12'

parents 457edcb7 809bdb25
KAlarm Change Log
=== Version 2.13.0 (KDE applications 19.12) --- 16 November 2019 ===
=== Version 2.13.0 (KDE Applications 19.12) --- 19 November 2019 ===
+ Fix user not always being prompted to update new resource if in old format.
+ Terminate application after executing 'kalarm --list'.
+ Fix alarm type column being too wide in alarm template list.
+ Fix failure to display image when alarm is configured to display an image file.
+ Fix failure to set no-autostart for non-KDE desktops, if no autostart directory exists.
+ Refactor to use generic resource classes.
=== Version 2.12.8 (KDE Applications 19.08.3) --- 16 October 2019 ===
......
......@@ -430,7 +430,7 @@ bool AkonadiModel::reloadResource(const Resource& resource)
}
/******************************************************************************
* Reload a collection from Akonadi storage. The backend data is not reloaded.
* Reload all collections from Akonadi storage. The backend data is not reloaded.
*/
void AkonadiModel::reload()
{
......@@ -627,7 +627,7 @@ void AkonadiModel::slotRowsInserted(const QModelIndex& parent, int start, int en
// Only notify new events if the collection is already populated.
// If not populated, all events will be notified when it is
// eventually populated.
if (res.isLoaded())
if (res.isPopulated())
events[res] += evnt;
mEventIds[evnt.id()] = EventIds(item.parentCollection().id(), item.id());
}
......
......@@ -130,7 +130,7 @@ void AkonadiResourceCreator::agentInstanceCreated(KJob* j)
if (result)
{
const QVector<Resource> resources = Resources::allResources();
const QVector<Resource> resources = Resources::allResources<AkonadiResource>();
QPointer<AgentConfigurationDialog> dlg = new AgentConfigurationDialog(mAgentInstance, mParent);
result = (dlg->exec() == QDialog::Accepted);
delete dlg;
......
......@@ -2089,7 +2089,7 @@ ShellProcess* KAlarmApp::doShellCommand(const QString& command, const KAEvent& e
if (alarm && alarm->dateTime().isValid())
{
const QString dateTime = alarm->dateTime().formatLocale();
heading.sprintf("\n******* KAlarm %s *******\n", dateTime.toLatin1().data());
heading = QStringLiteral("\n******* KAlarm %1 *******\n").arg(dateTime);
}
else
heading = QStringLiteral("\n******* KAlarm *******\n");
......@@ -2391,7 +2391,7 @@ bool KAlarmApp::waitUntilPopulated(ResourceId id, int timeout)
qCDebug(KALARM_LOG) << "KAlarmApp::waitUntilPopulated" << id;
const Resource res = Resources::resource(id);
if ((id < 0 && !Resources::allPopulated())
|| (id >= 0 && !res.isLoaded()))
|| (id >= 0 && !res.isPopulated()))
{
// Use AutoQPointer to guard against crash on application exit while
// the event loop is still running. It prevents double deletion (both
......@@ -2412,7 +2412,7 @@ bool KAlarmApp::waitUntilPopulated(ResourceId id, int timeout)
QTimer::singleShot(timeout * 1000, loop, &QEventLoop::quit);
loop->exec();
}
return (id < 0) ? Resources::allPopulated() : res.isLoaded();
return (id < 0) ? Resources::allPopulated() : res.isPopulated();
}
/******************************************************************************
......
......@@ -320,6 +320,7 @@ MessageWin::~MessageWin()
mAudioThread->quit();
mErrorMessages.remove(mEventId);
mWindowList.removeAll(this);
delete mTempFile;
if (!mRecreating)
{
if (!mNoPostAction && !mEvent.postAction().isEmpty())
......@@ -408,9 +409,24 @@ void MessageWin::initView()
{
opened = true;
const QByteArray data = job->data();
QTemporaryFile tmpFile;
tmpFile.write(data);
tmpFile.seek(0);
QMimeDatabase db;
QMimeType mime = db.mimeTypeForUrl(url);
if (mime.name() == QLatin1String("application/octet-stream"))
mime = db.mimeTypeForData(mTempFile);
const KAlarm::FileType fileType = KAlarm::fileType(mime);
switch (fileType)
{
case KAlarm::Image:
case KAlarm::TextFormatted:
delete mTempFile;
mTempFile = new QTemporaryFile;
mTempFile->open();
mTempFile->write(data);
break;
default:
break;
}
QTextBrowser* view = new QTextBrowser(topWidget);
view->setFrameStyle(QFrame::NoFrame);
......@@ -420,23 +436,21 @@ void MessageWin::initView()
view->viewport()->setPalette(pal);
view->setTextColor(mFgColour);
view->setCurrentFont(mFont);
QMimeDatabase db;
QMimeType mime = db.mimeTypeForUrl(url);
if (mime.name() == QLatin1String("application/octet-stream"))
mime = db.mimeTypeForData(&tmpFile);
switch (KAlarm::fileType(mime))
switch (fileType)
{
case KAlarm::Image:
view->setHtml(QLatin1String("<img source=\"") + tmpFile.fileName() + QLatin1String("\">"));
view->setHtml(QLatin1String("<div align=\"center\"><img src=\"") + mTempFile->fileName() + QLatin1String("\"></div>"));
mTempFile->close(); // keep the file available to be displayed
break;
case KAlarm::TextFormatted:
view->QTextBrowser::setSource(QUrl::fromLocalFile(tmpFile.fileName())); //krazy:exclude=qclasses
view->QTextBrowser::setSource(QUrl::fromLocalFile(mTempFile->fileName())); //krazy:exclude=qclasses
delete mTempFile;
mTempFile = nullptr;
break;
default:
{
view->setPlainText(QString::fromUtf8(data));
break;
}
}
view->setMinimumSize(view->sizeHint());
topLayout->addWidget(view);
......@@ -1848,6 +1862,8 @@ void MessageWin::frameDrawn()
*/
void MessageWin::displayComplete()
{
delete mTempFile;
mTempFile = nullptr;
playAudio();
if (mRescheduleEvent)
alarmShowing(mEvent);
......
......@@ -43,6 +43,7 @@ class PushButton;
class MessageText;
class QCheckBox;
class QLabel;
class QTemporaryFile;
class DeferAlarmDlg;
class EditAlarmDlg;
class ShellProcess;
......@@ -169,6 +170,7 @@ class MessageWin : public MainWindowBase
KAEvent mEvent; // the whole event, for updating the calendar file
KAEvent mOriginalEvent; // the original event supplied to the constructor
Resource mResource; // resource which the event comes/came from
QTemporaryFile* mTempFile{nullptr}; // temporary file used to display image/HTML
QLabel* mTimeLabel{nullptr}; // trigger time label
QLabel* mRemainingText{nullptr}; // the remaining time (for a reminder window)
PushButton* mOkButton;
......
......@@ -170,10 +170,12 @@ void Preferences::setNoAutoStart(bool yes)
// If the existing file isn't writable, find the path to create a writable copy
QString autostartFileRW = autostartFile;
QString configDirRW;
if (existingRO)
{
autostartFileRW = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/autostart/") + AUTOSTART_FILE;
if (autostartFileRW.isEmpty())
configDirRW = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
autostartFileRW = configDirRW + QLatin1String("/autostart/") + AUTOSTART_FILE;
if (configDirRW.isEmpty())
{
qCWarning(KALARM_LOG) << "Preferences::setNoAutoStart: No writable autostart file path";
return;
......@@ -231,6 +233,16 @@ void Preferences::setNoAutoStart(bool yes)
if (update)
{
// Write the updated file
QFileInfo info(configDirRW + QLatin1String("/autostart"));
if (!info.exists())
{
// First, create the directory for it.
if (!QDir(configDirRW).mkdir(QStringLiteral("autostart")))
{
qCWarning(KALARM_LOG) << "Preferences::setNoAutoStart: Error creating autostart file directory:" << info.filePath();
return;
}
}
QFile file(autostartFileRW);
if (!file.open(QIODevice::WriteOnly))
{
......
......@@ -162,7 +162,7 @@ QUrl AkonadiResource::location() const
QString AkonadiResource::displayLocation() const
{
// Don't simply use remoteId() since that may contain "file://" prefix.
// Don't simply use remoteId() since that may contain "file://" prefix, and percent encoding.
return location().toDisplayString(QUrl::PrettyDecoded | QUrl::PreferLocalFile);
}
......@@ -399,9 +399,9 @@ bool AkonadiResource::load(bool readThroughCache)
return true;
}
bool AkonadiResource::isLoaded() const
bool AkonadiResource::isPopulated() const
{
if (!ResourceType::isLoaded())
if (!ResourceType::isPopulated())
{
const QModelIndex ix = AkonadiModel::instance()->resourceIndex(mCollection.id());
if (!ix.data(AkonadiModel::IsPopulatedRole).toBool())
......@@ -423,20 +423,6 @@ bool AkonadiResource::save(bool writeThroughCache)
bool reload() override;
#endif
/******************************************************************************
* Close the resource.
*/
bool AkonadiResource::close()
{
qCDebug(KALARM_LOG) << "AkonadiResource::close:" << displayName();
mCollection.setId(-1);
mCollectionAttribute = CollectionAttribute();
mValid = false;
mHaveCollectionAttribute = false;
mNewEnabled = false;
return true;
}
/******************************************************************************
* Add an event to the resource, and add it to Akonadi.
*/
......
......@@ -234,7 +234,7 @@ public:
#endif
/** Return whether the resource has fully loaded. */
bool isLoaded() const override;
bool isPopulated() const override;
/** Save the resource.
* Not applicable to AkonadiResource, since AkonadiModel handles saving
......@@ -243,16 +243,6 @@ public:
*/
bool save(bool writeThroughCache = true) override;
/** Close the resource, without saving it.
* If the resource is currently being saved, it will be closed automatically
* once the save has completed.
*
* Derived classes must implement closing in doClose().
*
* @return true if closed, false if waiting for a save to complete.
*/
bool close() override;
/** Add an event to the resource, and add it to Akonadi. */
bool addEvent(const KAEvent&) override;
......
......@@ -220,9 +220,9 @@ bool Resource::load(bool readThroughCache)
return mResource.isNull() ? false : mResource->load(readThroughCache);
}
bool Resource::isLoaded() const
bool Resource::isPopulated() const
{
return mResource.isNull() ? false : mResource->isLoaded();
return mResource.isNull() ? false : mResource->isPopulated();
}
bool Resource::save(bool writeThroughCache)
......@@ -230,9 +230,9 @@ bool Resource::save(bool writeThroughCache)
return mResource.isNull() ? false : mResource->save(writeThroughCache);
}
bool Resource::close()
bool Resource::isSaving() const
{
return mResource.isNull() ? false : mResource->close();
return mResource.isNull() ? false : mResource->isSaving();
}
QList<KAEvent> Resource::events() const
......
......@@ -267,7 +267,7 @@ public:
#endif
/** Return whether the resource has fully loaded. */
bool isLoaded() const;
bool isPopulated() const;
/** Save the resource.
* Saving is not performed if the resource is disabled.
......@@ -280,15 +280,8 @@ public:
*/
bool save(bool writeThroughCache = true);
/** Close the resource, without saving it.
* If the resource is currently being saved, it will be closed automatically
* once the save has completed.
*
* Derived classes must implement closing in doClose().
*
* @return true if closed, false if waiting for a save to complete.
*/
bool close();
/** Return whether the resource is waiting for a save() to complete. */
bool isSaving() const;
/** Return all events belonging to this resource. */
QList<KAEvent> events() const;
......@@ -356,7 +349,10 @@ inline uint qHash(const Resource& resource, uint seed)
return qHash(resource.mResource.data(), seed);
}
/*****************************************************************************/
/*=============================================================================
* Template definitions.
*============================================================================*/
template <class Type> bool Resource::is() const
{
......
......@@ -56,25 +56,6 @@ Resource Resources::resource(ResourceId id)
return mResources.value(id, Resource::null());
}
/******************************************************************************
* Return all resources which contain a specified alarm type.
*/
QVector<Resource> Resources::allResources(CalEvent::Type type)
{
const CalEvent::Types types = (type == CalEvent::EMPTY)
? CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE
: type;
QVector<Resource> result;
for (auto it = mResources.constBegin(); it != mResources.constEnd(); ++it)
{
const Resource& res = it.value();
if (res.alarmTypes() & types)
result += res;
}
return result;
}
/******************************************************************************
* Return the resources which are enabled for a specified alarm type.
* If 'writable' is true, only writable resources are included.
......@@ -583,7 +564,7 @@ void Resources::checkResourcesPopulated()
// Check whether all resources have now loaded at least once.
for (auto it = mResources.constBegin(); it != mResources.constEnd(); ++it)
{
if (!it.value().isLoaded())
if (!it.value().isPopulated())
return;
}
mPopulated = true;
......@@ -591,26 +572,28 @@ void Resources::checkResourcesPopulated()
}
}
#if 0
/******************************************************************************
* Return whether one or all enabled collections have been loaded.
*/
bool Resources::isLoaded(ResourceId id)
bool Resources::isPopulated(ResourceId id)
{
if (id >= 0)
{
const Resource res = resource(id);
return res.isLoaded()
return res.isPopulated()
|| res.enabledTypes() == CalEvent::EMPTY;
}
for (auto it = mResources.constBegin(); it != mResources.constEnd(); ++it)
{
const Resource& res = it.value();
if (!res.isLoaded()
if (!res.isPopulated()
&& res.enabledTypes() != CalEvent::EMPTY)
return false;
}
return true;
}
#endif
// vim: et sw=4:
......@@ -53,10 +53,12 @@ public:
*/
static bool removeResource(Resource&);
/** Return all resources which contain a specified alarm type.
* @param type Alarm type to check for, or CalEvent::EMPTY for any type.
/** Return all resources of a kind which contain a specified alarm type.
* @tparam Type Resource type to fetch, default = all types.
* @param alarmType Alarm type to check for, or CalEvent::EMPTY for any type.
*/
static QVector<Resource> allResources(CalEvent::Type type = CalEvent::EMPTY);
template <class Type = ResourceType>
static QVector<Resource> allResources(CalEvent::Type alarmType = CalEvent::EMPTY);
/** Return the enabled resources which contain a specified alarm type.
* @param type Alarm type to check for, or CalEvent::EMPTY for any type.
......@@ -242,7 +244,6 @@ private:
static void removeResource(ResourceId);
static void checkResourcesPopulated();
static bool isLoaded(ResourceId);
static Resources* mInstance; // the unique instance
static QHash<ResourceId, Resource> mResources; // contains all ResourceType instances with an ID
......@@ -253,7 +254,26 @@ private:
};
/*===========================================================================*/
/*=============================================================================
* Template definitions.
*============================================================================*/
template <class Type>
QVector<Resource> Resources::allResources(CalEvent::Type type)
{
const CalEvent::Types types = (type == CalEvent::EMPTY)
? CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE
: type;
QVector<Resource> result;
for (auto it = mResources.constBegin(); it != mResources.constEnd(); ++it)
{
const Resource& res = it.value();
if (res.is<Type>() && (res.alarmTypes() & types))
result += res;
}
return result;
}
template <class DataModel>
Resource Resources::destination(CalEvent::Type type, QWidget* promptParent, bool noPrompt, bool* cancelled)
......
......@@ -271,9 +271,12 @@ void ResourceType::setDeletedEvents(const QList<KAEvent>& events)
void ResourceType::setLoaded(bool loaded) const
{
mLoaded = loaded;
if (loaded)
Resources::notifyResourcePopulated(this);
if (loaded != mLoaded)
{
mLoaded = loaded;
if (loaded)
Resources::notifyResourcePopulated(this);
}
}
QString ResourceType::storageTypeStr(bool description, bool file, bool local) const
......
......@@ -41,18 +41,24 @@ public:
/** The type of storage used by a resource. */
enum StorageType { NoStorage, File, Directory };
/** Settings change types. */
/** Settings change types. These may be combined.
* @note A resource's location is not allowed to change, except by
* deleting the resource and creating another resource with the new
* location. (Note that Akonadi resources do actually allow
* location change, but this is handled internally by Akonadi and
* has no impact on clients.)
*/
enum Change
{
NoChange = 0,
Name = 0x01,
AlarmTypes = 0x02,
Enabled = 0x04,
Standard = 0x08,
ReadOnly = 0x10,
KeepFormat = 0x20,
UpdateFormat = 0x40,
BackgroundColour = 0x80
Name = 0x01, //!< The resource's display name.
AlarmTypes = 0x02, //!< Alarm types contained in the resource.
Enabled = 0x04, //!< Alarm types which are enabled.
Standard = 0x08, //!< Alarm types which the resource is standard for.
ReadOnly = 0x10, //!< The resource's read-only setting.
KeepFormat = 0x20, //!< Whether the user has chosen not to convert to the current KAlarm format.
UpdateFormat = 0x40, //!< The resource should now be converted to the current KAlarm format.
BackgroundColour = 0x80 //!< The background colour to display the resource.
};
Q_DECLARE_FLAGS(Changes, Change)
......@@ -286,8 +292,11 @@ public:
virtual bool reload() = 0;
#endif
/** Return whether the resource has fully loaded. */
virtual bool isLoaded() const { return mLoaded; }
/** Return whether the resource has fully loaded.
* Once loading completes after the resource has initialised, this should
* always return true.
*/
virtual bool isPopulated() const { return mLoaded; }
/** Save the resource.
* Saving is not performed if the resource is disabled.
......@@ -300,15 +309,8 @@ public:
*/
virtual bool save(bool writeThroughCache = true) = 0;
/** Close the resource, without saving it.
* If the resource is currently being saved, it will be closed automatically
* once the save has completed.
*
* Derived classes must implement closing in doClose().
*
* @return true if closed, false if waiting for a save to complete.
*/
virtual bool close() = 0;
/** Return whether the resource is waiting for a save() to complete. */
virtual bool isSaving() const { return false; }
/** Return all events belonging to this resource, for enabled alarm types. */
QList<KAEvent> events() const;
......@@ -389,7 +391,9 @@ protected:
/** To be called when events have been deleted, to delete them from the resource's list. */
void setDeletedEvents(const QList<KAEvent>& events);
/** To be called when the loaded status of the resource has changed. */
void setLoaded(bool loaded) const;
QString storageTypeStr(bool description, bool file, bool local) const;
template <class T> static T* resource(Resource&);
template <class T> static const T* resource(const Resource&);
......@@ -398,13 +402,16 @@ private:
static ResourceType* data(Resource&);
static const ResourceType* data(const Resource&);
QHash<QString, KAEvent> mEvents; // all events in the resource, indexed by ID
QHash<QString, KAEvent> mEvents; // all events (of ALL types) in the resource, indexed by ID
ResourceId mId{-1}; // resource's ID, which can't be changed
mutable bool mLoaded{false}; // the resource has finished loading
bool mBeingDeleted{false}; // the resource is currently being deleted
};
/*****************************************************************************/
/*=============================================================================
* Template definitions.
*============================================================================*/
template <class T> T* ResourceType::resource(Resource& res)
{
......
......@@ -519,7 +519,7 @@ void ResourceSelector::showInfo()
if (altypes & CalEvent::TEMPLATE)
alarmTypes << i18nc("@info", "Alarm templates");
const QString alarmTypeString = alarmTypes.join(i18nc("@info List separator", ", "));
QString perms = AkonadiModel::readOnlyTooltip(resource);
QString perms = ResourceDataModelBase::readOnlyTooltip(resource);
if (perms.isEmpty())
perms = i18nc("@info", "Read-write");
const QString enabled = resource.isEnabled(alarmType)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment