diff --git a/src/declarativeimports/calendar/calendar.cpp b/src/declarativeimports/calendar/calendar.cpp index 790833b8d..ea636947d 100644 --- a/src/declarativeimports/calendar/calendar.cpp +++ b/src/declarativeimports/calendar/calendar.cpp @@ -1,352 +1,352 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek 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 #include "calendar.h" Calendar::Calendar(QObject *parent) : QObject(parent) , m_types(Holiday | Event | Todo | Journal) , m_dayList() , m_weekList() , m_days(0) , m_weeks(0) , m_firstDayOfWeek(QLocale::system().firstDayOfWeek()) , m_errorMessage() { m_daysModel = new DaysModel(this); m_daysModel->setSourceData(&m_dayList); // m_dayHelper = new CalendarDayHelper(this); // connect(m_dayHelper, SIGNAL(calendarChanged()), this, SLOT(updateData())); } QDate Calendar::displayedDate() const { return m_displayedDate; } void Calendar::setDisplayedDate(const QDate &dateTime) { if (m_displayedDate == dateTime) { return; } const int oldMonth = m_displayedDate.month(); const int oldYear = m_displayedDate.year(); m_displayedDate = dateTime; // m_dayHelper->setDate(m_displayedDate.year(), m_displayedDate.month()); updateData(); emit displayedDateChanged(); if (oldMonth != m_displayedDate.month()) { emit monthNameChanged(); } if (oldYear != m_displayedDate.year()) { emit yearChanged(); } } QDate Calendar::today() const { return m_today; } void Calendar::setToday(const QDate &dateTime) { if (dateTime == m_today) { return; } m_today = dateTime; if (m_displayedDate.isNull()) { resetToToday(); } else { // the else is to prevent calling updateData() twice // if the resetToToday() was called updateData(); } emit todayChanged(); } void Calendar::resetToToday() { m_displayedDate = m_today; updateData(); emit displayedDateChanged(); } int Calendar::types() const { return m_types; } void Calendar::setTypes(int types) { if (m_types == types) { return; } // m_types = static_cast(types); // updateTypes(); emit typesChanged(); } int Calendar::days() { return m_days; } void Calendar::setDays(int days) { if (m_days != days) { m_days = days; updateData(); emit daysChanged(); } } -int Calendar::weeks() +int Calendar::weeks() const { return m_weeks; } void Calendar::setWeeks(int weeks) { if (m_weeks != weeks) { m_weeks = weeks; updateData(); emit weeksChanged(); } } -int Calendar::firstDayOfWeek() +int Calendar::firstDayOfWeek() const { // QML has Sunday as 0, so we need to accomodate here return m_firstDayOfWeek == 7 ? 0 : m_firstDayOfWeek; } void Calendar::setFirstDayOfWeek(int day) { if (day > 7) { return; } if (m_firstDayOfWeek != day) { // QML has Sunday as 0, so we need to accomodate here // for QDate functions which have Sunday as 7 if (day == 0) { m_firstDayOfWeek = 7; } else { m_firstDayOfWeek = day; } updateData(); emit firstDayOfWeekChanged(); } } QString Calendar::errorMessage() const { return m_errorMessage; } int Calendar::currentWeek() const { QDate date(QDate::currentDate()); return date.weekNumber(); } QString Calendar::dayName(int weekday) const { return QDate::shortDayName(weekday); } QString Calendar::monthName() const { // Simple QDate::longMonthName won't do the job as it // will return the month name using LC_DATE locale which is used // for date formatting etc. So for example, in en_US locale // and cs_CZ LC_DATE, it would return Czech month names while // it should return English ones. So here we force the LANG // locale and take the month name from that. // // See https://bugs.kde.org/show_bug.cgi?id=353715 const QString lang = QLocale().uiLanguages().at(0); // If lang is empty, it will create just a system locale QLocale langLocale(lang); return langLocale.standaloneMonthName(m_displayedDate.month()); } int Calendar::year() const { return m_displayedDate.year(); } QAbstractListModel *Calendar::daysModel() const { return m_daysModel; } QJsonArray Calendar::weeksModel() const { return m_weekList; } void Calendar::updateData() { if (m_days == 0 || m_weeks == 0) { return; } m_dayList.clear(); m_weekList = QJsonArray(); int totalDays = m_days * m_weeks; int daysBeforeCurrentMonth = 0; int daysAfterCurrentMonth = 0; QDate firstDay(m_displayedDate.year(), m_displayedDate.month(), 1); // If the first day is the same as the starting day then we add a complete row before it. if (m_firstDayOfWeek < firstDay.dayOfWeek()) { daysBeforeCurrentMonth = firstDay.dayOfWeek() - m_firstDayOfWeek; } else { daysBeforeCurrentMonth = days() - (m_firstDayOfWeek - firstDay.dayOfWeek()); } int daysThusFar = daysBeforeCurrentMonth + m_displayedDate.daysInMonth(); if (daysThusFar < totalDays) { daysAfterCurrentMonth = totalDays - daysThusFar; } if (daysBeforeCurrentMonth > 0) { QDate previousMonth = m_displayedDate.addMonths(-1); //QDate previousMonth(m_displayedDate.year(), m_displayedDate.month() - 1, 1); for (int i = 0; i < daysBeforeCurrentMonth; i++) { DayData day; day.isCurrent = false; day.dayNumber = previousMonth.daysInMonth() - (daysBeforeCurrentMonth - (i + 1)); day.monthNumber = previousMonth.month(); day.yearNumber = previousMonth.year(); // day.containsEventItems = false; m_dayList << day; } } for (int i = 0; i < m_displayedDate.daysInMonth(); i++) { DayData day; day.isCurrent = true; day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates // day.containsEventItems = m_dayHelper->containsEventItems(i + 1); day.monthNumber = m_displayedDate.month(); day.yearNumber = m_displayedDate.year(); m_dayList << day; } if (daysAfterCurrentMonth > 0) { for (int i = 0; i < daysAfterCurrentMonth; i++) { DayData day; day.isCurrent = false; day.dayNumber = i + 1; // +1 to go form 0 based index to 1 based calendar dates // day.containsEventItems = false; day.monthNumber = m_displayedDate.addMonths(1).month(); day.yearNumber = m_displayedDate.addMonths(1).year(); m_dayList << day; } } const int numOfDaysInCalendar = m_dayList.count(); // Week numbers are always counted from Mondays // so find which index is Monday int mondayOffset = 0; if (!m_dayList.isEmpty()) { const DayData &data = m_dayList.at(0); QDate firstDay(data.yearNumber, data.monthNumber, data.dayNumber); // If the first day is not already Monday, get offset for Monday if (firstDay.dayOfWeek() != 1) { mondayOffset = 8 - firstDay.dayOfWeek(); } } // Fill weeksModel with the week numbers for (int i = mondayOffset; i < numOfDaysInCalendar; i += 7) { const DayData &data = m_dayList.at(i); m_weekList.append(QDate(data.yearNumber, data.monthNumber, data.dayNumber).weekNumber()); } emit weeksModelChanged(); m_daysModel->update(); // qDebug() << "---------------------------------------------------------------"; // qDebug() << "Date obj: " << m_displayedDate; // qDebug() << "Month: " << m_displayedDate.month(); // qDebug() << "m_days: " << m_days; // qDebug() << "m_weeks: " << m_weeks; // qDebug() << "Days before this month: " << daysBeforeCurrentMonth; // qDebug() << "Days after this month: " << daysAfterCurrentMonth; // qDebug() << "Days in current month: " << m_displayedDate.daysInMonth(); // qDebug() << "m_dayList size: " << m_dayList.count(); // qDebug() << "---------------------------------------------------------------"; } void Calendar::nextDecade() { setDisplayedDate(m_displayedDate.addYears(10)); } void Calendar::previousDecade() { setDisplayedDate(m_displayedDate.addYears(-10)); } void Calendar::nextYear() { setDisplayedDate(m_displayedDate.addYears(1)); } void Calendar::previousYear() { setDisplayedDate(m_displayedDate.addYears(-1)); } void Calendar::nextMonth() { setDisplayedDate(m_displayedDate.addMonths(1)); } void Calendar::previousMonth() { setDisplayedDate(m_displayedDate.addMonths(-1)); } void Calendar::goToMonth(int month) { setDisplayedDate(QDate(m_displayedDate.year(), month, 1)); } void Calendar::goToYear(int year) { setDisplayedDate(QDate(year, m_displayedDate.month(), 1)); } diff --git a/src/declarativeimports/calendar/calendar.h b/src/declarativeimports/calendar/calendar.h index 17783a4bc..a746bd915 100644 --- a/src/declarativeimports/calendar/calendar.h +++ b/src/declarativeimports/calendar/calendar.h @@ -1,219 +1,219 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek 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. */ #ifndef CALENDAR_H #define CALENDAR_H #include #include #include #include #include "daydata.h" #include "daysmodel.h" class Calendar : public QObject { Q_OBJECT /** * This property is used to determine which data from which month to show, it ensures * the day passed in the QDate is visible */ Q_PROPERTY(QDate displayedDate READ displayedDate WRITE setDisplayedDate NOTIFY displayedDateChanged) /** * This property is used to determine which data from which month to show, it ensures * the day passed in the QDate is visible */ Q_PROPERTY(QDate today READ today WRITE setToday NOTIFY todayChanged) /** * This determines which kind of data types should be contained in * selectedDayModel and upcomingEventsModel. By default all types are included. * NOTE: Only the Event type is fully implemented. * TODO: Fully implement the other data types throughout this code. */ Q_PROPERTY(int types READ types WRITE setTypes NOTIFY typesChanged) /** * This model contains the week numbers for the given date grid. */ Q_PROPERTY(QJsonArray weeksModel READ weeksModel NOTIFY weeksModelChanged) /** * The number of days a week contains. * TODO: perhaps this one can just be removed. A week is 7 days by definition. * However, i don't know if that's also the case in other more exotic calendars. */ Q_PROPERTY(int days READ days WRITE setDays NOTIFY daysChanged) /** * The number of weeks that the model property should contain. */ Q_PROPERTY(int weeks READ weeks WRITE setWeeks NOTIFY weeksChanged) /** * The start day of a week. By default this follows current Locale. It can be * changed. One then needs to use the numbers in the Qt DayOfWeek enum: * * Monday = 1 * Tuesday = 2 * Wednesday = 3 * Thursday = 4 * Friday = 5 * Saturday = 6 * Sunday = 7 * * This value doesn't do anything to other data structures, but helps you * visualizing the data. * * WARNING: QML has different enum values for week days - Sunday is 0, this function * automatically converts that on READ and WRITE and it's stored as QDate format * (ie. the one above). So firstDayOfWeek() call from QML would return 0 for Sunday * while internally it's 7 and vice-versa. */ Q_PROPERTY(int firstDayOfWeek READ firstDayOfWeek WRITE setFirstDayOfWeek NOTIFY firstDayOfWeekChanged) /** * The full year in a numeric value. For example 2013, not 13. */ Q_PROPERTY(int year READ year NOTIFY yearChanged) /** * If an error occured, it will be set in this string as human readable text. */ Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) /** * This is the human readable long month name. So not "Feb" but "February". * TODO: this should either be done in QML using javascript or by making a * function available because this is limiting. There are places * where you would want the short month name. */ Q_PROPERTY(QString monthName READ monthName NOTIFY monthNameChanged) /** * This model contains the actual grid data of days. For example, if you had set: * - days = 7 (7 days in one week) * - weeks = 6 (6 weeks in one month view) * then this model will contain 42 entries (days * weeks). Each entry contains * metadata about the current day. The exact metadata can be found in "daysmodel.cpp" * where the exact names usable in QML are being set. */ Q_PROPERTY(QAbstractListModel *daysModel READ daysModel CONSTANT) public: enum Type { Holiday = 1, Event = 2, Todo = 4, Journal = 8 }; Q_ENUM(Type) Q_DECLARE_FLAGS(Types, Type) enum DateMatchingPrecision { MatchYear, MatchYearAndMonth, MatchYearMonthAndDay }; Q_ENUM(DateMatchingPrecision) explicit Calendar(QObject *parent = nullptr); // Displayed date QDate displayedDate() const; void setDisplayedDate(const QDate &dateTime); // The day that represents "today" QDate today() const; void setToday(const QDate &dateTime); // Types int types() const; void setTypes(int types); // Days int days(); void setDays(int days); // Weeks - int weeks(); + int weeks() const; void setWeeks(int weeks); // Start day - int firstDayOfWeek(); + int firstDayOfWeek() const; void setFirstDayOfWeek(int day); // Error message QString errorMessage() const; // Month name QString monthName() const; int year() const; // Models QAbstractListModel *daysModel() const; QJsonArray weeksModel() const; // QML invokables Q_INVOKABLE void nextMonth(); Q_INVOKABLE void previousMonth(); Q_INVOKABLE void nextYear(); Q_INVOKABLE void previousYear(); Q_INVOKABLE void nextDecade(); Q_INVOKABLE void previousDecade(); Q_INVOKABLE QString dayName(int weekday) const; Q_INVOKABLE int currentWeek() const; Q_INVOKABLE void resetToToday(); Q_INVOKABLE void goToMonth(int month); Q_INVOKABLE void goToYear(int year); Q_SIGNALS: void displayedDateChanged(); void todayChanged(); void typesChanged(); void daysChanged(); void weeksChanged(); void firstDayOfWeekChanged(); void errorMessageChanged(); void monthNameChanged(); void yearChanged(); void weeksModelChanged(); public Q_SLOTS: void updateData(); private: QDate m_displayedDate; QDate m_today; Types m_types; QList m_dayList; DaysModel *m_daysModel; QJsonArray m_weekList; int m_days; int m_weeks; int m_firstDayOfWeek; QString m_errorMessage; }; #endif // CALENDAR_H diff --git a/src/declarativeimports/calendar/daysmodel.cpp b/src/declarativeimports/calendar/daysmodel.cpp index 6961b6b1b..273eab917 100644 --- a/src/declarativeimports/calendar/daysmodel.cpp +++ b/src/declarativeimports/calendar/daysmodel.cpp @@ -1,249 +1,249 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek 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 "daysmodel.h" #include "eventdatadecorator.h" #include "eventpluginsmanager.h" #include #include #include #include DaysModel::DaysModel(QObject *parent) : QAbstractListModel(parent), - m_pluginsManager(0), + m_pluginsManager(nullptr), m_lastRequestedEventsStartDate(QDate()), m_agendaNeedsUpdate(false) { } DaysModel::~DaysModel() { qDeleteAll(m_eventPlugins); } void DaysModel::setSourceData(QList *data) { if (m_data != data) { beginResetModel(); m_data = data; endResetModel(); } } int DaysModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) if (m_data->size() <= 0) { return 0; } else { return m_data->size(); } } QVariant DaysModel::data(const QModelIndex &index, int role) const { if (index.isValid()) { const DayData ¤tData = m_data->at(index.row()); const QDate currentDate(currentData.yearNumber, currentData.monthNumber, currentData.dayNumber); switch (role) { case isCurrent: return currentData.isCurrent; case containsEventItems: return m_eventsData.contains(currentDate); case dayNumber: return currentData.dayNumber; case monthNumber: return currentData.monthNumber; case yearNumber: return currentData.yearNumber; } } return QVariant(); } void DaysModel::update() { if (m_data->size() <= 0) { return; } m_eventsData.clear(); const QDate modelFirstDay(m_data->at(0).yearNumber, m_data->at(0).monthNumber, m_data->at(0).dayNumber); if (m_pluginsManager) { Q_FOREACH (CalendarEvents::CalendarEventsPlugin *eventsPlugin, m_pluginsManager->plugins()) { eventsPlugin->loadEventsForDateRange(modelFirstDay, modelFirstDay.addDays(42)); } } // We always have 42 items (or weeks * num of days in week) so we only have to tell the view that the data changed. emit dataChanged(index(0, 0), index(m_data->count() - 1, 0)); } void DaysModel::onDataReady(const QMultiHash &data) { m_eventsData.reserve(m_eventsData.size() + data.size()); m_eventsData += data; if (data.contains(QDate::currentDate())) { m_agendaNeedsUpdate = true; } // only the containsEventItems role may have changed emit dataChanged(index(0, 0), index(m_data->count() - 1, 0), {containsEventItems}); Q_EMIT agendaUpdated(QDate::currentDate()); } void DaysModel::onEventModified(const CalendarEvents::EventData &data) { QList updatesList; auto i = m_eventsData.begin(); while (i != m_eventsData.end()) { if (i->uid() == data.uid()) { *i = data; updatesList << i.key(); } ++i; } if (!updatesList.isEmpty()) { m_agendaNeedsUpdate = true; } Q_FOREACH (const QDate date, updatesList) { const QModelIndex changedIndex = indexForDate(date); if (changedIndex.isValid()) { Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems}); } Q_EMIT agendaUpdated(date); } } void DaysModel::onEventRemoved(const QString &uid) { QList updatesList; auto i = m_eventsData.begin(); while (i != m_eventsData.end()) { if (i->uid() == uid) { updatesList << i.key(); i = m_eventsData.erase(i); } else { ++i; } } if (!updatesList.isEmpty()) { m_agendaNeedsUpdate = true; } Q_FOREACH (const QDate date, updatesList) { const QModelIndex changedIndex = indexForDate(date); if (changedIndex.isValid()) { Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems}); } Q_EMIT agendaUpdated(date); } } QList DaysModel::eventsForDate(const QDate &date) { if (m_lastRequestedAgendaDate == date && !m_agendaNeedsUpdate) { return m_qmlData; } m_lastRequestedAgendaDate = date; qDeleteAll(m_qmlData); m_qmlData.clear(); QList events = m_eventsData.values(date); m_qmlData.reserve(events.size()); // sort events by their time and type std::sort(events.begin(), events.end(), [](const CalendarEvents::EventData &a, const CalendarEvents::EventData &b) { return b.type() > a.type() || b.startDateTime() > a.startDateTime(); }); Q_FOREACH (const CalendarEvents::EventData &event, events) { m_qmlData << new EventDataDecorator(event, this); } m_agendaNeedsUpdate = false; return m_qmlData; } QModelIndex DaysModel::indexForDate(const QDate &date) { if (!m_data) { return QModelIndex(); } const DayData &firstDay = m_data->at(0); const QDate firstDate(firstDay.yearNumber, firstDay.monthNumber, firstDay.dayNumber); qint64 daysTo = firstDate.daysTo(date); return createIndex(daysTo, 0); } void DaysModel::setPluginsManager(QObject *manager) { EventPluginsManager *m = qobject_cast(manager); if (!m) { return; } if (m_pluginsManager != 0) { m_pluginsManager->deleteLater(); m_pluginsManager = 0; } m_pluginsManager = m; connect(m_pluginsManager, &EventPluginsManager::dataReady, this, &DaysModel::onDataReady); connect(m_pluginsManager, &EventPluginsManager::eventModified, this, &DaysModel::onEventModified); connect(m_pluginsManager, &EventPluginsManager::eventRemoved, this, &DaysModel::onEventRemoved); connect(m_pluginsManager, &EventPluginsManager::pluginsChanged, this, &DaysModel::update); QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } QHash DaysModel::roleNames() const { return { {isCurrent, "isCurrent"}, {containsEventItems, "containsEventItems"}, {dayNumber, "dayNumber"}, {monthNumber, "monthNumber"}, {yearNumber, "yearNumber"} }; } diff --git a/src/declarativeimports/calendar/daysmodel.h b/src/declarativeimports/calendar/daysmodel.h index 636beb5a8..3906eb4ae 100644 --- a/src/declarativeimports/calendar/daysmodel.h +++ b/src/declarativeimports/calendar/daysmodel.h @@ -1,83 +1,83 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek 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. */ #ifndef DAYSMODEL_H #define DAYSMODEL_H #include #include "daydata.h" #include class EventPluginsManager; class DaysModel : public QAbstractListModel { Q_OBJECT public: enum Roles { isCurrent = Qt::UserRole + 1, //containsHolidayItems, containsEventItems, //containsTodoItems, //containsJournalItems, dayNumber, monthNumber, yearNumber }; explicit DaysModel(QObject *parent = nullptr); virtual ~DaysModel(); void setSourceData(QList *data); int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; Q_INVOKABLE void setPluginsManager(QObject *manager); Q_INVOKABLE QList eventsForDate(const QDate &date); QHash roleNames() const Q_DECL_OVERRIDE; Q_SIGNALS: void agendaUpdated(const QDate &updatedDate); public Q_SLOTS: void update(); private Q_SLOTS: void onDataReady(const QMultiHash &data); void onEventModified(const CalendarEvents::EventData &data); void onEventRemoved(const QString &uid); private: QModelIndex indexForDate(const QDate &date); - EventPluginsManager *m_pluginsManager; - QList *m_data; + EventPluginsManager *m_pluginsManager = nullptr; + QList *m_data = nullptr; QList m_qmlData; QDate m_lastRequestedAgendaDate; QList m_eventPlugins; QMultiHash m_eventsData; QDate m_lastRequestedEventsStartDate; // this is always this+42 days bool m_agendaNeedsUpdate; }; #endif // DAYSMODEL_H diff --git a/src/declarativeimports/calendar/eventpluginsmanager.h b/src/declarativeimports/calendar/eventpluginsmanager.h index e414e1869..1642e61db 100644 --- a/src/declarativeimports/calendar/eventpluginsmanager.h +++ b/src/declarativeimports/calendar/eventpluginsmanager.h @@ -1,83 +1,83 @@ /* Copyright (C) 2015 Martin Klapetek 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. */ #ifndef EVENTPLUGINSMANAGER_H #define EVENTPLUGINSMANAGER_H #include #include #include namespace CalendarEvents { class CalendarEventsPlugin; class EventData; } class EventPluginsModel; class QAbstractListModel; class EventPluginsManager : public QObject { Q_OBJECT Q_PROPERTY(QAbstractListModel *model READ pluginsModel NOTIFY pluginsChanged) Q_PROPERTY(QStringList enabledPlugins READ enabledPlugins WRITE setEnabledPlugins NOTIFY pluginsChanged) public: explicit EventPluginsManager(QObject *parent = nullptr); ~EventPluginsManager(); QList plugins() const; QAbstractListModel* pluginsModel() const; // This is a helper function to set which plugins // are enabled without needing to go through setEnabledPlugins // which also loads the plugins; from the Applet config // the plugins are not required to be actually loaded Q_INVOKABLE void populateEnabledPluginsList(const QStringList &pluginsList); void setEnabledPlugins(QStringList &pluginsList); QStringList enabledPlugins() const; Q_SIGNALS: void pluginsChanged(); // These three signals below are used for relaying the // plugin signals so that the EventPluginsManager don't // have to worry about connecting to newly loaded plugins void dataReady(const QMultiHash &data); void eventModified(const CalendarEvents::EventData &modifiedEvent); void eventRemoved(const QString &uid); private: void loadPlugin(const QString &absolutePath); friend class EventPluginsModel; - EventPluginsModel *m_model; + EventPluginsModel *m_model = nullptr; QList m_plugins; struct PluginData { QString name; QString desc; QString icon; QString configUi; }; QMap m_availablePlugins; QStringList m_enabledPlugins; }; #endif diff --git a/src/declarativeimports/core/colorscope.cpp b/src/declarativeimports/core/colorscope.cpp index f6cefcce0..b46f2e3da 100644 --- a/src/declarativeimports/core/colorscope.cpp +++ b/src/declarativeimports/core/colorscope.cpp @@ -1,215 +1,215 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2011 Artur Duque de Souza * * Copyright 2013 Sebastian Kügler * * * * 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 "colorscope.h" #include #include #include QHash ColorScope::s_attachedScopes = QHash(); ColorScope::ColorScope(QQuickItem *parent, QObject *parentObject) : QQuickItem(parent), m_inherit(false), m_group(Plasma::Theme::NormalColorGroup), m_parent(parentObject) { connect(&m_theme, &Plasma::Theme::themeChanged, this, &ColorScope::colorsChanged); connect(this, &ColorScope::colorGroupChanged, this, &ColorScope::colorsChanged); QQuickItem *parentItem = qobject_cast(parentObject); if (parentItem) { connect(parentItem, &QQuickItem::parentChanged, this, &ColorScope::checkColorGroupChanged); } else if (m_parent) { m_parent->installEventFilter(this); } } ColorScope::~ColorScope() { s_attachedScopes.remove(m_parent); } ColorScope *ColorScope::qmlAttachedProperties(QObject *object) { const auto cs = s_attachedScopes.value(object); if (cs) { return cs; } ColorScope *s = new ColorScope(0, object); s_attachedScopes[object] = s; s->m_inherit = true; s->setParent(object); s->checkColorGroupChanged(); return s; } bool ColorScope::eventFilter(QObject* watched, QEvent* event) { Q_ASSERT(watched == m_parent && !qobject_cast(watched)); if (event->type() == QEvent::ParentChange) { checkColorGroupChanged(); } return QQuickItem::eventFilter(watched, event); } void ColorScope::setParentScope(ColorScope* parentScope) { if (parentScope == m_parentScope) return; if (m_parentScope) { disconnect(m_parentScope.data(), &ColorScope::colorGroupChanged, this, &ColorScope::checkColorGroupChanged); } m_parentScope = parentScope; if (parentScope) { connect(parentScope, &ColorScope::colorGroupChanged, this, &ColorScope::checkColorGroupChanged); } } ColorScope *ColorScope::findParentScope() { - QObject *p = 0; + QObject *p = nullptr; if (m_parent) { QQuickItem *gp = qobject_cast(m_parent); if (gp) { p = gp->parentItem(); } else { p = m_parent->parent(); } } if (!p || !m_parent) { setParentScope(nullptr); - return 0; + return nullptr; } ColorScope *c = qobject_cast(p); if (!c) { c = qmlAttachedProperties(p); } setParentScope(c); return m_parentScope; } void ColorScope::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_group == group) { return; } m_group = group; checkColorGroupChanged(); } Plasma::Theme::ColorGroup ColorScope::colorGroup() const { return m_actualGroup; } QColor ColorScope::textColor() const { return m_theme.color(Plasma::Theme::TextColor, colorGroup()); } QColor ColorScope::highlightColor() const { return m_theme.color(Plasma::Theme::HighlightColor, colorGroup()); } QColor ColorScope::highlightedTextColor() const { return m_theme.color(Plasma::Theme::HighlightedTextColor, colorGroup()); } QColor ColorScope::backgroundColor() const { return m_theme.color(Plasma::Theme::BackgroundColor, colorGroup()); } QColor ColorScope::positiveTextColor() const { return m_theme.color(Plasma::Theme::PositiveTextColor, colorGroup()); } QColor ColorScope::neutralTextColor() const { return m_theme.color(Plasma::Theme::NeutralTextColor, colorGroup()); } QColor ColorScope::negativeTextColor() const { return m_theme.color(Plasma::Theme::NegativeTextColor, colorGroup()); } bool ColorScope::inherit() const { return m_inherit; } void ColorScope::setInherit(bool inherit) { if (m_inherit == inherit) { return; } m_inherit = inherit; emit inheritChanged(); checkColorGroupChanged(); } void ColorScope::itemChange(ItemChange change, const ItemChangeData &value) { if (change == QQuickItem::ItemSceneChange) { //we have a window: create the representations if needed if (value.window) { checkColorGroupChanged(); } } QQuickItem::itemChange(change, value); } void ColorScope::checkColorGroupChanged() { const auto last = m_actualGroup; if (m_inherit) { findParentScope(); m_actualGroup = m_parentScope ? m_parentScope->colorGroup() : m_group; } else { m_actualGroup = m_group; } if (m_actualGroup != last) { Q_EMIT colorGroupChanged(); } } #include "moc_colorscope.cpp" diff --git a/src/declarativeimports/core/datamodel.cpp b/src/declarativeimports/core/datamodel.cpp index 224e19460..7664cb747 100644 --- a/src/declarativeimports/core/datamodel.cpp +++ b/src/declarativeimports/core/datamodel.cpp @@ -1,564 +1,564 @@ /* * Copyright 2010 by Marco Martin * This program 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, 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 Library 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 "datamodel.h" #include "datasource.h" #include #include #include namespace Plasma { SortFilterModel::SortFilterModel(QObject *parent) : QSortFilterProxyModel(parent) { setObjectName(QStringLiteral("SortFilterModel")); setDynamicSortFilter(true); connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged())); connect(this, SIGNAL(countChanged()), this, SLOT(syncRoleNames())); } SortFilterModel::~SortFilterModel() { } void SortFilterModel::syncRoleNames() { if (!sourceModel()) { return; } m_roleIds.clear(); const QHash rNames = roleNames(); m_roleIds.reserve(rNames.count()); for (auto i = rNames.constBegin(); i != rNames.constEnd(); ++i) { m_roleIds[QString::fromUtf8(i.value())] = i.key(); } setRoleNames(sourceModel()->roleNames()); setFilterRole(m_filterRole); setSortRole(m_sortRole); } int SortFilterModel::roleNameToId(const QString &name) const { return m_roleIds.value(name, Qt::DisplayRole); } void SortFilterModel::setModel(QAbstractItemModel *model) { if (model == sourceModel()) { return; } if (sourceModel()) { disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(syncRoleNames())); } QSortFilterProxyModel::setSourceModel(model); if (model) { connect(model, SIGNAL(modelReset()), this, SLOT(syncRoleNames())); syncRoleNames(); } emit sourceModelChanged(model); } bool SortFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { if (m_filterCallback.isCallable()) { QJSValueList args; args << QJSValue(source_row); const QModelIndex idx = sourceModel()->index(source_row, filterKeyColumn(), source_parent); QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine(); args << engine->toScriptValue(idx.data(m_roleIds.value(m_filterRole))); return const_cast(this)->m_filterCallback.call(args).toBool(); } return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } void SortFilterModel::setFilterRegExp(const QString &exp) { if (exp == filterRegExp()) { return; } QSortFilterProxyModel::setFilterRegExp(QRegExp(exp, Qt::CaseInsensitive)); Q_EMIT filterRegExpChanged(exp); } QString SortFilterModel::filterRegExp() const { return QSortFilterProxyModel::filterRegExp().pattern(); } void SortFilterModel::setFilterString(const QString &filterString) { if (filterString == m_filterString) { return; } m_filterString = filterString; QSortFilterProxyModel::setFilterFixedString(filterString); Q_EMIT filterStringChanged(filterString); } QString SortFilterModel::filterString() const { return m_filterString; } QJSValue SortFilterModel::filterCallback() const { return m_filterCallback; } void SortFilterModel::setFilterCallback(const QJSValue& callback) { if (m_filterCallback.strictlyEquals(callback)) { return; } if (!callback.isNull() && !callback.isCallable()) { return; } m_filterCallback = callback; invalidateFilter(); - emit filterCallbackChanged(callback); + Q_EMIT filterCallbackChanged(callback); } void SortFilterModel::setFilterRole(const QString &role) { QSortFilterProxyModel::setFilterRole(roleNameToId(role)); m_filterRole = role; } QString SortFilterModel::filterRole() const { return m_filterRole; } void SortFilterModel::setSortRole(const QString &role) { m_sortRole = role; if (role.isEmpty()) { sort(-1, Qt::AscendingOrder); } else if (sourceModel()) { QSortFilterProxyModel::setSortRole(roleNameToId(role)); sort(0, sortOrder()); } } QString SortFilterModel::sortRole() const { return m_sortRole; } void SortFilterModel::setSortOrder(const Qt::SortOrder order) { sort(0, order); } QVariantMap SortFilterModel::get(int row) const { QModelIndex idx = index(row, 0); QVariantMap hash; const QHash rNames = roleNames(); for (auto i = rNames.begin(); i != rNames.end(); ++i) { hash[QString::fromUtf8(i.value())] = data(idx, i.key()); } return hash; } int SortFilterModel::mapRowToSource(int row) const { QModelIndex idx = index(row, 0); return mapToSource(idx).row(); } int SortFilterModel::mapRowFromSource(int row) const { if (!sourceModel()) { qWarning() << "No source model defined!"; return -1; } QModelIndex idx = sourceModel()->index(row, 0); return mapFromSource(idx).row(); } DataModel::DataModel(QObject *parent) : QAbstractItemModel(parent), m_dataSource(0), m_maxRoleId(Qt::UserRole + 1) { //There is one reserved role name: DataEngineSource m_roleNames[m_maxRoleId] = QByteArrayLiteral("DataEngineSource"); m_roleIds[QStringLiteral("DataEngineSource")] = m_maxRoleId; ++m_maxRoleId; setObjectName(QStringLiteral("DataModel")); connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged())); } DataModel::~DataModel() { } void DataModel::dataUpdated(const QString &sourceName, const QVariantMap &data) { if (!m_sourceFilter.isEmpty() && m_sourceFilterRE.isValid() && !m_sourceFilterRE.exactMatch(sourceName)) { return; } if (m_keyRoleFilter.isEmpty()) { //an item is represented by a source: keys are roles m_roleLevel == FirstLevel QVariantList list; if (!m_dataSource->data()->isEmpty()) { foreach (const QString &key, m_dataSource->data()->keys()) { if (!m_sourceFilter.isEmpty() && m_sourceFilterRE.isValid() && !m_sourceFilterRE.exactMatch(key)) { continue; } QVariant value = m_dataSource->data()->value(key); if (value.isValid() && value.canConvert()) { Plasma::DataEngine::Data data = value.value(); data[QStringLiteral("DataEngineSource")] = key; list.append(data); } } } setItems(QString(), list); } else { //a key that matches the one we want exists and is a list of DataEngine::Data if (data.contains(m_keyRoleFilter) && data.value(m_keyRoleFilter).canConvert()) { setItems(sourceName, data.value(m_keyRoleFilter).value()); } else if (m_keyRoleFilterRE.isValid()) { //try to match the key we want with a regular expression if set QVariantList list; QVariantMap::const_iterator i; for (i = data.constBegin(); i != data.constEnd(); ++i) { if (m_keyRoleFilterRE.exactMatch(i.key())) { list.append(i.value()); } } setItems(sourceName, list); } } } void DataModel::setDataSource(QObject *object) { DataSource *source = qobject_cast(object); if (!source) { qWarning() << "Error: DataSource type expected"; return; } if (m_dataSource == source) { return; } if (m_dataSource) { disconnect(m_dataSource, 0, this, 0); } m_dataSource = source; foreach (const QString &key, m_dataSource->data()->keys()) { dataUpdated(key, m_dataSource->data()->value(key).value()); } connect(m_dataSource, &DataSource::newData, this, &DataModel::dataUpdated); connect(m_dataSource, &DataSource::sourceRemoved, this, &DataModel::removeSource); connect(m_dataSource, &DataSource::sourceDisconnected, this, &DataModel::removeSource); } QObject *DataModel::dataSource() const { return m_dataSource; } void DataModel::setKeyRoleFilter(const QString &key) { // the "key role filter" can be used in one of three ways: // // 1) empty string -> all data is used, each source is one row in the model // 2) matches a key in the data exactly -> only that key/value pair is used, and the value is // treated as a collection where each item in the collection becomes a row in the model // 3) regular expression -> matches zero or more keys in the data, and each matching key/value // pair becomes a row in the model if (m_keyRoleFilter == key) { return; } m_keyRoleFilter = key; m_keyRoleFilterRE = QRegExp(m_keyRoleFilter); } QString DataModel::keyRoleFilter() const { return m_keyRoleFilter; } void DataModel::setSourceFilter(const QString &key) { if (m_sourceFilter == key) { return; } m_sourceFilter = key; m_sourceFilterRE = QRegExp(key); /* FIXME: if the user changes the source filter, it won't immediately be reflected in the available data if (m_sourceFilterRE.isValid()) { .. iterate through all items and weed out the ones that don't match .. } */ } QString DataModel::sourceFilter() const { return m_sourceFilter; } void DataModel::setItems(const QString &sourceName, const QVariantList &list) { const int oldLength = m_items.value(sourceName).count(); const int delta = list.length() - oldLength; const bool firstRun = m_items.isEmpty(); //At what row number the first item associated to this source starts int sourceIndex = 0; QMap >::const_iterator i; for (i = m_items.constBegin(); i != m_items.constEnd(); ++i) { if (i.key() == sourceName) { break; } sourceIndex += i.value().count(); } //signal as inserted the rows at the end, all the other rows will signal a dataupdated. //better than a model reset because doesn't cause deletion and re-creation of every list item on a qml ListView, repeaters etc. //the first run it gets reset because otherwise setRoleNames gets broken if (firstRun) { beginResetModel(); } else if (delta > 0) { beginInsertRows(QModelIndex(), sourceIndex + oldLength, sourceIndex + list.length() - 1); } else if (delta < 0) { beginRemoveRows(QModelIndex(), sourceIndex + list.length(), sourceIndex + oldLength - 1); } //convert to vector, so data() will be O(1) m_items[sourceName] = list.toVector(); if (!list.isEmpty()) { if (list.first().canConvert()) { foreach (const QVariant &item, list) { const QVariantMap &vh = item.value(); QMapIterator it(vh); while (it.hasNext()) { it.next(); const QString &roleName = it.key(); if (!m_roleIds.contains(roleName)) { ++m_maxRoleId; m_roleNames[m_maxRoleId] = roleName.toLatin1(); m_roleIds[roleName] = m_maxRoleId; } } } } else { foreach (const QVariant &item, list) { const QVariantMap &vh = item.value(); QMapIterator it(vh); while (it.hasNext()) { it.next(); const QString &roleName = it.key(); if (!m_roleIds.contains(roleName)) { ++m_maxRoleId; m_roleNames[m_maxRoleId] = roleName.toLatin1(); m_roleIds[roleName] = m_maxRoleId; } } } } } setRoleNames(m_roleNames); if (firstRun) { endResetModel(); } else if (delta > 0) { endInsertRows(); } else if (delta < 0) { endRemoveRows(); } emit dataChanged(createIndex(sourceIndex, 0), createIndex(sourceIndex + qMin(list.length(), oldLength), 0)); } void DataModel::removeSource(const QString &sourceName) { //FIXME: find a way to remove only the proper things also in the case where sources are items if (m_keyRoleFilter.isEmpty()) { //source name in the map, linear scan for (int i = 0; i < m_items.value(QString()).count(); ++i) { if (m_items.value(QString())[i].value().value(QStringLiteral("DataEngineSource")) == sourceName) { beginRemoveRows(QModelIndex(), i, i); m_items[QString()].remove(i); endRemoveRows(); break; } } } else { if (m_items.contains(sourceName)) { //At what row number the first item associated to this source starts int sourceIndex = 0; for (auto i = m_items.constBegin(); i != m_items.constEnd(); ++i) { if (i.key() == sourceName) { break; } sourceIndex += i.value().count(); } //source name as key of the map int count = m_items.value(sourceName).count(); if (count > 0) { beginRemoveRows(QModelIndex(), sourceIndex, sourceIndex + count - 1); } m_items.remove(sourceName); if (count > 0) { endRemoveRows(); } } } } QVariant DataModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.column() > 0 || index.row() < 0 || index.row() >= countItems()) { return QVariant(); } int count = 0; int actualRow = 0; QString source; QMap >::const_iterator i; for (i = m_items.constBegin(); i != m_items.constEnd(); ++i) { const int oldCount = count; count += i.value().count(); if (index.row() < count) { source = i.key(); actualRow = index.row() - oldCount; break; } } //is it the reserved role: DataEngineSource ? //also, if each source is an item DataEngineSource is a role between all the others, otherwise we know it from the role variable if (!m_keyRoleFilter.isEmpty() && m_roleNames.value(role) == "DataEngineSource") { return source; } else { return m_items.value(source).value(actualRow).value().value(QString::fromUtf8(m_roleNames.value(role))); } } QVariant DataModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section) Q_UNUSED(orientation) Q_UNUSED(role) return QVariant(); } QModelIndex DataModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() || column > 0 || row < 0 || row >= countItems()) { return QModelIndex(); } return createIndex(row, column); } QModelIndex DataModel::parent(const QModelIndex &child) const { Q_UNUSED(child) return QModelIndex(); } int DataModel::rowCount(const QModelIndex &parent) const { //this is not a tree //TODO: make it possible some day? if (parent.isValid()) { return 0; } return countItems(); } int DataModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 1; } QVariantMap DataModel::get(int row) const { QModelIndex idx = index(row, 0); QVariantMap map; const QHash rNames = roleNames(); for (auto i = rNames.constBegin(); i != rNames.constEnd(); ++i) { map[QString::fromUtf8(i.value())] = data(idx, i.key()); } return map; } } diff --git a/src/declarativeimports/core/datasource.cpp b/src/declarativeimports/core/datasource.cpp index e2ac96dfe..52d842284 100644 --- a/src/declarativeimports/core/datasource.cpp +++ b/src/declarativeimports/core/datasource.cpp @@ -1,270 +1,268 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco Martin * Copyright 2013 by Sebastian Kügler * * This program 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, 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 Library 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 "datasource.h" namespace Plasma { DataSource::DataSource(QObject *parent) : QObject(parent), m_ready(false), m_interval(0), - m_intervalAlignment(Plasma::Types::NoAlignment), - m_dataEngine(0), - m_dataEngineConsumer(0) + m_intervalAlignment(Plasma::Types::NoAlignment) { m_models = new QQmlPropertyMap(this); m_data = new QQmlPropertyMap(this); setObjectName(QStringLiteral("DataSource")); } void DataSource::classBegin() { } void DataSource::componentComplete() { m_ready = true; setupData(); } void DataSource::setConnectedSources(const QStringList &sources) { bool sourcesChanged = false; foreach (const QString &source, sources) { if (!m_connectedSources.contains(source)) { sourcesChanged = true; if (m_dataEngine) { m_connectedSources.append(source); m_dataEngine->connectSource(source, this, m_interval, m_intervalAlignment); emit sourceConnected(source); } } } foreach (const QString &source, m_connectedSources) { if (!sources.contains(source)) { m_data->clear(source); sourcesChanged = true; if (m_dataEngine) { m_dataEngine->disconnectSource(source, this); emit sourceDisconnected(source); } } } if (sourcesChanged) { m_connectedSources = sources; emit connectedSourcesChanged(); } } void DataSource::setEngine(const QString &e) { if (e == m_engine) { return; } m_engine = e; if (m_engine.isEmpty()) { emit engineChanged(); return; } m_dataEngineConsumer = new Plasma::DataEngineConsumer(); Plasma::DataEngine *engine = dataEngine(m_engine); if (!engine) { qWarning() << "DataEngine" << m_engine << "not found"; emit engineChanged(); return; } if (m_dataEngine) { m_dataEngine->disconnect(this); // Deleting the consumer triggers the reference counting delete m_dataEngineConsumer; - m_dataEngineConsumer = 0; + m_dataEngineConsumer = nullptr; } /* * It is due little explanation why this is a queued connection: * If sourceAdded arrives immediately, in the case we have a datamodel * with items at source level we connect too soon (before setData for * all roles is done), having a dataupdated in the datamodel with only * the first role, killing off the other roles. * Besides causing a model reset more, unfortunately setRoleNames can be done a single time, so is not possible adding new roles after the * first setRoleNames() is called. * This fixes engines that have 1 item per source like the * recommendations engine. */ m_dataEngine = engine; connect(m_dataEngine, SIGNAL(sourceAdded(QString)), this, SLOT(updateSources()), Qt::QueuedConnection); connect(m_dataEngine, SIGNAL(sourceRemoved(QString)), this, SLOT(updateSources())); connect(m_dataEngine, SIGNAL(sourceAdded(QString)), this, SIGNAL(sourceAdded(QString)), Qt::QueuedConnection); connect(m_dataEngine, SIGNAL(sourceRemoved(QString)), this, SLOT(removeSource(QString))); connect(m_dataEngine, SIGNAL(sourceRemoved(QString)), this, SIGNAL(sourceRemoved(QString))); updateSources(); emit engineChanged(); } void DataSource::setInterval(const int interval) { if (interval == m_interval) { return; } m_interval = interval; setupData(); emit intervalChanged(); } void DataSource::setIntervalAlignment(Plasma::Types::IntervalAlignment intervalAlignment) { if (intervalAlignment == m_intervalAlignment) { return; } m_intervalAlignment = intervalAlignment; setupData(); emit intervalAlignmentChanged(); } void DataSource::setupData() { if (!m_ready) { return; } // qDebug() << " loading engine " << m_engine; //FIXME: should all services be deleted just because we're changing the interval, etc? qDeleteAll(m_services); m_services.clear(); foreach (const QString &source, m_connectedSources) { m_dataEngine->connectSource(source, this, m_interval, m_intervalAlignment); emit sourceConnected(source); } } void DataSource::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data) { //it can arrive also data we don't explicitly connected a source if (m_connectedSources.contains(sourceName)) { m_data->insert(sourceName, data); emit dataChanged(); emit newData(sourceName, data); } else if (m_dataEngine) { m_dataEngine->disconnectSource(sourceName, this); } } void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *model) { if (!model) { m_models->clear(sourceName); return; } m_models->insert(sourceName, QVariant::fromValue(model)); //FIXME: this will break in the case a second model is set connect(model, &QObject::destroyed, m_models, [ = ]() { m_models->clear(sourceName); }); } void DataSource::removeSource(const QString &source) { m_data->clear(source); m_models->clear(source); //TODO: emit those signals as last thing if (m_connectedSources.contains(source)) { m_connectedSources.removeAll(source); emit sourceDisconnected(source); emit connectedSourcesChanged(); } if (m_dataEngine) { QHash::iterator it = m_services.find(source); if (it != m_services.end()) { delete it.value(); m_services.erase(it); } } } QObject *DataSource::serviceForSource(const QString &source) { if (!m_services.contains(source)) { Plasma::Service *service = m_dataEngine->serviceForSource(source); if (!service) { - return 0; + return nullptr; } m_services[source] = service; } return m_services.value(source); } void DataSource::connectSource(const QString &source) { if (m_connectedSources.contains(source)) { return; } m_connectedSources.append(source); if (m_dataEngine) { m_dataEngine->connectSource(source, this, m_interval, m_intervalAlignment); emit sourceConnected(source); emit connectedSourcesChanged(); } } void DataSource::disconnectSource(const QString &source) { if (m_dataEngine && m_connectedSources.contains(source)) { m_connectedSources.removeAll(source); m_dataEngine->disconnectSource(source, this); emit sourceDisconnected(source); emit connectedSourcesChanged(); } } void DataSource::updateSources() { QStringList sources; if (m_dataEngine) { sources = m_dataEngine->sources(); } if (sources != m_sources) { m_sources = sources; emit sourcesChanged(); } } } diff --git a/src/declarativeimports/core/datasource.h b/src/declarativeimports/core/datasource.h index dedbc43e1..42b662a54 100644 --- a/src/declarativeimports/core/datasource.h +++ b/src/declarativeimports/core/datasource.h @@ -1,201 +1,201 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco MArtin * Copyright 2013 by Sebastian Kügler * * This program 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, 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 Library 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. */ #ifndef DATASOURCE_H #define DATASOURCE_H #include #include #include #include #include #include class QQmlPropertyMap; namespace Plasma { class DataEngine; /** * @class DataSource * @short Provides data from a range of plugins */ class DataSource : public QObject, public QQmlParserStatus, DataEngineConsumer { Q_OBJECT Q_INTERFACES(QQmlParserStatus) public: enum Change { NoChange = 0, DataEngineChanged = 1, SourcesChanged = 2 }; Q_DECLARE_FLAGS(Changes, Change) typedef QMap Data; explicit DataSource(QObject *parent = nullptr); void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; /** * true if the connection to the Plasma DataEngine is valid */ Q_PROPERTY(bool valid READ valid) bool valid() const { return m_dataEngine && m_dataEngine->isValid(); } /** * Polling interval in milliseconds when the data will be fetched again. If 0 no polling will be done. */ Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) int interval() const { return m_interval; } void setInterval(const int interval); /** * The interval to align polling to */ Q_PROPERTY(Plasma::Types::IntervalAlignment intervalAlignment READ intervalAlignment WRITE setIntervalAlignment NOTIFY intervalAlignmentChanged) Plasma::Types::IntervalAlignment intervalAlignment() const { return m_intervalAlignment; } void setIntervalAlignment(Plasma::Types::IntervalAlignment intervalAlignment); /** * Plugin name of the Plasma DataEngine */ Q_PROPERTY(QString dataEngine READ engine WRITE setEngine NOTIFY engineChanged) Q_PROPERTY(QString engine READ engine WRITE setEngine NOTIFY engineChanged) QString engine() const { return m_engine; } void setEngine(const QString &e); /** * String array of all the source names connected to the DataEngine */ Q_PROPERTY(QStringList connectedSources READ connectedSources WRITE setConnectedSources NOTIFY connectedSourcesChanged) QStringList connectedSources() const { return m_connectedSources; } void setConnectedSources(const QStringList &s); /** * Read only string array of all the sources available from the DataEngine (connected or not) */ Q_PROPERTY(QStringList sources READ sources NOTIFY sourcesChanged) QStringList sources() const { return m_sources; } /** * All the data fetched by this dataengine. * This is a map of maps. At the first level, there are the source names, at the second, they keys set by the DataEngine */ Q_PROPERTY(QQmlPropertyMap *data READ data CONSTANT) QQmlPropertyMap *data() const { return m_data; } /** * All the models associated to this DataEngine, indexed by source. * In order for a model to be present, besides being implemented in the DataEngine, * The user has to be connected to its source, so the source name has to be present in the connectedSources property. */ Q_PROPERTY(QQmlPropertyMap *models READ models CONSTANT) QQmlPropertyMap *models() const { return m_models; } /** * @returns a Plasma::Service given a source name * @param source source name we want a service of */ Q_INVOKABLE QObject *serviceForSource(const QString &source); /** * Connect a new source. It adds it to connectedSources */ Q_INVOKABLE void connectSource(const QString &source); /** * Disconnects from a DataEngine Source. It also removes it from connectedSources */ Q_INVOKABLE void disconnectSource(const QString &source); public Q_SLOTS: void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); void modelChanged(const QString &sourceName, QAbstractItemModel *model); protected Q_SLOTS: void removeSource(const QString &source); void setupData(); void updateSources(); Q_SIGNALS: void newData(const QString &sourceName, const QVariantMap &data); void sourceAdded(const QString &source); void sourceRemoved(const QString &source); void sourceConnected(const QString &source); void sourceDisconnected(const QString &source); void intervalChanged(); void intervalAlignmentChanged(); void engineChanged(); void dataChanged(); void connectedSourcesChanged(); void sourcesChanged(); private: bool m_ready; QString m_id; int m_interval; Plasma::Types::IntervalAlignment m_intervalAlignment; QString m_engine; - QQmlPropertyMap *m_data; - QQmlPropertyMap *m_models; - Plasma::DataEngine *m_dataEngine; - Plasma::DataEngineConsumer *m_dataEngineConsumer; + QQmlPropertyMap *m_data = nullptr; + QQmlPropertyMap *m_models = nullptr; + Plasma::DataEngine *m_dataEngine = nullptr; + Plasma::DataEngineConsumer *m_dataEngineConsumer = nullptr; QStringList m_sources; QStringList m_connectedSources; QStringList m_oldSources; QStringList m_newSources; Changes m_changes; QHash m_services; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DataSource::Changes) } #endif diff --git a/src/declarativeimports/core/fadingnode.cpp b/src/declarativeimports/core/fadingnode.cpp index 6347dbdba..9c2c93d4f 100644 --- a/src/declarativeimports/core/fadingnode.cpp +++ b/src/declarativeimports/core/fadingnode.cpp @@ -1,127 +1,127 @@ /* * Copyright (C) 2014 David Edmundson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "fadingnode_p.h" #include #include #include struct FadingMaterialState { - QSGTexture *source; - QSGTexture *target; + QSGTexture *source = nullptr; + QSGTexture *target = nullptr; qreal progress; }; class FadingMaterialShader : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(FadingMaterialShader, FadingMaterialState) public: FadingMaterialShader(); using QSGSimpleMaterialShader::updateState; virtual void updateState(const FadingMaterialState* newState, const FadingMaterialState* oldState) override; QList attributes() const Q_DECL_OVERRIDE; void initialize() Q_DECL_OVERRIDE; private: - QOpenGLFunctions *glFuncs = 0; + QOpenGLFunctions *glFuncs = nullptr; int m_progressId = 0; }; FadingMaterialShader::FadingMaterialShader() { setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/plasma-framework/shaders/fadingmaterial.frag")); setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/plasma-framework/shaders/fadingmaterial.vert")); } QList FadingMaterialShader::attributes() const { return {QByteArrayLiteral("qt_Vertex"), QByteArrayLiteral("qt_MultiTexCoord0")}; } void FadingMaterialShader::updateState(const FadingMaterialState* newState, const FadingMaterialState* oldState) { if (!oldState || oldState->source != newState->source) { glFuncs->glActiveTexture(GL_TEXTURE1); newState->source->bind(); // reset the active texture back to 0 after we changed it to something else glFuncs->glActiveTexture(GL_TEXTURE0); } if (!oldState || oldState->target != newState->target) { glFuncs->glActiveTexture(GL_TEXTURE0); newState->target->bind(); } if (!oldState || oldState->progress != newState->progress) { program()->setUniformValue(m_progressId, (GLfloat) newState->progress); } } void FadingMaterialShader::initialize() { if (!program()->isLinked()) { // shader not linked, exit otherwise we crash, BUG: 336272 return; } QSGSimpleMaterialShader< FadingMaterialState >::initialize(); glFuncs = QOpenGLContext::currentContext()->functions(); program()->bind(); program()->setUniformValue("u_src", 0); program()->setUniformValue("u_target", 1); m_progressId = program()->uniformLocation("u_transitionProgress"); } FadingNode::FadingNode(QSGTexture *source, QSGTexture *target): m_source(source), m_target(target) { QSGSimpleMaterial *m = FadingMaterialShader::createMaterial(); m->setFlag(QSGMaterial::Blending); setMaterial(m); setFlag(OwnsMaterial, true); setProgress(1.0); QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); QSGGeometry::updateTexturedRectGeometry(g, QRect(), QRect()); setGeometry(g); setFlag(QSGNode::OwnsGeometry, true); } FadingNode::~FadingNode() { } void FadingNode::setRect(const QRectF &bounds) { QSGGeometry::updateTexturedRectGeometry(geometry(), bounds, QRectF(0, 0, 1, 1)); markDirty(QSGNode::DirtyGeometry); } void FadingNode::setProgress(qreal progress) { QSGSimpleMaterial *m = static_cast*>(material()); m->state()->source = m_source.data(); m->state()->target = m_target.data(); m->state()->progress = progress; markDirty(QSGNode::DirtyMaterial); } diff --git a/src/declarativeimports/core/tooltip.cpp b/src/declarativeimports/core/tooltip.cpp index 7ebe39c54..735f456ea 100644 --- a/src/declarativeimports/core/tooltip.cpp +++ b/src/declarativeimports/core/tooltip.cpp @@ -1,358 +1,358 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2011 Artur Duque de Souza * * Copyright 2013 Sebastian Kügler * * * * 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 "tooltip.h" #include "tooltipdialog.h" #include #include #include #include "framesvgitem.h" #include #include -ToolTipDialog *ToolTip::s_dialog = 0; +ToolTipDialog *ToolTip::s_dialog = nullptr; int ToolTip::s_dialogUsers = 0; ToolTip::ToolTip(QQuickItem *parent) : QQuickItem(parent), m_tooltipsEnabledGlobally(false), m_containsMouse(false), m_location(Plasma::Types::Floating), m_textFormat(Qt::AutoText), m_active(true), m_interactive(false), m_usingDialog(false) { setAcceptHoverEvents(true); setFiltersChildMouseEvents(true); m_showTimer = new QTimer(this); m_showTimer->setSingleShot(true); connect(m_showTimer, &QTimer::timeout, this, &ToolTip::showToolTip); loadSettings(); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/plasmarc"); KDirWatch::self()->addFile(configFile); QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), this, SLOT(settingsChanged())); QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(settingsChanged())); } ToolTip::~ToolTip() { if (s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } if (m_usingDialog) { --s_dialogUsers; } if (s_dialogUsers == 0) { delete s_dialog; - s_dialog = 0; + s_dialog = nullptr; } } void ToolTip::settingsChanged() { KSharedConfig::openConfig(QStringLiteral("plasmarc"))->reparseConfiguration(); loadSettings(); } void ToolTip::loadSettings() { KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "PlasmaToolTips"); m_interval = cfg.readEntry("Delay", 700); m_tooltipsEnabledGlobally = (m_interval > 0); } QQuickItem *ToolTip::mainItem() const { return m_mainItem.data(); } ToolTipDialog *ToolTip::tooltipDialogInstance() { if (!s_dialog) { s_dialog = new ToolTipDialog; s_dialogUsers = 1; } if (!m_usingDialog) { s_dialogUsers++; m_usingDialog = true; } return s_dialog; } void ToolTip::setMainItem(QQuickItem *mainItem) { if (m_mainItem.data() != mainItem) { m_mainItem = mainItem; emit mainItemChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } } void ToolTip::showToolTip() { if (!m_active) { return; } ToolTipDialog *dlg = tooltipDialogInstance(); if (!mainItem()) { setMainItem(dlg->loadDefaultItem()); } // Unset the dialog's old contents before reparenting the dialog. dlg->setMainItem(nullptr); Plasma::Types::Location location = m_location; if (m_location == Plasma::Types::Floating) { QQuickItem *p = parentItem(); while (p) { if (p->property("location").isValid()) { location = (Plasma::Types::Location)p->property("location").toInt(); break; } p = p->parentItem(); } } if (mainItem()) { mainItem()->setProperty("toolTip", QVariant::fromValue(this)); mainItem()->setVisible(true); } dlg->setOwner(this); dlg->setLocation(location); dlg->setVisualParent(this); dlg->setMainItem(mainItem()); dlg->setInteractive(m_interactive); dlg->setVisible(true); } QString ToolTip::mainText() const { return m_mainText; } void ToolTip::setMainText(const QString &mainText) { if (mainText == m_mainText) { return; } m_mainText = mainText; emit mainTextChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } QString ToolTip::subText() const { return m_subText; } void ToolTip::setSubText(const QString &subText) { if (subText == m_subText) { return; } m_subText = subText; emit subTextChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } int ToolTip::textFormat() const { return m_textFormat; } void ToolTip::setTextFormat(int format) { if (m_textFormat == format) { return; } m_textFormat = format; emit textFormatChanged(); } Plasma::Types::Location ToolTip::location() const { return m_location; } void ToolTip::setLocation(Plasma::Types::Location location) { if (m_location == location) { return; } m_location = location; emit locationChanged(); } void ToolTip::setActive(bool active) { if (m_active == active) { return; } m_active = active; if (!active) { tooltipDialogInstance()->dismiss(); } emit activeChanged(); } void ToolTip::setInteractive(bool interactive) { if (m_interactive == interactive) { return; } m_interactive = interactive; emit interactiveChanged(); } void ToolTip::hideToolTip() { m_showTimer->stop(); tooltipDialogInstance()->dismiss(); } QVariant ToolTip::icon() const { if (m_icon.isValid()) { return m_icon; } else { return QString(); } } void ToolTip::setIcon(const QVariant &icon) { if (icon == m_icon) { return; } m_icon = icon; emit iconChanged(); } QVariant ToolTip::image() const { if (m_image.isValid()) { return m_image; } else { return QString(); } } void ToolTip::setImage(const QVariant &image) { if (image == m_image) { return; } m_image = image; emit imageChanged(); } bool ToolTip::containsMouse() const { return m_containsMouse; } void ToolTip::setContainsMouse(bool contains) { if (m_containsMouse != contains) { m_containsMouse = contains; emit containsMouseChanged(); } if (!contains) { tooltipDialogInstance()->dismiss(); } } void ToolTip::hoverEnterEvent(QHoverEvent *event) { Q_UNUSED(event) setContainsMouse(true); if (!m_tooltipsEnabledGlobally) { return; } if (!isValid()) { return; } if (tooltipDialogInstance()->isVisible()) { // We signal the tooltipmanager that we're "potentially interested, // and ask to keep it open for a bit, so other items get the chance // to update the content before the tooltip hides -- this avoids // flickering // It need to be considered only when other items can deal with tooltip area if (m_active) { tooltipDialogInstance()->keepalive(); //FIXME: showToolTip needs to be renamed in sync or something like that showToolTip(); } } else { m_showTimer->start(m_interval); } } void ToolTip::hoverLeaveEvent(QHoverEvent *event) { Q_UNUSED(event) setContainsMouse(false); m_showTimer->stop(); } bool ToolTip::childMouseEventFilter(QQuickItem *item, QEvent *event) { return QQuickItem::childMouseEventFilter(item, event); } bool ToolTip::isValid() const { return m_mainItem || !mainText().isEmpty() || !subText().isEmpty(); } diff --git a/src/declarativeimports/plasmacomponents/plasmacomponentsplugin.cpp b/src/declarativeimports/plasmacomponents/plasmacomponentsplugin.cpp index 891476565..64f6feaeb 100644 --- a/src/declarativeimports/plasmacomponents/plasmacomponentsplugin.cpp +++ b/src/declarativeimports/plasmacomponents/plasmacomponentsplugin.cpp @@ -1,97 +1,97 @@ /* * Copyright 2011 by Marco Martin * This program 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, 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 Library 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 "plasmacomponentsplugin.h" #include #include #include #include "qrangemodel.h" #include #include #include #include "enums.h" #include "qmenu.h" #include "qmenuitem.h" class BKSingleton { public: EngineBookKeeping self; }; Q_GLOBAL_STATIC(BKSingleton, privateBKSelf) EngineBookKeeping::EngineBookKeeping() { } EngineBookKeeping *EngineBookKeeping::self() { return &privateBKSelf->self; } QQmlEngine *EngineBookKeeping::engine() const { //for components creation, any engine will do, as long is valid if (m_engines.isEmpty()) { qWarning() << "No engines found, this should never happen"; - return 0; + return nullptr; } else { return *m_engines.constBegin(); } } void EngineBookKeeping::insertEngine(QQmlEngine *engine) { connect(engine, SIGNAL(destroyed(QObject*)), this, SLOT(engineDestroyed(QObject*))); m_engines.insert(engine); } void EngineBookKeeping::engineDestroyed(QObject *deleted) { m_engines.remove(static_cast(deleted)); } void PlasmaComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); EngineBookKeeping::self()->insertEngine(engine); } void PlasmaComponentsPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QByteArray("org.kde.plasma.components")); qmlRegisterType(uri, 2, 0, "Menu"); qmlRegisterType(uri, 2, 0, "MenuItem"); qmlRegisterType(uri, 2, 0, "RangeModel"); qmlRegisterUncreatableType(uri, 2, 0, "DialogStatus", {}); qmlRegisterUncreatableType(uri, 2, 0, "PageOrientation", {}); qmlRegisterUncreatableType(uri, 2, 0, "PageStatus", {}); } #include "moc_plasmacomponentsplugin.cpp" diff --git a/src/declarativeimports/plasmacomponents/qmenu.cpp b/src/declarativeimports/plasmacomponents/qmenu.cpp index 96249a8db..793445af6 100644 --- a/src/declarativeimports/plasmacomponents/qmenu.cpp +++ b/src/declarativeimports/plasmacomponents/qmenu.cpp @@ -1,478 +1,478 @@ /*************************************************************************** * Copyright 2011 Viranch Mehta * * * * 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 "qmenu.h" #include #include #include #include #include #include #include #include #include "plasmacomponentsplugin.h" QMenuProxy::QMenuProxy(QObject *parent) : QObject(parent), m_menu(nullptr), m_status(DialogStatus::Closed), m_placement(Plasma::Types::LeftPosedTopAlignedPopup) { if (qobject_cast(QCoreApplication::instance())) { - m_menu = new QMenu(0); + m_menu = new QMenu(nullptr); // Breeze and Oxygen have rounded corners on menus. They set this attribute in polish() // but at that time the underlying surface has already been created where setting this // flag makes no difference anymore (Bug 385311) m_menu->setAttribute(Qt::WA_TranslucentBackground); KAcceleratorManager::manage(m_menu); connect(m_menu, &QMenu::triggered, this, &QMenuProxy::itemTriggered); connect(m_menu, &QMenu::aboutToHide, this, [ = ]() { m_status = DialogStatus::Closed; emit statusChanged(); }); } } QMenuProxy::~QMenuProxy() { delete m_menu; } QQmlListProperty QMenuProxy::content() { return QQmlListProperty(this, m_items); } int QMenuProxy::actionCount() const { return m_items.count(); } QMenuItem *QMenuProxy::action(int index) const { return m_items.at(index); } DialogStatus::Status QMenuProxy::status() const { return m_status; } QObject *QMenuProxy::visualParent() const { return m_visualParent.data(); } void QMenuProxy::setVisualParent(QObject *parent) { if (m_visualParent.data() == parent) { return; } //if the old parent was a QAction, disconnect the menu from it QAction *action = qobject_cast(m_visualParent.data()); if (action) { - action->setMenu(0); + action->setMenu(nullptr); m_menu->clear(); } //if parent is a QAction, become a submenu action = qobject_cast(parent); if (action) { action->setMenu(m_menu); m_menu->clear(); foreach (QMenuItem *item, m_items) { if (item->section()) { if (!item->isVisible()) { continue; } m_menu->addSection(item->text()); } else { m_menu->addAction(item->action()); } } m_menu->updateGeometry(); } m_visualParent = parent; emit visualParentChanged(); } QWindow *QMenuProxy::transientParent() { if (!m_menu) { return nullptr; } return m_menu->windowHandle()->transientParent(); } void QMenuProxy::setTransientParent(QWindow *parent) { if (parent == m_menu->windowHandle()->transientParent()) { return; } m_menu->windowHandle()->setTransientParent(parent); emit transientParentChanged(); } Plasma::Types::PopupPlacement QMenuProxy::placement() const { return m_placement; } void QMenuProxy::setPlacement(Plasma::Types::PopupPlacement placement) { if (m_placement != placement) { m_placement = placement; emit placementChanged(); } } int QMenuProxy::minimumWidth() const { return m_menu->minimumWidth(); } void QMenuProxy::setMinimumWidth(int width) { if (m_menu->minimumWidth() != width) { m_menu->setMinimumWidth(width); emit minimumWidthChanged(); } } int QMenuProxy::maximumWidth() const { return m_menu->maximumWidth(); } void QMenuProxy::setMaximumWidth(int width) { if (m_menu->maximumWidth() != width) { m_menu->setMaximumWidth(width); emit maximumWidthChanged(); } } void QMenuProxy::resetMaximumWidth() { setMaximumWidth(QWIDGETSIZE_MAX); } bool QMenuProxy::event(QEvent *event) { switch (event->type()) { case QEvent::ChildAdded: { QChildEvent *ce = static_cast(event); QMenuItem *mi = qobject_cast(ce->child()); //FIXME: linear complexity here if (mi && !m_items.contains(mi)) { if (mi->separator()) { m_menu->addSection(mi->text()); } else { m_menu->addAction(mi->action()); } m_items << mi; } break; } case QEvent::ChildRemoved: { QChildEvent *ce = static_cast(event); QMenuItem *mi = qobject_cast(ce->child()); //FIXME: linear complexity here if (mi) { m_menu->removeAction(mi->action()); m_items.removeAll(mi); } break; } default: break; } return QObject::event(event); } void QMenuProxy::clearMenuItems() { qDeleteAll(m_items); m_items.clear(); } void QMenuProxy::addMenuItem(const QString &text) { QMenuItem *item = new QMenuItem(); item->setText(text); m_menu->addAction(item->action()); m_items << item; } void QMenuProxy::addMenuItem(QMenuItem *item, QMenuItem *before) { if (before) { if (m_items.contains(item)) { m_menu->removeAction(item->action()); m_items.removeAll(item); } m_menu->insertAction(before->action(), item->action()); const int index = m_items.indexOf(before); if (index != -1) { m_items.insert(index, item); } else { m_items << item; } } else if (!m_items.contains(item)) { m_menu->addAction(item->action()); m_items << item; } } void QMenuProxy::addSection(const QString &text) { m_menu->addSection(text); } void QMenuProxy::removeMenuItem(QMenuItem *item) { if (!item) { return; } m_menu->removeAction(item->action()); m_items.removeOne(item); } void QMenuProxy::itemTriggered(QAction *action) { QMenuItem *item = qobject_cast(action); if (item) { emit triggered(item); int index = m_items.indexOf(item); if (index > -1) { emit triggeredIndex(index); } } } void QMenuProxy::rebuildMenu() { m_menu->clear(); foreach (QMenuItem *item, m_items) { if (item->section()) { if (!item->isVisible()) { continue; } m_menu->addSection(item->text()); } else { m_menu->addAction(item->action()); if (item->action()->menu()) { //This ensures existence of the QWindow m_menu->winId(); item->action()->menu()->winId(); item->action()->menu()->windowHandle()->setTransientParent(m_menu->windowHandle()); } } } m_menu->adjustSize(); } void QMenuProxy::open(int x, int y) { qDebug() << "Opening menu at" << x << y; QQuickItem *parentItem = nullptr; if (m_visualParent) { parentItem = qobject_cast(m_visualParent.data()); } else { parentItem = qobject_cast(parent()); } if (!parentItem) { return; } rebuildMenu(); QPointF pos = parentItem->mapToScene(QPointF(x, y)); if (parentItem->window() && parentItem->window()->screen()) { pos = parentItem->window()->mapToGlobal(pos.toPoint()); } openInternal(pos.toPoint()); } -Q_INVOKABLE void QMenuProxy::openRelative() +void QMenuProxy::openRelative() { QQuickItem *parentItem = nullptr; if (m_visualParent) { parentItem = qobject_cast(m_visualParent.data()); } else { parentItem = qobject_cast(parent()); } if (!parentItem) { return; } rebuildMenu(); QPointF pos; using namespace Plasma; auto boundaryCorrection = [&pos, this, parentItem](int hDelta, int vDelta) { if (!parentItem->window()) { return; } QScreen *screen = parentItem->window()->screen(); if (!screen) { return; } QRect geo = screen->geometry(); pos = parentItem->window()->mapToGlobal(pos.toPoint()); if (pos.x() < geo.x()) { pos.setX(pos.x() + hDelta); } if (pos.y() < geo.y()) { pos.setY(pos.y() + vDelta); } if (geo.x() + geo.width() < pos.x() + this->m_menu->width()) { pos.setX(pos.x() + hDelta); } if (geo.y() + geo.height() < pos.y() + this->m_menu->height()) { pos.setY(pos.y() + vDelta); } }; switch(m_placement) { case Types::TopPosedLeftAlignedPopup: { pos = parentItem->mapToScene(QPointF(0, -m_menu->height())); boundaryCorrection(-m_menu->width() + parentItem->width(), m_menu->height() + parentItem->height()); break; } case Types::LeftPosedTopAlignedPopup: { pos = parentItem->mapToScene(QPointF(-m_menu->width(), 0)); boundaryCorrection(m_menu->width() + parentItem->width(), -m_menu->height() + parentItem->height()); break; } case Types::TopPosedRightAlignedPopup: pos = parentItem->mapToScene(QPointF(parentItem->width() - m_menu->width(), -m_menu->height())); boundaryCorrection(m_menu->width() - parentItem->width(), m_menu->height() + parentItem->height()); break; case Types::RightPosedTopAlignedPopup: { pos = parentItem->mapToScene(QPointF(parentItem->width(), 0)); boundaryCorrection(-m_menu->width() - parentItem->width(), -m_menu->height() + parentItem->height()); break; } case Types::LeftPosedBottomAlignedPopup: pos = parentItem->mapToScene(QPointF(-m_menu->width(), -m_menu->height() + parentItem->height())); boundaryCorrection(m_menu->width() + parentItem->width(), m_menu->height() - parentItem->height()); break; case Types::BottomPosedLeftAlignedPopup: { pos = parentItem->mapToScene(QPointF(0, parentItem->height())); boundaryCorrection(-m_menu->width() + parentItem->width(), -m_menu->height() - parentItem->height()); break; } case Types::BottomPosedRightAlignedPopup: { pos = parentItem->mapToScene(QPointF(parentItem->width() - m_menu->width(), parentItem->height())); boundaryCorrection(m_menu->width() - parentItem->width(), -m_menu->height() - parentItem->height()); break; } case Types::RightPosedBottomAlignedPopup: { pos = parentItem->mapToScene(QPointF(parentItem->width(), -m_menu->height() + parentItem->height())); boundaryCorrection(-m_menu->width() - parentItem->width(), m_menu->height() - parentItem->height()); break; } default: open(); return; } openInternal(pos.toPoint()); } void QMenuProxy::openInternal(QPoint pos) { QQuickItem *parentItem = this->parentItem(); if (parentItem && parentItem->window()) { //create the QWindow m_menu->winId(); m_menu->windowHandle()->setTransientParent(parentItem->window()); // Workaround for QTBUG-59044 auto ungrabMouseHack = [this]() { QQuickItem *parentItem = this->parentItem(); if (parentItem && parentItem->window() && parentItem->window()->mouseGrabberItem()) { parentItem->window()->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(QString::fromLatin1(qVersion())) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, this, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround } m_menu->popup(pos); m_status = DialogStatus::Open; emit statusChanged(); } QQuickItem *QMenuProxy::parentItem() const { if (m_visualParent) { return qobject_cast(m_visualParent.data()); } return qobject_cast(parent()); } void QMenuProxy::close() { m_menu->hide(); } diff --git a/src/plasma/applet.cpp b/src/plasma/applet.cpp index 2a0481245..10230ff48 100644 --- a/src/plasma/applet.cpp +++ b/src/plasma/applet.cpp @@ -1,842 +1,842 @@ /* * Copyright 2005 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * Copyright 2008 by Ménard Alexis * Copyright (c) 2009 Chani Armitage * * This program 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, 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 Library 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 "applet.h" #include "private/applet_p.h" #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containment.h" #include "corona.h" #include "package.h" #include "plasma.h" #include "scripting/appletscript.h" #include "pluginloader.h" #include "private/associatedapplicationmanager_p.h" #include "private/containment_p.h" #include "private/package_p.h" #include "debug_p.h" namespace Plasma { static KPluginMetaData appletMetadataForDirectory(const QString &path) { return QFile::exists(path + QLatin1String("/metadata.json")) ? KPluginMetaData(path + QLatin1String("/metadata.json")) : KPluginMetaData::fromDesktopFile(path + QLatin1String("/metadata.desktop"), { QStringLiteral("plasma-applet.desktop") }); } Applet::Applet(const KPluginMetaData &info, QObject *parent, uint appletId) : QObject(parent), d(new AppletPrivate(info, appletId, this)) { qCDebug(LOG_PLASMA) << " From KPluginMetaData, valid? " << info.isValid(); // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(); d->setupPackage(); } Applet::Applet(const KPluginInfo &info, QObject *parent, uint appletId) : Applet(info.toMetaData(), parent, appletId) { } Applet::Applet(QObject *parent, const QString &serviceID, uint appletId) : QObject(parent), d(new AppletPrivate(KPluginMetaData(serviceID), appletId, this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(); d->setupPackage(); } Applet::Applet(QObject *parentObject, const QVariantList &args) : QObject(0), d(new AppletPrivate(KPluginMetaData(), args.count() > 2 ? args[2].toInt() : 0, this)) { setParent(parentObject); if (args.count() > 0) { const QVariant first = args.first(); if (first.canConvert()) { d->package = first.value(); } } if (args.count() > 1) { const QVariant second = args[1]; if (second.canConvert()) { d->appletDescription = KPluginMetaData(second.toString()); } else if (second.canConvert()) { auto metadata = second.toMap().value(QStringLiteral("MetaData")).toMap(); d->appletDescription = KPluginMetaData(QJsonObject::fromVariantMap(metadata), {}); } } d->icon = d->appletDescription.iconName(); if (args.contains(QVariant::fromValue(QStringLiteral("org.kde.plasma:force-create")))) { setProperty("org.kde.plasma:force-create", true); } // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(QString(), args.mid(3)); d->setupPackage(); } Applet::Applet(const QString &packagePath, uint appletId) - : QObject(0), + : QObject(nullptr), d(new AppletPrivate(appletMetadataForDirectory(packagePath), appletId, this)) { d->init(packagePath); d->setupPackage(); } Applet::~Applet() { if (d->transient) { d->resetConfigurationObject(); } //let people know that i will die emit appletDeleted(this); delete d; } void Applet::init() { //Don't implement anything here, it will be overridden by subclasses } uint Applet::id() const { return d->appletId; } void Applet::save(KConfigGroup &g) const { if (d->transient || !d->appletDescription.isValid()) { return; } KConfigGroup group = g; if (!group.isValid()) { group = *d->mainConfigGroup(); } //qCDebug(LOG_PLASMA) << "saving" << pluginName() << "to" << group.name(); // we call the dptr member directly for locked since isImmutable() // also checks kiosk and parent containers group.writeEntry("immutability", (int)d->immutability); group.writeEntry("plugin", d->appletDescription.pluginId()); if (!d->started) { return; } KConfigGroup appletConfigGroup(&group, "Configuration"); saveState(appletConfigGroup); if (d->configLoader) { // we're saving so we know its changed, we don't need or want the configChanged // signal bubbling up at this point due to that disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); d->configLoader->save(); connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); } } void Applet::restore(KConfigGroup &group) { setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable)); KConfigGroup shortcutConfig(&group, "Shortcuts"); QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString()); if (!shortcutText.isEmpty()) { setGlobalShortcut(QKeySequence(shortcutText)); /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText); #endif #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "set to" << d->activationAction->objectName() #endif << d->activationAction->globalShortcut().primary(); */ } // local shortcut, if any //TODO: implement; the shortcut will need to be registered with the containment /* #include "accessmanager.h" #include "private/plasmoidservice_p.h" #include "authorizationmanager.h" #include "authorizationmanager.h" shortcutText = shortcutConfig.readEntryUntranslated("local", QString()); if (!shortcutText.isEmpty()) { //TODO: implement; the shortcut } */ } void Applet::setLaunchErrorMessage(const QString &message) { if (message == d->launchErrorMessage) { return; } d->failed = true; d->launchErrorMessage = message; } void Applet::saveState(KConfigGroup &group) const { if (d->script) { emit d->script->saveState(group); } if (group.config()->name() != config().config()->name()) { // we're being saved to a different file! // let's just copy the current values in our configuration over KConfigGroup c = config(); c.copyTo(&group); } } KConfigGroup Applet::config() const { if (d->transient) { return KConfigGroup(KSharedConfig::openConfig(), "PlasmaTransientsConfig"); } if (isContainment()) { return *(d->mainConfigGroup()); } return KConfigGroup(d->mainConfigGroup(), "Configuration"); } KConfigGroup Applet::globalConfig() const { KConfigGroup globalAppletConfig; QString group = isContainment() ? QStringLiteral("ContainmentGlobals") : QStringLiteral("AppletGlobals"); Containment *cont = containment(); Corona *corona = 0; if (cont) { corona = cont->corona(); } if (corona) { KSharedConfig::Ptr coronaConfig = corona->config(); globalAppletConfig = KConfigGroup(coronaConfig, group); } else { globalAppletConfig = KConfigGroup(KSharedConfig::openConfig(), group); } return KConfigGroup(&globalAppletConfig, d->globalName()); } void Applet::destroy() { if (immutability() != Types::Mutable || d->transient || !d->started) { return; //don't double delete } d->setDestroyed(true); //FIXME: an animation on leave if !isContainment() would be good again .. which should be handled by the containment class d->cleanUpAndDelete(); } bool Applet::destroyed() const { return d->transient; } KConfigLoader *Applet::configScheme() const { if (!d->configLoader) { const QString xmlPath = d->package.isValid() ? d->package.filePath("mainconfigxml") : QString(); KConfigGroup cfg = config(); if (xmlPath.isEmpty()) { d->configLoader = new KConfigLoader(cfg, 0); } else { QFile file(xmlPath); d->configLoader = new KConfigLoader(cfg, &file); QObject::connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); } } return d->configLoader; } Package Applet::package() const { Package p; p.d->internalPackage = new KPackage::Package(d->package); return p; } KPackage::Package Applet::kPackage() const { return d->package; } void Applet::updateConstraints(Plasma::Types::Constraints constraints) { d->scheduleConstraintsUpdate(constraints); } void Applet::constraintsEvent(Plasma::Types::Constraints constraints) { //NOTE: do NOT put any code in here that reacts to constraints updates // as it will not get called for any applet that reimplements constraintsEvent // without calling the Applet:: version as well, which it shouldn't need to. // INSTEAD put such code into flushPendingConstraintsEvents Q_UNUSED(constraints) //qCDebug(LOG_PLASMA) << constraints << "constraints are FormFactor: " << formFactor() // << ", Location: " << location(); if (d->script) { d->script->constraintsEvent(constraints); } } QString Applet::title() const { if (!d->customTitle.isEmpty()) { return d->customTitle; } if (d->appletDescription.isValid()) { return d->appletDescription.name(); } return i18n("Unknown"); } void Applet::setTitle(const QString &title) { if (title == d->customTitle) { return; } d->customTitle = title; emit titleChanged(title); } QString Applet::icon() const { return d->icon; } void Applet::setIcon(const QString &icon) { if (icon == d->icon) { return; } d->icon = icon; emit iconChanged(icon); } bool Applet::isBusy() const { return d->busy; } void Applet::setBusy(bool busy) { if (busy == d->busy) { return; } d->busy = busy; emit busyChanged(busy); } KPluginInfo Applet::pluginInfo() const { return KPluginInfo(d->appletDescription); } KPluginMetaData Applet::pluginMetaData() const { return d->appletDescription; } Types::ImmutabilityType Applet::immutability() const { // if this object is itself system immutable, then just return that; it's the most // restrictive setting possible and will override anything that might be happening above it // in the Corona->Containment->Applet hierarchy if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) { return Types::SystemImmutable; } //Returning the more strict immutability between the applet immutability, Containment and Corona Types::ImmutabilityType upperImmutability = Types::Mutable; if (isContainment()) { Corona *cor = static_cast(const_cast(this))->corona(); if (cor) { upperImmutability = cor->immutability(); } } else { const Containment *cont = containment(); if (cont) { if (cont->corona()) { upperImmutability = cont->corona()->immutability(); } else { upperImmutability = cont->immutability(); } } } if (upperImmutability != Types::Mutable) { // it's either system or user immutable, and we already check for local system immutability, // so upperImmutability is guaranteed to be as or more severe as this object's immutability return upperImmutability; } else { return d->immutability; } } void Applet::setImmutability(const Types::ImmutabilityType immutable) { if (d->immutability == immutable || immutable == Types::SystemImmutable) { // we do not store system immutability in d->immutability since that gets saved // out to the config file; instead, we check with // the config group itself for this information at all times. this differs from // corona, where SystemImmutability is stored in d->immutability. return; } d->immutability = immutable; updateConstraints(Types::ImmutableConstraint); } QString Applet::launchErrorMessage() const { return d->launchErrorMessage; } bool Applet::failedToLaunch() const { return d->failed; } bool Applet::configurationRequired() const { return d->needsConfig; } QString Applet::configurationRequiredReason() const { return d->configurationRequiredReason; } void Applet::setConfigurationRequired(bool needsConfig, const QString &reason) { if (d->needsConfig == needsConfig && reason == d->configurationRequiredReason) { return; } d->needsConfig = needsConfig; d->configurationRequiredReason = reason; emit configurationRequiredChanged(needsConfig, reason); } bool Applet::isUserConfiguring() const { return d->userConfiguring; } void Applet::setUserConfiguring(bool configuring) { if (configuring == d->userConfiguring) { return; } d->userConfiguring = configuring; emit userConfiguringChanged(configuring); } Types::ItemStatus Applet::status() const { return d->itemStatus; } void Applet::setStatus(const Types::ItemStatus status) { if (status == d->itemStatus) { return; } d->itemStatus = status; emit statusChanged(status); } void Applet::flushPendingConstraintsEvents() { if (d->pendingConstraints == Types::NoConstraint) { return; } if (d->constraintsTimer.isActive()) { d->constraintsTimer.stop(); } //qCDebug(LOG_PLASMA) << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!"; Plasma::Types::Constraints c = d->pendingConstraints; d->pendingConstraints = Types::NoConstraint; if (c & Plasma::Types::UiReadyConstraint) { d->setUiReady(); } if (c & Plasma::Types::StartupCompletedConstraint) { //common actions bool unlocked = immutability() == Types::Mutable; QAction *closeApplet = d->actions->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setEnabled(unlocked); closeApplet->setVisible(unlocked); connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(askDestroy()), Qt::UniqueConnection); } QAction *configAction = d->actions->action(QStringLiteral("configure")); if (configAction) { if (d->hasConfigurationInterface) { bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); configAction->setVisible(canConfig); configAction->setEnabled(canConfig); } } QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { connect(runAssociatedApplication, SIGNAL(triggered(bool)), this, SLOT(runAssociatedApplication()), Qt::UniqueConnection); } d->updateShortcuts(); } if (c & Plasma::Types::ImmutableConstraint) { bool unlocked = immutability() == Types::Mutable; QAction *action = d->actions->action(QStringLiteral("remove")); if (action) { action->setVisible(unlocked); action->setEnabled(unlocked); } action = d->actions->action(QStringLiteral("configure")); if (action && d->hasConfigurationInterface) { bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); action->setVisible(canConfig); action->setEnabled(canConfig); } //an immutable constraint will alwasy happen at startup //make sure don't emit a change signal for nothing if (d->oldImmutability != immutability()) { emit immutabilityChanged(immutability()); } d->oldImmutability = immutability(); } // now take care of constraints in special subclass: Contaiment Containment *containment = qobject_cast(this); if (containment) { containment->d->containmentConstraintsEvent(c); } // pass the constraint on to the actual subclass constraintsEvent(c); if (c & Types::StartupCompletedConstraint) { // start up is done, we can now go do a mod timer if (d->modificationsTimer) { if (d->modificationsTimer->isActive()) { d->modificationsTimer->stop(); } } else { d->modificationsTimer = new QBasicTimer; } } if (c & Plasma::Types::FormFactorConstraint) { emit formFactorChanged(formFactor()); } if (c & Plasma::Types::LocationConstraint) { emit locationChanged(location()); } } QList Applet::contextualActions() { //qCDebug(LOG_PLASMA) << "empty context actions"; return d->script ? d->script->contextualActions() : QList(); } KActionCollection *Applet::actions() const { return d->actions; } Types::FormFactor Applet::formFactor() const { Containment *c = containment(); QObject *pw = qobject_cast(parent()); Plasma::Applet *parentApplet = qobject_cast(pw); //assumption: this loop is usually is -really- short or doesn't run at all while (!parentApplet && pw && pw->parent()) { pw = pw->parent(); parentApplet = qobject_cast(pw); } return c ? c->d->formFactor : Plasma::Types::Planar; } Containment *Applet::containment() const { Containment *c = qobject_cast(const_cast(this)); if (c && c->isContainment()) { return c; } else { c = 0; } QObject *parent = this->parent(); while (parent) { Containment *possibleC = qobject_cast(parent); if (possibleC && possibleC->isContainment()) { c = possibleC; break; } parent = parent->parent(); } return c; } void Applet::setGlobalShortcut(const QKeySequence &shortcut) { if (!d->activationAction) { d->activationAction = new QAction(this); d->activationAction->setText(i18n("Activate %1 Widget", title())); d->activationAction->setObjectName(QStringLiteral("activate widget %1").arg(id())); // NO I18N connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activated())); connect(d->activationAction, SIGNAL(changed()), this, SLOT(globalShortcutChanged())); } else if (d->activationAction->shortcut() == shortcut) { return; } d->activationAction->setShortcut(shortcut); d->globalShortcutEnabled = true; QList seqs; seqs << shortcut; KGlobalAccel::self()->setShortcut(d->activationAction, seqs, KGlobalAccel::NoAutoloading); d->globalShortcutChanged(); } QKeySequence Applet::globalShortcut() const { if (d->activationAction) { QList shortcuts = KGlobalAccel::self()->shortcut(d->activationAction); if (!shortcuts.isEmpty()) { return shortcuts.first(); } } return QKeySequence(); } Types::Location Applet::location() const { Containment *c = containment(); return c ? c->d->location : Plasma::Types::Desktop; } bool Applet::hasConfigurationInterface() const { return d->hasConfigurationInterface; } void Applet::setHasConfigurationInterface(bool hasInterface) { if (hasInterface == d->hasConfigurationInterface) { return; } QAction *configAction = d->actions->action(QStringLiteral("configure")); if (configAction) { bool enable = hasInterface; if (enable) { const bool unlocked = immutability() == Types::Mutable; enable = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); } configAction->setEnabled(enable); } d->hasConfigurationInterface = hasInterface; } void Applet::configChanged() { if (d->script) { if (d->configLoader) { d->configLoader->load(); } d->script->configChanged(); } } void Applet::setAssociatedApplication(const QString &string) { AssociatedApplicationManager::self()->setApplication(this, string); QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); runAssociatedApplication->setVisible(valid); runAssociatedApplication->setEnabled(valid); } } void Applet::setAssociatedApplicationUrls(const QList &urls) { AssociatedApplicationManager::self()->setUrls(this, urls); QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); runAssociatedApplication->setVisible(valid); runAssociatedApplication->setEnabled(valid); } } QString Applet::associatedApplication() const { return AssociatedApplicationManager::self()->application(this); } QList Applet::associatedApplicationUrls() const { return AssociatedApplicationManager::self()->urls(this); } void Applet::runAssociatedApplication() { AssociatedApplicationManager::self()->run(this); } bool Applet::hasValidAssociatedApplication() const { return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); } Applet *Applet::loadPlasmoid(const QString &path, uint appletId) { const KPluginMetaData md = appletMetadataForDirectory(path); if (md.isValid()) { QStringList types = md.serviceTypes(); if (types.contains(QStringLiteral("Plasma/Containment"))) { return new Containment(md, appletId); } else { return new Applet(md, nullptr, appletId); } } return 0; } void Applet::timerEvent(QTimerEvent *event) { if (d->transient) { d->constraintsTimer.stop(); if (d->modificationsTimer) { d->modificationsTimer->stop(); } return; } if (event->timerId() == d->constraintsTimer.timerId()) { d->constraintsTimer.stop(); // Don't flushPendingConstraints if we're just starting up // flushPendingConstraints will be called by Corona if (!(d->pendingConstraints & Plasma::Types::StartupCompletedConstraint)) { flushPendingConstraintsEvents(); } } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) { d->modificationsTimer->stop(); // invalid group, will result in save using the default group KConfigGroup cg; save(cg); emit configNeedsSaving(); } } bool Applet::isContainment() const { //HACK: this is a special case for the systray //containment in an applet that is not a containment Applet *pa = qobject_cast(parent()); if (pa && !pa->isContainment()) { return true; } //normal "acting as a containment" condition return qobject_cast(this) && qobject_cast(parent()); } } // Plasma namespace #include "moc_applet.cpp"