diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 47 | ||||
-rw-r--r-- | src/actions.cpp | 27 | ||||
-rw-r--r-- | src/engine-eds.cpp | 65 | ||||
-rw-r--r-- | src/engine-mkcal.cpp | 632 | ||||
-rw-r--r-- | src/exporter.cpp | 10 | ||||
-rw-r--r-- | src/haptic.cpp | 16 | ||||
-rw-r--r-- | src/locations-settings.cpp | 16 | ||||
-rw-r--r-- | src/main.cpp | 6 | ||||
-rw-r--r-- | src/menu.cpp | 99 | ||||
-rw-r--r-- | src/notifications.cpp | 38 | ||||
-rw-r--r-- | src/planner-aggregate.cpp | 4 | ||||
-rw-r--r-- | src/planner-snooze.cpp | 6 | ||||
-rw-r--r-- | src/settings-live.cpp | 13 | ||||
-rw-r--r-- | src/snap.cpp | 21 | ||||
-rw-r--r-- | src/wakeup-timer-mainloop.cpp | 4 | ||||
-rw-r--r-- | src/wakeup-timer-powerd.cpp | 6 |
16 files changed, 888 insertions, 122 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d6d6844..2bc4452 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,13 @@ set (SERVICE_LIB "indicatordatetimeservice") -set (SERVICE_EXEC "ayatana-indicator-datetime-service") -add_definitions (-DG_LOG_DOMAIN="ayatana-indicator-datetime") +if (ENABLE_LOMIRI_FEATURES) + set (INDICATOR_VARIANT_NAME "lomiri-indicator-datetime") +else () + set (INDICATOR_VARIANT_NAME "${CMAKE_PROJECT_NAME}") +endif () + +add_definitions (-DG_LOG_DOMAIN="${INDICATOR_VARIANT_NAME}") +set (SERVICE_EXEC "${INDICATOR_VARIANT_NAME}-service") # handwritten sources set (SERVICE_C_SOURCES @@ -15,7 +21,6 @@ set (SERVICE_CXX_SOURCES clock.cpp clock-live.cpp date-time.cpp - engine-eds.cpp exporter.cpp formatter.cpp formatter-desktop.cpp @@ -41,34 +46,46 @@ set (SERVICE_CXX_SOURCES wakeup-timer-mainloop.cpp wakeup-timer-powerd.cpp) +if (ENABLE_LOMIRI_FEATURES) + list (APPEND SERVICE_CXX_SOURCES engine-mkcal.cpp) +else () + list (APPEND SERVICE_CXX_SOURCES engine-eds.cpp) +endif() + # generated sources include (GdbusCodegen) set(SERVICE_GENERATED_SOURCES) add_gdbus_codegen(SERVICE_GENERATED_SOURCES dbus-alarm-properties org.ayatana.indicator ${CMAKE_SOURCE_DIR}/data/org.ayatana.indicator.datetime.AlarmProperties.xml) -if(LOMIRI_SCHEMAS_FOUND) + +if (ENABLE_LOMIRI_FEATURES) add_gdbus_codegen(SERVICE_GENERATED_SOURCES dbus-accounts-sound com.lomiri.touch /usr/share/accountsservice/interfaces/com.lomiri.touch.AccountsService.Sound.xml) - add_definitions (-DHAS_LOMIRI_SCHEMAS) endif() -# add warnings/coverage info on handwritten files -# but not the autogenerated ones... -set_source_files_properties(${SERVICE_CXX_SOURCES} - PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS}) -set_source_files_properties(${SERVICE_C_SOURCES} - PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS}) - # add the bin dir to our include path so our code can find the generated header files include_directories (${CMAKE_CURRENT_BINARY_DIR}) +set_source_files_properties (engine-eds.cpp PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -Wno-enum-constexpr-conversion") add_library (${SERVICE_LIB} STATIC ${SERVICE_C_SOURCES} ${SERVICE_CXX_SOURCES} ${SERVICE_GENERATED_SOURCES}) + +pkg_check_modules (LIBNOTIFY libnotify REQUIRED) + +if (LIBNOTIFY_VERSION VERSION_GREATER_EQUAL "0.8.4") + target_compile_definitions (${SERVICE_LIB} PUBLIC LIBNOTIFY_HAS_SET_APP_ICON) +endif() + include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) +if (ENABLE_LOMIRI_FEATURES) + set_source_files_properties (engine-mkcal.cpp PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") + target_link_libraries (${SERVICE_LIB} KF5::CalendarCore) + target_compile_definitions (${SERVICE_LIB} PRIVATE -DQT_DEBUG -DQT_MESSAGELOGCONTEXT) +endif () + add_executable (${SERVICE_EXEC} main.cpp) -set_source_files_properties(${SERVICE_SOURCES} main.cpp PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS}) -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${URLDISPATCHER_LIBRARIES}) -install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${INDICATOR_VARIANT_NAME}") diff --git a/src/actions.cpp b/src/actions.cpp index 315340a..69582ba 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1,5 +1,6 @@ /* * Copyright 2013 Canonical Ltd. + * Copyright 2021 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,14 +16,20 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/actions.h> #include <datetime/utils.h> // split_settings_location() - +#include <algorithm> #include <glib.h> #include <gio/gio.h> +extern "C" +{ + #include <ayatana/common/utils.h> +} + namespace ayatana { namespace indicator { namespace datetime { @@ -50,16 +57,18 @@ DateTime datetime_from_timet_variant(GVariant* v) bool lookup_appointment_by_uid(const std::shared_ptr<State>& state, const gchar* uid, Appointment& setme) { - for(const auto& appt : state->calendar_upcoming->appointments().get()) + bool bRet = false; + + std::for_each(state->calendar_upcoming->appointments().get().begin(), state->calendar_upcoming->appointments().get().end(), [uid, &setme, &bRet](const Appointment& appt) { if (appt.uid == uid) { setme = appt; - return true; + bRet = true; } - } + }); - return false; + return bRet; } void on_appointment_activated (GSimpleAction*, GVariant *vdata, gpointer gself) @@ -120,7 +129,8 @@ void on_calendar_date_activated(GSimpleAction * /*action*/, g_return_if_fail(t != 0); auto dt = DateTime::Local(t).start_of_day(); - static_cast<Actions*>(gself)->set_calendar_date(dt, false); + bool bLomiri = ayatana_common_utils_is_lomiri (); + static_cast<Actions*>(gself)->set_calendar_date(dt, bLomiri); } GVariant* create_default_header_state() @@ -138,7 +148,10 @@ GVariant* create_calendar_state(const std::shared_ptr<State>& state) { gboolean days[32] = { 0 }; for (const auto& appt : state->calendar_month->appointments().get()) - days[appt.begin.day_of_month()] = true; + if (!appt.is_alarm() || state->settings->show_alarms.get()) + { + days[appt.begin.day_of_month()] = true; + } GVariantBuilder day_builder; g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); diff --git a/src/engine-eds.cpp b/src/engine-eds.cpp index 4396d45..7a61fc0 100644 --- a/src/engine-eds.cpp +++ b/src/engine-eds.cpp @@ -1,6 +1,6 @@ /* * Copyright 2014 Canonical Ltd. - * Copyright 2021 Robert Tari + * Copyright 2021-2024 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -19,6 +19,10 @@ * Robert Tari <robert@tari.in> */ +#ifndef ALARM_DEFAULT_SOUND +#define ALARM_DEFAULT_SOUND "dummy" +#endif + #include <datetime/engine-eds.h> #include <datetime/myself.h> #include <libical/ical.h> @@ -48,7 +52,7 @@ class EdsEngine::Impl { public: - Impl(const std::shared_ptr<Myself> &myself) + explicit Impl(const std::shared_ptr<Myself> &myself) : m_myself(myself) { auto cancellable_deleter = [](GCancellable * c) { @@ -473,14 +477,14 @@ private: // for each component.. for (auto l=components; l!=nullptr; l=l->next) { - bool changed = false; + bool bChanged = false; // for each alarm... auto component = E_CAL_COMPONENT(l->data); auto auids = e_cal_component_get_alarm_uids(component); - for(auto l=auids; l!=nullptr; l=l->next) + for(auto lAlarms=auids; lAlarms!=nullptr; lAlarms=lAlarms->next) { - auto auid = static_cast<const char*>(l->data); + auto auid = static_cast<const char*>(lAlarms->data); auto alarm = e_cal_component_get_alarm(component, auid); if (alarm == nullptr) continue; @@ -490,13 +494,13 @@ private: { e_cal_component_remove_alarm (component, auid); e_cal_component_add_alarm (component, new_alarm); - changed = true; + bChanged = true; g_clear_pointer (&new_alarm, e_cal_component_alarm_free); } } g_slist_free_full (auids, g_free); - if (changed) + if (bChanged) { auto icc = e_cal_component_get_icalcomponent(component); // icc owned by ecc modify_slist = g_slist_prepend(modify_slist, icc); @@ -631,19 +635,18 @@ private: auto action = e_cal_component_alarm_get_action(alarm); if (action == E_CAL_COMPONENT_ALARM_AUDIO) { - ICalAttach *attach = nullptr; auto attachments = e_cal_component_alarm_get_attachments(alarm); - if (attachments != nullptr && attachments->next != nullptr) - attach = I_CAL_ATTACH (attachments->data); + for (; attachments != nullptr; attachments = attachments->next) { + ICalAttach *attach = I_CAL_ATTACH (attachments->data); - if (attach != nullptr) - { - if (i_cal_attach_get_is_url (attach)) + if (attach != nullptr && i_cal_attach_get_is_url (attach)) { const char* url = i_cal_attach_get_url(attach); - if (url != nullptr) + if (url != nullptr) { ret = url; + break; + } } } if (ret.empty()) @@ -1071,6 +1074,34 @@ private: } g_slist_free_full(categ_list, g_free); + // Get the colour - we might need this override in the future + + /*if (icc) + { + ECalComponentPropertyBag *pBag = e_cal_component_property_bag_new_from_component (icc, NULL, NULL); + + if (pBag) + { + guint nProperties = e_cal_component_property_bag_get_count (pBag); + + for (guint nProperty = 0; nProperty < nProperties; nProperty++) + { + ICalProperty *pProperty = e_cal_component_property_bag_get (pBag, nProperty); + gchar *sName = i_cal_property_get_property_name (pProperty); + gboolean bColour = !g_strcmp0 (sName, "COLOR"); + + if (bColour) + { + baseline.color = i_cal_property_get_value_as_string (pProperty); + + break; + } + } + + e_cal_component_property_bag_free (pBag); + } + }*/ + g_debug("%s got appointment from %s to %s: %s", G_STRLOC, baseline.begin.format("%F %T %z").c_str(), baseline.end.format("%F %T %z").c_str(), @@ -1127,9 +1158,9 @@ private: alarm.text = get_alarm_text(a); if (alarm.audio_url.empty()) - alarm.audio_url = get_alarm_sound_url(a, (baseline.is_alarm() ? - "file://" ALARM_DEFAULT_SOUND : - "file://" CALENDAR_DEFAULT_SOUND)); + { + alarm.audio_url = get_alarm_sound_url(a, (baseline.is_alarm() ? "file://" ALARM_DEFAULT_SOUND : "file://" CALENDAR_DEFAULT_SOUND)); + } if (!alarm.time.is_set()) alarm.time = trigger_time; diff --git a/src/engine-mkcal.cpp b/src/engine-mkcal.cpp new file mode 100644 index 0000000..56e4821 --- /dev/null +++ b/src/engine-mkcal.cpp @@ -0,0 +1,632 @@ +/* + * Copyright 2024-2025 Robert Tari + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Robert Tari <robert@tari.in> + */ + +#include <gio/gio.h> +#include <datetime/engine-eds.h> +#include <datetime/myself.h> +#include <extendedstorage.h> +#include <QDebug> + +namespace ayatana +{ + namespace indicator + { + namespace datetime + { + class EdsEngine::Impl + { + public: + + explicit Impl (const std::shared_ptr<Myself> &myself) : pMyself (myself) + { + this->pMyself->emails ().changed ().connect ([this] (const std::set<std::string> &) + { + setDirtySoon (); + }); + + // mKCal::ExtendedStorageObserver does not work + const gchar* sHome = g_get_home_dir (); + gchar *sDatabase = g_strdup_printf ("%s/.local/share/system/privileged/Calendar/mkcal/db.changed", sHome); + GFile *pFile = g_file_new_for_path (sDatabase); + g_free (sDatabase); + GError *pError = NULL; + this->pMonitor = g_file_monitor_file (pFile, G_FILE_MONITOR_NONE, NULL, &pError); + g_object_unref (pFile); + + if (!this->pMonitor) + { + g_warning ("Panic: Error creating file monitor: %s", pError->message); + g_error_free (pError); + } + else + { + g_signal_connect (this->pMonitor, "changed", G_CALLBACK (onDatabaseChanged), this); + } + } + + ~Impl () + { + if (this->pMonitor) + { + g_object_unref (this->pMonitor); + } + } + + core::Signal<>& changed () + { + return this->pChanged; + } + + void addAlarm (KCalendarCore::Alarm::Ptr pAlarm, std::map<std::pair<DateTime, DateTime>, std::map<DateTime, Alarm>> *pAlarms, QDateTime *pTriggerTime, QTimeZone *pTimeZone, KCalendarCore::Incidence::Ptr pIncidence, bool bAlarm) + { + /* + Loop through lAlarms to get information that we need to build the instance appointments and their alarms. + + Outer map key is the instance component's start + end time. We build Appointment.begin and .end from that. + + Inner map key is the alarm trigger, we build Alarm.time from that. + + Inner map value is the Alarm. + + We map the alarms based on their trigger time so that we can fold together multiple valarms that trigger for the same component at the same time. This is commonplace; + e.g. one valarm will have a display action and another will specify a sound to be played. + */ + DateTime cBeginTime = datetimeFromQDateTime (pTriggerTime, pTimeZone); + QDateTime cQEndTime = {}; + const bool bTime = pAlarm->hasTime (); + + if (!bTime) + { + cQEndTime = *pTriggerTime; + } + else + { + cQEndTime = pAlarm->endTime (); + } + + DateTime cEndTime = datetimeFromQDateTime (&cQEndTime, pTimeZone); + auto dInstanceTime = std::make_pair (cBeginTime, cEndTime); + DateTime cTriggerTime = datetimeFromQDateTime (pTriggerTime, pTimeZone); + auto &cAlarm = (*pAlarms)[dInstanceTime][cTriggerTime]; + bool bTextEmpty = cAlarm.text.empty (); + + if (bTextEmpty) + { + const KCalendarCore::Alarm::Type eType = pAlarm->type (); + + if (eType == KCalendarCore::Alarm::Type::Display) + { + const QString sText = pAlarm->text (); + bool bEmpty = sText.isEmpty (); + + if (!bEmpty) + { + cAlarm.text = sText.toStdString (); + } + } + } + + bool bAudioEmpty = cAlarm.audio_url.empty (); + + if (bAudioEmpty) + { + QString sUri = {}; + const KCalendarCore::Alarm::Type eType = pAlarm->type (); + + if (eType == KCalendarCore::Alarm::Type::Audio) + { + const QString sFile = pAlarm->audioFile (); + bool bEmpty = sFile.isEmpty (); + + if (bEmpty) + { + cAlarm.audio_url = bAlarm ? "file://" ALARM_DEFAULT_SOUND : "file://" CALENDAR_DEFAULT_SOUND; + } + else + { + cAlarm.audio_url = sFile.toStdString (); + } + } + } + + bool bTimeSet = cAlarm.time.is_set (); + + if (!bTimeSet) + { + cAlarm.time = cTriggerTime; + } + } + + void getAppointments (const QDateTime *pDateTimeBegin, const QDateTime *pDateTimeEnd, QTimeZone *pTimezone, std::function<void (const std::vector<Appointment>&)> pOnAppointments) + { + qDebug () << "Getting all appointments from " << *pDateTimeBegin << " to " << *pDateTimeEnd; + + const QDate cDateBegin = pDateTimeBegin->date (); + const QDate cDateEnd = pDateTimeEnd->date (); + + // Load the incidences + QTimeZone cSystemTimeZone = QTimeZone::systemTimeZone (); + mKCal::ExtendedCalendar *pCalendar = new mKCal::ExtendedCalendar (cSystemTimeZone); + mKCal::ExtendedCalendar::Ptr pCalendarPtr = mKCal::ExtendedCalendar::Ptr (pCalendar); + mKCal::ExtendedStorage::Ptr pStoragePtr = mKCal::ExtendedCalendar::defaultStorage (pCalendarPtr); + pStoragePtr->open (); + pStoragePtr->load (cDateBegin, cDateEnd); + + // Get the notebooks + mKCal::Notebook::List lNotebooks = pStoragePtr->notebooks (); + std::map<QString, mKCal::Notebook::Ptr> dNotebooks; + + for (mKCal::Notebook::Ptr pNotebook : lNotebooks) + { + QString sUid = pNotebook->uid (); + dNotebooks[sUid] = pNotebook; + } + + std::vector<Appointment> lAppointments; + KCalendarCore::Incidence::List lIncidences = pCalendarPtr->incidences (); + + std::sort (lIncidences.begin (), lIncidences.end (), [](const KCalendarCore::Incidence::Ptr &pIncidence1, const KCalendarCore::Incidence::Ptr &pIncidence2) + { + return (pIncidence1->dtStart () < pIncidence2->dtStart ()); + }); + + // Walk through the incidences to build the appointment list + for (KCalendarCore::Incidence::Ptr pIncidence : lIncidences) + { + QString sCalendar = pCalendarPtr->notebook (pIncidence); + bool bAlarm = dNotebooks[sCalendar]->name () == "Alarms"; + const QString sUid = pIncidence->uid (); + bool bEmpty = sUid.isEmpty (); + + if (bEmpty == false) + { + bool bIncidenceInteresting = isIncidenceInteresting (pIncidence); + + if (bIncidenceInteresting) + { + const QString sColor = dNotebooks[sCalendar]->color (); + std::string sColorStd = sColor.toStdString (); + Appointment cAppointment = getAppointment (pIncidence, pTimezone, bAlarm, sColorStd); + Appointment *pAppointment = nullptr; + + if (!bAlarm) + { + // Walk the recurrences and add them + const KCalendarCore::Recurrence *pRecurrence = pIncidence->recurrence (); + const bool bRecurs = pRecurrence->recurs (); + + if (bRecurs) + { + QDateTime cRecurrenceStart = pRecurrence->getNextDateTime (*pDateTimeBegin); + bool bRecurrenceValid = cRecurrenceStart.isValid (); + + while (bRecurrenceValid && cRecurrenceStart <= *pDateTimeEnd) + { + Appointment cRecurringAppointment = cAppointment; + cRecurringAppointment.begin = datetimeFromQDateTime (&cRecurrenceStart, pTimezone); + /*const int64_t nAppointmentEnd = cRecurringAppointment.end.to_unix (); + QDateTime cRecurrenceEnd = QDateTime::fromSecsSinceEpoch (nAppointmentEnd); + const qint64 nRecurrenceStart = cRecurrenceStart.toSecsSinceEpoch (); + QDateTime cIncidenceStart = pIncidence->dtStart (); + const qint64 nIncidenceStart = cIncidenceStart.toSecsSinceEpoch (); + cRecurrenceEnd = cRecurrenceEnd.addSecs (nRecurrenceStart - nIncidenceStart);*/ + const QDateTime cRecurrenceEnd = pIncidence->endDateForStart (cRecurrenceStart); + cRecurringAppointment.end = datetimeFromQDateTime (&cRecurrenceEnd, pTimezone); + lAppointments.push_back (cRecurringAppointment); + qDebug () << "Recurrence from " << cRecurringAppointment.begin.format ("%F %T %z").c_str () << " to " << cRecurringAppointment.end.format ("%F %T %z").c_str () << ":" << cRecurringAppointment.summary.c_str (); + cRecurrenceStart = pRecurrence->getNextDateTime (cRecurrenceStart); + bRecurrenceValid = cRecurrenceStart.isValid (); + } + } + else + { + lAppointments.push_back (cAppointment); + qDebug () << "Event from " << cAppointment.begin.format ("%F %T %z").c_str () << " to " << cAppointment.end.format ("%F %T %z").c_str () << ":" << cAppointment.summary.c_str (); + + } + + pAppointment = &lAppointments.back (); + } + + // Generate alarms + KCalendarCore::Alarm::List lAlarms = pIncidence->alarms (); + std::map<std::pair<DateTime, DateTime>, std::map<DateTime, Alarm>> dAlarms; + + // Walk the alarms and add them + for (KCalendarCore::Alarm::Ptr pAlarm : lAlarms) + { + // we only care about AUDIO or DISPLAY alarms, other kind of alarm will not generate a notification + bool bAlarmInteresting = isAlarmInteresting (pAlarm); + + if (bAlarmInteresting) + { + const int nRepeat = pAlarm->repeatCount (); + const KCalendarCore::Duration cSnoozeTime = pAlarm->snoozeTime (); + const KCalendarCore::Duration cStartOffset = pAlarm->startOffset (); + const int nStartOffset = cStartOffset.asSeconds (); + + if (nRepeat && cSnoozeTime) + { + const int nSnoozeTime = cSnoozeTime.asSeconds (); + + for (int nIter = 0; nIter < nRepeat + 1; nIter++) + { + QDateTime cStartDateTime = pIncidence->dtStart (); + QDateTime cTriggerTime = cStartDateTime.addSecs (nStartOffset + (nIter * nSnoozeTime)); + const Qt::TimeSpec cTimeSpec = cTriggerTime.timeSpec (); + + if (cTimeSpec != Qt::LocalTime) + { + cTriggerTime = cTriggerTime.toTimeZone (*pTimezone); + } + + this->addAlarm (pAlarm, &dAlarms, &cTriggerTime, pTimezone, pIncidence, bAlarm); + } + } + else + { + QDateTime cTriggerTime = (*pDateTimeBegin).addSecs (nStartOffset); + cTriggerTime = pAlarm->nextRepetition (cTriggerTime); + bool bValid = cTriggerTime.isValid (); + + while (bValid && cTriggerTime <= *pDateTimeEnd) + { + const Qt::TimeSpec cTimeSpec = cTriggerTime.timeSpec (); + + if (cTimeSpec != Qt::LocalTime) + { + cTriggerTime = cTriggerTime.toTimeZone (*pTimezone); + } + + this->addAlarm (pAlarm, &dAlarms, &cTriggerTime, pTimezone, pIncidence, bAlarm); + cTriggerTime = pAlarm->nextRepetition (cTriggerTime); + bValid = cTriggerTime.isValid (); + } + } + } + } + + for (auto &cAlarm : dAlarms) + { + if (bAlarm) + { + pAppointment = new Appointment (cAppointment); + pAppointment->begin = cAlarm.first.first; + pAppointment->end = cAlarm.first.second; + } + + int nAlarms = cAlarm.second.size (); + pAppointment->alarms.reserve (nAlarms); + + for (auto &cAlarmSecond : cAlarm.second) + { + bool bText = cAlarmSecond.second.has_text (); + bool bSound = cAlarmSecond.second.has_sound (); + + if (bText || bSound) + { + pAppointment->alarms.push_back (cAlarmSecond.second); + qDebug () << "Alarm at " << cAlarmSecond.second.time.format ("%F %T %z").c_str (); + } + } + + if (bAlarm) + { + lAppointments.push_back (*pAppointment); + qDebug () << "Alarm from " << (*pAppointment).begin.format ("%F %T %z").c_str () << " to " << (*pAppointment).end.format ("%F %T %z").c_str () << ":" << (*pAppointment).summary.c_str (); + delete pAppointment; + } + } + } + } + else + { + qWarning () << "Panic: incidence has no UID" << pIncidence; + } + } + + // Give the caller the sorted finished product + auto &pAppointments = lAppointments; + std::sort (pAppointments.begin (), pAppointments.end (), [](const Appointment &pAppointment1, const Appointment &pAppointment2){return pAppointment1.begin < pAppointment2.begin;}); + pOnAppointments (pAppointments); + + qDebug () << "Returning appointments: " << lAppointments.size (); + + pStoragePtr.clear (); + } + + private: + + static void onDatabaseChanged (GFileMonitor *pMonitor, GFile *pFile, GFile *OtherFile, GFileMonitorEvent eEvent, gpointer pData) + { + auto pSelf = static_cast<Impl*> (pData); + + if (eEvent == G_FILE_MONITOR_EVENT_CHANGED) + { + pSelf->setDirtySoon (); + } + } + + void setDirtyNow () + { + this->pChanged (); + } + + static gboolean setDirtyNowStatic (gpointer pSelfPtr) + { + auto pSelf = static_cast<Impl*> (pSelfPtr); + pSelf->nRebuildTag = 0; + pSelf->nRebuildDeadline = 0; + pSelf->setDirtyNow (); + + return G_SOURCE_REMOVE; + } + + void setDirtySoon () + { + static constexpr int MIN_BATCH_SEC = 1; + static constexpr int MAX_BATCH_SEC = 60; + static_assert (MIN_BATCH_SEC <= MAX_BATCH_SEC, "bad boundaries"); + const auto nNow = time (nullptr); + + // First pass + if (this->nRebuildDeadline == 0) + { + this->nRebuildDeadline = nNow + MAX_BATCH_SEC; + this->nRebuildTag = g_timeout_add_seconds (MIN_BATCH_SEC, setDirtyNowStatic, this); + } + else if (nNow < this->nRebuildDeadline) + { + g_source_remove (this->nRebuildTag); + this->nRebuildTag = g_timeout_add_seconds (MIN_BATCH_SEC, setDirtyNowStatic, this); + } + } + + static bool isAlarmInteresting (KCalendarCore::Alarm::Ptr pAlarm) + { + const KCalendarCore::Alarm::Type eType = pAlarm->type (); + + if ((eType == KCalendarCore::Alarm::Type::Audio) || (eType == KCalendarCore::Alarm::Type::Display)) + { + // We don't want disabled alarms + const bool bEnabled = pAlarm->enabled (); + + return bEnabled; + } + + return false; + } + + static DateTime datetimeFromQDateTime (const QDateTime *pDateTime, QTimeZone *pTimeZone) + { + DateTime cDateTimeOut = {}; + bool bValid = pDateTime->isValid (); + + if (!bValid) + { + return cDateTimeOut; + } + + const QByteArray sId = pTimeZone->id (); + const char *sIdData = sId.constData (); + GTimeZone *pGTimeZone = g_time_zone_new_identifier (sIdData); + const QDate cDate = pDateTime->date (); + const QTime cTime = pDateTime->time (); + const int nYear = cDate.year (); + const int nMonth = cDate.month (); + const int nDay = cDate.day (); + const int nHour = cTime.hour (); + const int nMinute = cTime.minute (); + const int nSecond = cTime.second (); + cDateTimeOut = DateTime (pGTimeZone, nYear, nMonth, nDay, nHour, nMinute, nSecond); + g_time_zone_unref (pGTimeZone); + + return cDateTimeOut; + } + + bool isIncidenceInteresting (KCalendarCore::Incidence::Ptr pIncidence) + { + // We only want calendar events and todos + const KCalendarCore::IncidenceBase::IncidenceType eType = pIncidence->type (); + + if ((eType != KCalendarCore::IncidenceBase::IncidenceType::TypeEvent) && (eType != KCalendarCore::IncidenceBase::IncidenceType::TypeTodo)) + { + return false; + } + + // We're not interested in completed or cancelled incidences + const KCalendarCore::Incidence::Status eIncidenceStatus = pIncidence->status (); + + if ((eIncidenceStatus == KCalendarCore::Incidence::Status::StatusCompleted) || (eIncidenceStatus == KCalendarCore::Incidence::Status::StatusCanceled)) + { + return false; + } + + // We don't want not attending alarms + const KCalendarCore::Attendee::List lAttendees = pIncidence->attendees (); + + for (KCalendarCore::Attendee cAttendee : lAttendees) + { + const QString sEmail = cAttendee.email (); + const std::string sEmailStd = sEmail.toStdString (); + bool bMyEmail = this->pMyself->isMyEmail (sEmailStd); + + // Check if the user is part of the attendee list + if (bMyEmail) + { + // Check the status + const KCalendarCore::Attendee::PartStat eAttendeeStatus = cAttendee.status (); + bool bDeclined = (eAttendeeStatus == KCalendarCore::Attendee::PartStat::Declined); + + return !bDeclined; + } + } + + return true; + } + + static Appointment getAppointment (KCalendarCore::Incidence::Ptr pIncidence, QTimeZone *pTimeZone, bool bAlarm, std::string sColor) + { + Appointment cAppointment; + + // Get Appointment.uid + const QString sUid = pIncidence->uid (); + cAppointment.uid = sUid.toStdString (); + + // Get Appointment.summary + const QString sSummary = pIncidence->summary (); + cAppointment.summary = sSummary.toStdString (); + + // Get Appointment.begin + QDateTime cBegin = pIncidence->dtStart (); + const Qt::TimeSpec cTimeSpecBegin = cBegin.timeSpec (); + + if (cTimeSpecBegin != Qt::LocalTime) + { + cBegin = cBegin.toTimeZone (*pTimeZone); + } + + cAppointment.begin = datetimeFromQDateTime (&cBegin, pTimeZone); + + // Get Appointment.end + QDateTime cEnd = {}; + const KCalendarCore::IncidenceBase::IncidenceType eType = pIncidence->type (); + + if (eType == KCalendarCore::IncidenceBase::IncidenceType::TypeEvent) + { + KCalendarCore::Event::Ptr pEvent = qSharedPointerCast<KCalendarCore::Event> (pIncidence); + cEnd = pEvent->dtEnd (); + const Qt::TimeSpec cTimeSpecEnd = cEnd.timeSpec (); + + if (cTimeSpecEnd != Qt::LocalTime) + { + cEnd = cEnd.toTimeZone (*pTimeZone); + } + + // Check for all day event + bool bHasEndDate = pEvent->hasEndDate (); + bool bAllDay = pIncidence->allDay (); + + if (!bHasEndDate && bAllDay) + { + cEnd = cBegin.addDays (1); + } + } + + const bool bValid = cEnd.isValid (); + + if (bValid) + { + cAppointment.end = datetimeFromQDateTime (&cEnd, pTimeZone); + } + else + { + cAppointment.end = cAppointment.begin; + } + + // Get Appointment.type + if (bAlarm) + { + cAppointment.type = Appointment::ALARM; + } + else + { + cAppointment.type = Appointment::EVENT; + } + + // Get Appointment.color + cAppointment.color = sColor; + + return cAppointment; + } + + core::Signal<> pChanged; + guint nRebuildTag {}; + time_t nRebuildDeadline {}; + std::shared_ptr<Myself> pMyself; + GFileMonitor *pMonitor; + }; + + EdsEngine::EdsEngine (const std::shared_ptr<Myself> &myself): p (new Impl (myself)) + { + } + + EdsEngine::~EdsEngine () = default; + + core::Signal<>& EdsEngine::changed () + { + return this->p->changed (); + } + + void EdsEngine::get_appointments (const DateTime &pDateTimeBegin, const DateTime &pDateTimeEnd, const Timezone &pTimezone, std::function<void (const std::vector<Appointment>&)> pFunc) + { + qint64 nDateTimeBegin = pDateTimeBegin.to_unix (); + QDateTime cDateTimeBegin = QDateTime::fromSecsSinceEpoch (nDateTimeBegin, Qt::UTC); + qint64 nDateTimeEnd = pDateTimeEnd.to_unix (); + QDateTime cDateTimeEnd = QDateTime::fromSecsSinceEpoch (nDateTimeEnd, Qt::UTC); + QTimeZone cTimeZone = QTimeZone (); + std::string sTimeZone = pTimezone.timezone.get (); + const char *sTimeZoneData = sTimeZone.c_str (); + QTimeZone cTimezone = QTimeZone (sTimeZoneData); + this->p->getAppointments (&cDateTimeBegin, &cDateTimeEnd, &cTimezone, pFunc); + } + + void EdsEngine::disable_alarm (const Appointment &pAppointment) + { + bool bAlarm = pAppointment.is_alarm (); + + if (bAlarm) + { + QTimeZone cSystemTimeZone = QTimeZone::systemTimeZone (); + mKCal::ExtendedCalendar *pCalendar = new mKCal::ExtendedCalendar (cSystemTimeZone); + mKCal::ExtendedCalendar::Ptr pCalendarPtr = mKCal::ExtendedCalendar::Ptr (pCalendar); + mKCal::ExtendedStorage::Ptr pStoragePtr = mKCal::ExtendedCalendar::defaultStorage (pCalendarPtr); + pStoragePtr->open (); + const char *sUid = pAppointment.uid.c_str (); + pStoragePtr->load (sUid); + KCalendarCore::Incidence::List lIncidences = pCalendarPtr->incidences (); + bool bChanged = false; + + for (KCalendarCore::Incidence::Ptr pIncidence : lIncidences) + { + KCalendarCore::Alarm::List lAlarms = pIncidence->alarms (); + + for (KCalendarCore::Alarm::Ptr pAlarm : lAlarms) + { + const bool bEnabled = pAlarm->enabled (); + + if (bEnabled) + { + pAlarm->setEnabled (false); + bChanged = true; + } + } + } + + if (bChanged) + { + pStoragePtr->save (); + } + + pStoragePtr.clear (); + } + } + } + } +} diff --git a/src/exporter.cpp b/src/exporter.cpp index ea514c0..41a1583 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -1,5 +1,6 @@ /* * Copyright 2013 Canonical Ltd. + * Copyright 2021 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/dbus-shared.h> @@ -37,7 +39,7 @@ class Exporter::Impl { public: - Impl(const std::shared_ptr<Settings>& settings): + explicit Impl(const std::shared_ptr<Settings>& settings): m_settings(settings), m_alarm_props(datetime_alarm_properties_skeleton_new()) { @@ -190,10 +192,10 @@ private: for(auto& menu : m_menus) { const auto path = std::string(BUS_DATETIME_PATH) + "/" + menu->name(); - const auto id = g_dbus_connection_export_menu_model(m_bus, path.c_str(), menu->menu_model(), &error); - if (id) + const auto nId = g_dbus_connection_export_menu_model(m_bus, path.c_str(), menu->menu_model(), &error); + if (nId) { - m_exported_menu_ids.insert(id); + m_exported_menu_ids.insert(nId); } else { diff --git a/src/haptic.cpp b/src/haptic.cpp index 7e09c24..2b0af4c 100644 --- a/src/haptic.cpp +++ b/src/haptic.cpp @@ -1,6 +1,6 @@ /* * Copyright 2014 Canonical Ltd. - * Copyright 2021 Robert Tari + * Copyright 2021-2022 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -39,11 +39,11 @@ class Haptic::Impl { public: - Impl(bool repeat): + explicit Impl(bool repeat): m_cancellable(g_cancellable_new()), m_repeat(repeat) { - g_bus_get (G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready, this); + g_bus_get (G_BUS_TYPE_SYSTEM, m_cancellable, on_bus_ready, this); } ~Impl() @@ -105,20 +105,14 @@ private: void call_vibrate() { - GVariantBuilder builder; - auto duration = g_variant_new_int32 (1000); - - g_variant_builder_init (&builder, G_VARIANT_TYPE_INT32); - g_variant_builder_add_value (&builder, duration); - - auto vibrate_arg = g_variant_builder_end (&builder); + auto duration = g_variant_new ("(i)", 1000); g_dbus_connection_call (m_bus, BUS_HAPTIC_NAME, BUS_HAPTIC_PATH, BUS_HAPTIC_INTERFACE, "vibrate", - vibrate_arg, + duration, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index f1e2b42..2673e66 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -1,5 +1,6 @@ /* * Copyright 2013 Canonical Ltd. + * Copyright 2021 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/locations-settings.h> @@ -59,10 +61,10 @@ SettingsLocations::reload() } // add the other detected timezones - for(const auto& zone : m_timezones->timezones.get()) + for(const auto& sZone : m_timezones->timezones.get()) { - gchar * name = get_beautified_timezone_name(zone.c_str(), timezone_name.c_str()); - Location l(zone, name); + gchar * name = get_beautified_timezone_name(sZone.c_str(), timezone_name.c_str()); + Location l(sZone, name); if (std::find(v.begin(), v.end(), l) == v.end()) v.push_back(l); g_free(name); @@ -73,14 +75,14 @@ SettingsLocations::reload() { for(const auto& locstr : m_settings->locations.get()) { - gchar* zone; + gchar* sZone; gchar* name; - split_settings_location(locstr.c_str(), &zone, &name); - Location loc(zone, name); + split_settings_location(locstr.c_str(), &sZone, &name); + Location loc(sZone, name); if (std::find(v.begin(), v.end(), loc) == v.end()) v.push_back(loc); g_free(name); - g_free(zone); + g_free(sZone); } } diff --git a/src/main.cpp b/src/main.cpp index 0dc7198..29e4472 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,9 +32,7 @@ #include <datetime/planner-snooze.h> #include <datetime/planner-range.h> #include <datetime/settings-live.h> -#ifdef HAS_LOMIRI_SCHEMAS #include <datetime/snap.h> -#endif #include <datetime/state.h> #include <datetime/timezones-live.h> #include <datetime/timezone-timedated.h> @@ -95,7 +93,6 @@ namespace return state; } -#ifdef HAS_LOMIRI_SCHEMAS std::shared_ptr<AlarmQueue> create_simple_alarm_queue(const std::shared_ptr<Clock>& clock, const std::shared_ptr<Planner>& snooze_planner, const std::shared_ptr<Engine>& engine, @@ -119,7 +116,6 @@ namespace auto wakeup_timer = std::make_shared<PowerdWakeupTimer>(clock); return std::make_shared<SimpleAlarmQueue>(clock, planner, wakeup_timer); } -#endif } int @@ -149,7 +145,6 @@ main(int /*argc*/, char** /*argv*/) auto actions = std::make_shared<LiveActions>(state); MenuFactory factory(actions, state); -#ifdef HAS_LOMIRI_SCHEMAS // set up the snap decisions auto snooze_planner = std::make_shared<SnoozePlanner>(state->settings, state->clock); auto notification_engine = std::make_shared<ain::Engine>("ayatana-indicator-datetime-service"); @@ -173,7 +168,6 @@ main(int /*argc*/, char** /*argv*/) engine->disable_alarm(appointment); }; alarm_queue->alarm_reached().connect(on_alarm_reached); -#endif // create the menus std::vector<std::shared_ptr<Menu>> menus; diff --git a/src/menu.cpp b/src/menu.cpp index ab0bd71..b8ef1b6 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -72,13 +72,14 @@ GMenuModel* Menu::menu_model() std::vector<Appointment> Menu::get_display_appointments(const std::vector<Appointment>& appointments_in, const DateTime& now, - unsigned int max_items) + unsigned int max_items, + const bool include_alarms) { std::vector<Appointment> appointments; std::copy_if(appointments_in.begin(), appointments_in.end(), std::back_inserter(appointments), - [now](const Appointment& a){return a.end >= now;}); + [now, include_alarms](const Appointment& a){return a.end >= now && (!a.is_alarm() || include_alarms);}); if (appointments.size() > max_items) { @@ -107,10 +108,6 @@ Menu::get_display_appointments(const std::vector<Appointment>& appointments_in, if (a_full_day_today != b_full_day_today) return a_full_day_today; - const bool a_after_today = (a.begin > end_of_day) || (a.end > end_of_day); - const bool b_after_today = (a.begin > end_of_day) || (a.end > end_of_day); - if (a_after_today != b_after_today) - return a_after_today; if (a.begin != b.begin) return a.begin < b.begin; if (b.end != b.end) @@ -190,6 +187,9 @@ protected: m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); + m_state->settings->show_alarms.changed().connect([this](bool){ + update_section(Appointments); // showing alarms got toggled + }); m_state->calendar_upcoming->date().changed().connect([this](const DateTime&){ update_upcoming(); // our m_upcoming is planner->upcoming() filtered by time }); @@ -244,7 +244,9 @@ protected: auto upcoming = get_display_appointments( m_state->calendar_upcoming->appointments().get(), - begin + begin, + 5, + m_state->settings->show_alarms.get() ); if (m_upcoming != upcoming) @@ -272,10 +274,6 @@ protected: return m_serialized_alarm_icon; } - std::vector<Appointment> m_upcoming; - -private: - GVariant* get_serialized_calendar_icon() { if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) @@ -288,6 +286,10 @@ private: return m_serialized_calendar_icon; } + std::vector<Appointment> m_upcoming; + +private: + void create_gmenu() { g_assert(m_submenu == nullptr); @@ -432,29 +434,32 @@ private: { auto menu = g_menu_new(); - if ((profile==Desktop) && m_state->settings->show_events.get()) + if (m_state->settings->show_events.get()) { - add_appointments (menu, profile); - - if (m_actions->desktop_has_calendar_app()) + if (profile==Desktop) { - // add the 'Add Event…' menuitem - auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); - const gchar* action_name = "indicator.desktop.open-calendar-app"; - auto v = g_variant_new_int64(0); - g_menu_item_set_action_and_target_value(menu_item, action_name, v); - g_menu_append_item(menu, menu_item); - g_object_unref(menu_item); + add_appointments (menu, profile); + + if (m_actions->desktop_has_calendar_app()) + { + // add the 'Add Event…' menuitem + auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); + const gchar* action_name = "indicator.desktop.open-calendar-app"; + auto v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value(menu_item, action_name, v); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + } } - } - else if (profile==Phone) - { - auto menu_item = g_menu_item_new (_("Clock"), "indicator.phone.open-alarm-app"); - g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); - g_menu_append_item (menu, menu_item); - g_object_unref (menu_item); + else if (profile==Phone) + { + auto menu_item = g_menu_item_new (_("Clock"), "indicator.phone.open-alarm-app"); + g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); - add_appointments (menu, profile); + add_appointments (menu, profile); + } } return G_MENU_MODEL(menu); @@ -554,7 +559,7 @@ protected: update_header(); } - GVariant* create_header_state() + GVariant* create_header_state() override { const auto title = _("Date and Time"); auto label = g_variant_new_string(m_formatter->header.get().c_str()); @@ -565,6 +570,7 @@ protected: g_variant_builder_add(&b, "{sv}", "label", label); g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(title)); g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(TRUE)); + g_variant_builder_add(&b, "{sv}", "tooltip", g_variant_new_string (_("Time & date settings, quick calendar access"))); return g_variant_builder_end(&b); } }; @@ -597,25 +603,46 @@ protected: update_header(); } - GVariant* create_header_state() + GVariant* create_header_state() override { // are there alarms? bool has_alarms = false; + bool has_non_alarm_events = false; for(const auto& appointment : m_upcoming) - if((has_alarms = appointment.is_alarm())) + { + has_alarms = appointment.is_alarm(); + + if (has_alarms) + { break; + } + else + { + has_non_alarm_events = true; + } + } + GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string (_("Time and Date"))); g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); - if (has_alarms) + if (has_alarms || has_non_alarm_events) + { auto label = m_formatter->header.get(); - auto a11y = g_strdup_printf(_("%s (has alarms)"), label.c_str()); + auto a11y = g_strdup_printf(_("%s (has events)"), label.c_str()); g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(label.c_str())); g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_take_string(a11y)); - g_variant_builder_add(&b, "{sv}", "icon", get_serialized_alarm_icon()); + + if (has_alarms && m_state->settings->show_alarms.get()) + { + g_variant_builder_add(&b, "{sv}", "icon", get_serialized_alarm_icon()); + } + else + { + g_variant_builder_add(&b, "{sv}", "icon", get_serialized_calendar_icon()); + } } else { diff --git a/src/notifications.cpp b/src/notifications.cpp index f21b5e8..c7b20c8 100644 --- a/src/notifications.cpp +++ b/src/notifications.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Canonical Ltd. + * Copyright 2021-2025 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <notifications/notifications.h> @@ -24,7 +26,7 @@ #include <messaging-menu/messaging-menu-app.h> #include <messaging-menu/messaging-menu-message.h> -#ifdef HAS_URLDISPATCHER +#ifdef LOMIRI_FEATURES_ENABLED #include <lomiri-url-dispatcher.h> #endif @@ -163,7 +165,7 @@ class Engine::Impl public: - Impl(const std::string& app_name): + explicit Impl(const std::string& app_name): m_app_name(app_name) { if (!notify_init(app_name.c_str())) @@ -197,6 +199,13 @@ public: return server_caps().count("actions") != 0; } +#ifdef LOMIRI_FEATURES_ENABLED + bool requires_hint_lomiri_timeout() const + { + return server_caps().count(HINT_LOMIRI_TIMEOUT) != 0; + } +#endif + void close_all () { // call close() on all our keys @@ -242,14 +251,25 @@ public: } ); +#ifdef LIBNOTIFY_HAS_SET_APP_ICON + NotifyNotification *pNotification = nn.get (); + const char *sIcon = info.m_icon_name.c_str (); + notify_notification_set_app_icon (pNotification, sIcon); +#endif + if (info.m_duration.count() != 0) { const auto& d= info.m_duration; auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d); - - notify_notification_set_hint (nn.get(), - HINT_TIMEOUT, - g_variant_new_int32(ms.count())); + notify_notification_set_timeout (nn.get (), ms.count ()); +#ifdef LOMIRI_FEATURES_ENABLED + if (requires_hint_lomiri_timeout()) { + // Lomiri has its own logic regarding timeout. + notify_notification_set_hint (nn.get(), + HINT_LOMIRI_TIMEOUT, + g_variant_new_int32(ms.count())); + } +#endif } for (const auto& hint : info.m_string_hints) @@ -462,7 +482,7 @@ private: static std::string calendar_app_id() { -#ifdef HAS_URLDISPATCHER +#ifdef LOMIRI_FEATURES_ENABLED auto urls = g_strsplit("calendar://", ",", 0); auto appids = lomiri_url_dispatch_url_appid(const_cast<const gchar**>(urls)); g_strfreev(urls); @@ -511,7 +531,9 @@ private: // as the name indicates, don't use this directly: use server_caps() instead mutable std::set<std::string> m_lazy_caps; - static constexpr char const * HINT_TIMEOUT {"x-lomiri-snap-decisions-timeout"}; +#ifdef LOMIRI_FEATURES_ENABLED + static constexpr char const * HINT_LOMIRI_TIMEOUT {"x-lomiri-snap-decisions-timeout"}; +#endif }; /*** diff --git a/src/planner-aggregate.cpp b/src/planner-aggregate.cpp index 23a1230..3b05ea1 100644 --- a/src/planner-aggregate.cpp +++ b/src/planner-aggregate.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Canonical Ltd. + * Copyright 2021 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/planner-aggregate.h> @@ -30,7 +32,7 @@ namespace datetime { class AggregatePlanner::Impl { public: - Impl(AggregatePlanner* owner): + explicit Impl(AggregatePlanner* owner): m_owner(owner) { } diff --git a/src/planner-snooze.cpp b/src/planner-snooze.cpp index b81c912..3cbd740 100644 --- a/src/planner-snooze.cpp +++ b/src/planner-snooze.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Canonical Ltd. + * Copyright 2024 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,12 +16,11 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/planner-snooze.h> -#include <libedataserver/libedataserver.h> // e_uid_new() - namespace ayatana { namespace indicator { namespace datetime { @@ -67,7 +67,7 @@ public: appt.alarms[0].time += offset; // give it a new ID - gchar* uid = e_uid_new(); + gchar* uid = g_uuid_string_random (); appt.uid = uid; g_free(uid); diff --git a/src/settings-live.cpp b/src/settings-live.cpp index cf18739..9cbc23c 100644 --- a/src/settings-live.cpp +++ b/src/settings-live.cpp @@ -47,6 +47,7 @@ LiveSettings::LiveSettings(): update_show_day(); update_show_detected_locations(); update_show_events(); + update_show_alarms(); update_show_locations(); update_show_seconds(); update_show_week_numbers(); @@ -117,6 +118,10 @@ LiveSettings::LiveSettings(): g_settings_set_boolean(m_settings, SETTINGS_SHOW_EVENTS_S, value); }); + show_alarms.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_ALARMS_S, value); + }); + show_locations.changed().connect([this](bool value){ g_settings_set_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S, value); }); @@ -239,6 +244,12 @@ void LiveSettings::update_show_events() show_events.set(val); } +void LiveSettings::update_show_alarms() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_ALARMS_S); + show_alarms.set(val); +} + void LiveSettings::update_show_locations() { const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S); @@ -415,6 +426,8 @@ void LiveSettings::update_key_ccid(const std::string& key) update_show_week_numbers(); else if (key == SETTINGS_SHOW_EVENTS_S) update_show_events(); + else if (key == SETTINGS_SHOW_ALARMS_S) + update_show_alarms(); else if (key == SETTINGS_SHOW_LOCATIONS_S) update_show_locations(); else if (key == SETTINGS_SHOW_DETECTED_S) diff --git a/src/snap.cpp b/src/snap.cpp index e306dea..c18f955 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -19,8 +19,9 @@ * Robert Tari <robert@tari.in> */ -#ifdef HAS_LOMIRI_SCHEMAS +#ifdef LOMIRI_FEATURES_ENABLED #include "dbus-accounts-sound.h" +#endif #include <datetime/snap.h> #include <datetime/utils.h> // is_locale_12h() @@ -64,6 +65,7 @@ public: m_cancellable(g_cancellable_new()), m_system_bus{G_DBUS_CONNECTION(g_object_ref(system_bus))} { + #ifdef LOMIRI_FEATURES_ENABLED auto object_path = g_strdup_printf("/org/freedesktop/Accounts/User%lu", (gulong)getuid()); @@ -75,13 +77,16 @@ public: on_sound_proxy_ready, this); g_free(object_path); + #endif } ~Impl() { g_cancellable_cancel(m_cancellable); g_clear_object(&m_cancellable); + #ifdef LOMIRI_FEATURES_ENABLED g_clear_object(&m_accounts_service_sound_proxy); + #endif g_clear_object(&m_system_bus); for (const auto& key : m_notifications) @@ -235,6 +240,7 @@ private: return m_settings->vibrate_silent_mode.get(); } +#ifdef LOMIRI_FEATURES_ENABLED static void on_sound_proxy_ready(GObject* /*source_object*/, GAsyncResult* res, gpointer gself) { GError * error; @@ -253,17 +259,26 @@ private: static_cast<Impl*>(gself)->m_accounts_service_sound_proxy = proxy; } } +#endif bool silent_mode() const { +#ifdef LOMIRI_FEATURES_ENABLED return (m_accounts_service_sound_proxy != nullptr) && (accounts_service_sound_get_silent_mode(m_accounts_service_sound_proxy)); +#else + return false; +#endif } bool should_vibrate() const { +#ifdef LOMIRI_FEATURES_ENABLED return (m_accounts_service_sound_proxy != nullptr) && (accounts_service_sound_get_other_vibrate(m_accounts_service_sound_proxy)); +#else + return true; +#endif } std::string get_alarm_uri(const Appointment& appointment, @@ -306,7 +321,9 @@ private: const std::shared_ptr<const Settings> m_settings; std::set<int> m_notifications; GCancellable * m_cancellable {nullptr}; +#ifdef LOMIRI_FEATURES_ENABLED AccountsServiceSound * m_accounts_service_sound_proxy {nullptr}; +#endif GDBusConnection * m_system_bus {nullptr}; static constexpr char const * ACTION_NONE {"none"}; @@ -345,5 +362,3 @@ Snap::operator()(const Appointment& appointment, } // namespace datetime } // namespace indicator } // namespace ayatana - -#endif diff --git a/src/wakeup-timer-mainloop.cpp b/src/wakeup-timer-mainloop.cpp index 56961f2..738306c 100644 --- a/src/wakeup-timer-mainloop.cpp +++ b/src/wakeup-timer-mainloop.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Canonical Ltd. + * Copyright 2021 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/wakeup-timer-mainloop.h> @@ -36,7 +38,7 @@ class MainloopWakeupTimer::Impl public: - Impl(const std::shared_ptr<Clock>& clock): + explicit Impl(const std::shared_ptr<Clock>& clock): m_clock(clock) { } diff --git a/src/wakeup-timer-powerd.cpp b/src/wakeup-timer-powerd.cpp index e66c94c..77893c8 100644 --- a/src/wakeup-timer-powerd.cpp +++ b/src/wakeup-timer-powerd.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Canonical Ltd. + * Copyright 2021-2022 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Charles Kerr <charles.kerr@canonical.com> + * Robert Tari <robert@tari.in> */ #include <datetime/clock.h> @@ -38,8 +40,7 @@ class PowerdWakeupTimer::Impl { public: - Impl(const std::shared_ptr<Clock>& clock): - m_clock(clock), + explicit Impl(const std::shared_ptr<Clock>& clock): m_cancellable(g_cancellable_new()) { g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_bus_ready, this); @@ -269,7 +270,6 @@ private: ***/ core::Signal<> m_timeout; - const std::shared_ptr<Clock>& m_clock; DateTime m_wakeup_time; std::shared_ptr<GDBusConnection> m_bus; |