~stgraber/indicator-datetime/fix-deps

« back to all changes in this revision

Viewing changes to src/engine-eds.cpp

Improve valarm support to honor calendar events' valarm triggers. Fixes: #1419001
Approved by: Ted Gould, PS Jenkins bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
#include <libedataserver/libedataserver.h>
26
26
 
27
27
#include <algorithm> // std::sort()
 
28
#include <array>
28
29
#include <ctime> // time()
29
30
#include <map>
30
31
#include <set>
126
127
            auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR);
127
128
            const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension));
128
129
            g_debug("calling e_cal_client_generate_instances for %p", (void*)client);
 
130
            auto subtask = new AppointmentSubtask(main_task,
 
131
                                                  client,
 
132
                                                  color,
 
133
                                                  default_timezone,
 
134
                                                  begin_timet,
 
135
                                                  end_timet);
129
136
            e_cal_client_generate_instances(client,
130
137
                                            begin_timet,
131
138
                                            end_timet,
132
139
                                            m_cancellable,
133
140
                                            my_get_appointments_foreach,
134
 
                                            new AppointmentSubtask (main_task, client, color),
 
141
                                            subtask,
135
142
                                            [](gpointer g){delete static_cast<AppointmentSubtask*>(g);});
136
143
        }
137
144
    }
409
416
        std::shared_ptr<Task> task;
410
417
        ECalClient* client;
411
418
        std::string color;
412
 
        AppointmentSubtask(const std::shared_ptr<Task>& task_in, ECalClient* client_in, const char* color_in):
413
 
            task(task_in), client(client_in)
 
419
        icaltimezone* default_timezone;
 
420
        time_t begin;
 
421
        time_t end;
 
422
 
 
423
        AppointmentSubtask(const std::shared_ptr<Task>& task_in,
 
424
                           ECalClient* client_in,
 
425
                           const char* color_in,
 
426
                           icaltimezone* default_tz,
 
427
                           time_t begin_,
 
428
                           time_t end_):
 
429
            task(task_in),
 
430
            client(client_in),
 
431
            default_timezone(default_tz),
 
432
            begin(begin_),
 
433
            end(end_)
414
434
        {
415
435
            if (color_in)
416
436
                color = color_in;
417
437
        }
418
438
    };
419
439
 
 
440
    static std::string get_alarm_text(ECalComponentAlarm * alarm)
 
441
    {
 
442
        std::string ret;
 
443
 
 
444
        ECalComponentAlarmAction action;
 
445
        e_cal_component_alarm_get_action(alarm, &action);
 
446
        if (action == E_CAL_COMPONENT_ALARM_DISPLAY)
 
447
        {
 
448
            ECalComponentText text {};
 
449
            e_cal_component_alarm_get_description(alarm, &text);
 
450
            if (text.value)
 
451
                ret = text.value;
 
452
        }
 
453
 
 
454
        return ret;
 
455
    }
 
456
 
 
457
    static std::string get_alarm_sound_url(ECalComponentAlarm * alarm)
 
458
    {
 
459
        std::string ret;
 
460
 
 
461
        ECalComponentAlarmAction action;
 
462
        e_cal_component_alarm_get_action(alarm, &action);
 
463
        if (action == E_CAL_COMPONENT_ALARM_AUDIO)
 
464
        {
 
465
            icalattach* attach = nullptr;
 
466
            e_cal_component_alarm_get_attach(alarm, &attach);
 
467
            if (attach != nullptr)
 
468
            {
 
469
                if (icalattach_get_is_url (attach))
 
470
                {
 
471
                    const char* url = icalattach_get_url(attach);
 
472
                    if (url != nullptr)
 
473
                        ret = url;
 
474
                }
 
475
 
 
476
                icalattach_unref(attach);
 
477
            }
 
478
        }
 
479
 
 
480
        return ret;
 
481
    }
 
482
 
420
483
    static gboolean
