aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt47
-rw-r--r--src/actions.cpp27
-rw-r--r--src/engine-eds.cpp65
-rw-r--r--src/engine-mkcal.cpp632
-rw-r--r--src/exporter.cpp10
-rw-r--r--src/haptic.cpp16
-rw-r--r--src/locations-settings.cpp16
-rw-r--r--src/main.cpp6
-rw-r--r--src/menu.cpp99
-rw-r--r--src/notifications.cpp38
-rw-r--r--src/planner-aggregate.cpp4
-rw-r--r--src/planner-snooze.cpp6
-rw-r--r--src/settings-live.cpp13
-rw-r--r--src/snap.cpp21
-rw-r--r--src/wakeup-timer-mainloop.cpp4
-rw-r--r--src/wakeup-timer-powerd.cpp6
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;