421
484
    my_get_appointments_foreach(ECalComponent* component,
422
485
                                time_t         begin,
434
497
            auto status = ICAL_STATUS_NONE;
435
498
            e_cal_component_get_status(component, &status);
436
499
 
437
 
            const auto begin_dt = DateTime(begin);
438
 
            const auto end_dt = DateTime(end);
 
500
            // get the timezone we want to use for generated Appointments/Alarms
 
501
            const char * location = icaltimezone_get_location(subtask->default_timezone);
 
502
            auto gtz = g_time_zone_new(location);
 
503
            g_debug("timezone abbreviation is %s", g_time_zone_get_abbreviation (gtz, 0));
 
504
 
 
505
            const DateTime begin_dt { gtz, begin };
 
506
            const DateTime end_dt { gtz, end };
439
507
            g_debug ("got appointment from %s to %s, uid %s status %d",
440
 
                     begin_dt.format("%F %T").c_str(),
441
 
                     end_dt.format("%F %T").c_str(),
 
508
                     begin_dt.format("%F %T %z").c_str(),
 
509
                     end_dt.format("%F %T %z").c_str(),
442
510
                     uid,
443
511
                     (int)status);
444
512
 
461
529
                (status != ICAL_STATUS_COMPLETED) &&
462
530
                (status != ICAL_STATUS_CANCELLED))
463
531
            {
 
532
                constexpr std::array<ECalComponentAlarmAction,1> omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1
464
533
                Appointment appointment;
465
534
 
466
 
                ECalComponentText text;
467
 
                text.value = nullptr;
 
535
                ECalComponentText text {};
468
536
                e_cal_component_get_summary(component, &text);
469
537
                if (text.value)
470
538
                    appointment.summary = text.value;
475
543
                appointment.uid = uid;
476
544
                appointment.type = type;
477
545
 
478
 
                // Look through all of this component's alarms
479
 
                // for DISPLAY or AUDIO url attachments.
480
 
                // If we find any, use them for appointment.url and audio_sound
481
 
                auto alarm_uids = e_cal_component_get_alarm_uids(component);
482
 
                for(auto walk=alarm_uids; appointment.url.empty() && walk!=nullptr; walk=walk->next)
483
 
                {
484
 
                    auto alarm = e_cal_component_get_alarm(component, static_cast<const char*>(walk->data));
485
 
 
486
 
                    ECalComponentAlarmAction action;
487
 
                    e_cal_component_alarm_get_action(alarm, &action);
488
 
                    if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) || (action == E_CAL_COMPONENT_ALARM_AUDIO))
489
 
                    {
490
 
                        icalattach* attach = nullptr;
491
 
                        e_cal_component_alarm_get_attach(alarm, &attach);
492
 
                        if (attach != nullptr)
493
 
                        {
494
 
                            if (icalattach_get_is_url (attach))
495
 
                            {
496
 
                                const char* url = icalattach_get_url(attach);
497
 
                                if (url != nullptr)
498
 
                                {
499
 
                                    if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) && appointment.url.empty())
500
 
                                    {
501
 
                                        appointment.url = url;
502
 
                                    }
503
 
                                    else if ((action == E_CAL_COMPONENT_ALARM_AUDIO) && appointment.audio_url.empty())
504
 
                                    {
505
 
                                        appointment.audio_url = url;
506
 
                                    }
507
 
                                }
508
 
                            }
509
 
 
510
 
                            icalattach_unref(attach);
511
 
                        }
512
 
                    }
513
 
 
514
 
                    e_cal_component_alarm_free(alarm);
515
 
                }
516
 
                cal_obj_uid_list_free(alarm_uids);
517
 
 
518
 
                g_debug("adding appointment '%s' '%s'", appointment.summary.c_str(), appointment.url.c_str());
 
546
                icalcomponent * icc = e_cal_component_get_icalcomponent(component);
 
547
                g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak
 
548
 
 
549
                auto e_alarms = e_cal_util_generate_alarms_for_comp(component,
 
550
                                                                    subtask->begin,
 
551
                                                                    subtask->end,
 
552
                                                                    const_cast<ECalComponentAlarmAction*>(omit.data()),
 
553
                                                                    e_cal_client_resolve_tzid_cb,
 
554
                                                                    subtask->client,
 
555
                                                                    subtask->default_timezone);
 
556
 
 
557
                std::map<DateTime,Alarm> alarms;
 
558
 
 
559
                if (e_alarms != nullptr)
 
560
                {
 
561
                    for (auto l=e_alarms->alarms; l!=nullptr; l=l->next)
 
562
                    {
 
563
                        auto ai = static_cast<ECalComponentAlarmInstance*>(l->data);
 
564
                        auto a = e_cal_component_get_alarm(component, ai->auid);
 
565
 
 
566
                        if (a != nullptr)
 
567
                        {
 
568
                            const DateTime alarm_begin{gtz, ai->trigger};
 
569
                            auto& alarm = alarms[alarm_begin];
 
570
 
 
571
                            if (alarm.text.empty())
 
572
                                alarm.text = get_alarm_text(a);
 
573
                            if (alarm.audio_url.empty())
 
574
                                alarm.audio_url = get_alarm_sound_url(a);
 
575
                            if (!alarm.time.is_set())
 
576
                                alarm.time = alarm_begin;
 
577
 
 
578
                            e_cal_component_alarm_free(a);
 
579
                        }
 
580
                    }
 
581
 
 
582
                    e_cal_component_alarms_free(e_alarms);
 
583
                }
 
584
                // Hm, no alarm triggers? 
 
585
                // That's a bug in alarms created by some versions of ubuntu-ui-toolkit.
 
586
                // If that's what's happening here, let's handle those alarms anyway
 
587
                // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S
 
588
                else if (appointment.is_ubuntu_alarm())
 
589
                {
 
590
                    Alarm tmp;
 
591
                    tmp.time = appointment.begin;
 
592
 
 
593
                    auto auids = e_cal_component_get_alarm_uids(component);
 
594
                    for(auto l=auids; l!=nullptr; l=l->next)
 
595
                    {
 
596
                        const auto auid = static_cast<const char*>(l->data);
 
597
                        auto a = e_cal_component_get_alarm(component, auid);
 
598
                        if (a != nullptr)
 
599
                        {
 
600
                            if (tmp.text.empty())
 
601
                                tmp.text = get_alarm_text(a);
 
602
                            if (tmp.audio_url.empty())
 
603
                                tmp.audio_url = get_alarm_sound_url(a);
 
604
                            e_cal_component_alarm_free(a);
 
605
                        }
 
606
                    }
 
607
                    cal_obj_uid_list_free(auids);
 
608
 
 
609
                    alarms[tmp.time] = tmp;
 
610
                }
 
611
 
 
612
                appointment.alarms.reserve(alarms.size());
 
613
                for (const auto& it : alarms)
 
614
                    appointment.alarms.push_back(it.second);
 
615
 
519
616
                subtask->task->appointments.push_back(appointment);
520
617
            }
 
618
 
 
619
            g_time_zone_unref(gtz);
521
620
        }
522
621
 
523
622
        return G_SOURCE_CONTINUE;
591
690
    std::set<ESource*> m_sources;
592
691
    std::map<ESource*,ECalClient*> m_clients;
593
692
    std::map<ESource*,ECalClientView*> m_views;
594
 
    GCancellable* m_cancellable = nullptr;
595
 
    ESourceRegistry* m_source_registry = nullptr;
596
 
    guint m_rebuild_tag = 0;
597
 
    time_t m_rebuild_deadline = 0;
 
693
    GCancellable* m_cancellable {};
 
694
    ESourceRegistry* m_source_registry {};
 
695
    guint m_rebuild_tag {};
 
696
    time_t m_rebuild_deadline {};
598
697
};
599
698
 
600
699
/***