2
* This file is a part of the Cairo-Dock project
4
* Copyright : (C) see the 'copyright' file.
5
* E-mail : see the 'copyright' file.
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 3
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
25
#include "applet-struct.h"
26
#include "applet-task-editor.h"
27
#include "applet-calendar.h"
29
#define _cd_task_matches_month(pTask, iMonth, iYear) (((pTask)->iMonth == iMonth && ((pTask)->iYear == iYear || (pTask)->iFrequency == CD_TASK_EACH_YEAR)) || (pTask)->iFrequency == CD_TASK_EACH_MONTH)
30
#define _cd_task_matches_day(pTask, iDay, iMonth, iYear) ((pTask)->iDay == iDay && _cd_task_matches_month (pTask, iMonth, iYear))
37
void cd_clock_register_backend (GldiModuleInstance *myApplet, const gchar *cBackendName, CDClockTaskBackend *pBackend)
39
if (myData.pBackends == NULL)
40
myData.pBackends = g_hash_table_new_full (g_str_hash,
44
g_hash_table_insert (myData.pBackends, g_strdup (cBackendName), pBackend);
47
CDClockTaskBackend *cd_clock_get_backend (GldiModuleInstance *myApplet, const gchar *cBackendName)
49
CDClockTaskBackend *pBackend = NULL;
50
if (cBackendName != NULL)
51
pBackend = g_hash_table_lookup (myData.pBackends, cBackendName);
56
void cd_clock_set_current_backend (GldiModuleInstance *myApplet)
58
if (myData.pBackend && myData.pBackend->stop)
59
myData.pBackend->stop (myApplet);
60
myData.pBackend = cd_clock_get_backend (myApplet, myConfig.cTaskMgrName);
61
if (myData.pBackend == NULL)
62
myData.pBackend = cd_clock_get_backend (myApplet, "Default");
63
if (myData.pBackend->init)
64
myData.pBackend->init (myApplet);
72
static int _compare_task (CDClockTask *pTask1, CDClockTask *pTask2, gpointer data)
74
if (pTask1->iYear < pTask2->iYear)
76
if (pTask1->iYear > pTask2->iYear)
79
if (pTask1->iMonth < pTask2->iMonth)
81
if (pTask1->iMonth > pTask2->iMonth)
84
if (pTask1->iDay < pTask2->iDay)
86
if (pTask1->iDay > pTask2->iDay)
89
if (pTask1->iHour < pTask2->iHour)
91
if (pTask1->iHour > pTask2->iHour)
94
if (pTask1->iMinute < pTask2->iMinute)
96
if (pTask1->iMinute > pTask2->iMinute)
102
void cd_clock_list_tasks (GldiModuleInstance *myApplet)
104
cd_message ("%s ()", __func__);
105
if (myData.pTasks != NULL)
106
cd_clock_reset_tasks_list (myApplet);
108
myData.pTasks = myData.pBackend->get_tasks (myApplet);
111
for (t = myData.pTasks; t != NULL; t = t->next)
114
pTask->pApplet = myApplet;
116
myData.pTasks = g_list_sort_with_data (myData.pTasks,
117
(GCompareDataFunc) _compare_task,
119
myData.pNextTask = cd_clock_get_next_scheduled_task (myApplet);
120
myData.pNextAnniversary = cd_clock_get_next_anniversary (myApplet);
123
void cd_clock_add_task_to_list (CDClockTask *pTask, GldiModuleInstance *myApplet)
125
pTask->pApplet = myApplet;
126
myData.pTasks = g_list_insert_sorted (myData.pTasks, pTask, (GCompareFunc)_compare_task);
127
myData.pNextTask = cd_clock_get_next_scheduled_task (myApplet);
128
myData.pNextAnniversary = cd_clock_get_next_anniversary (myApplet);
131
void cd_clock_remove_task_from_list (CDClockTask *pTask, GldiModuleInstance *myApplet)
133
myData.pTasks = g_list_remove (myData.pTasks, pTask);
134
myData.pMissedTasks = g_list_remove (myData.pMissedTasks, pTask);
135
myData.pNextTask = cd_clock_get_next_scheduled_task (myApplet);
136
myData.pNextAnniversary = cd_clock_get_next_anniversary (myApplet);
139
void cd_clock_free_task (CDClockTask *pTask)
143
if (pTask->iSidWarning != 0)
144
g_source_remove (pTask->iSidWarning);
145
gldi_object_unref (GLDI_OBJECT(pTask->pWarningDialog));
146
g_free (pTask->cTitle);
147
g_free (pTask->cText);
148
g_free (pTask->cTags);
153
void cd_clock_reset_tasks_list (GldiModuleInstance *myApplet)
155
g_list_foreach (myData.pTasks, (GFunc)cd_clock_free_task, NULL);
156
g_list_free (myData.pTasks);
157
g_list_free (myData.pMissedTasks);
158
myData.pTasks = NULL;
159
myData.pNextTask = NULL;
160
myData.pMissedTasks = NULL;
163
CDClockTask *cd_clock_get_task_by_id (const gchar *cID, GldiModuleInstance *myApplet)
169
for (t = myData.pTasks; t != NULL; t = t->next)
172
if (strcmp (pTask->cID, cID) == 0)
178
gchar *cd_clock_get_tasks_for_today (GldiModuleInstance *myApplet)
180
guint iDay = myData.currentTime.tm_mday, iMonth = myData.currentTime.tm_mon, iYear = myData.currentTime.tm_year + 1900;
182
GString *sTaskString = NULL;
185
for (t = myData.pTasks; t != NULL; t = t->next)
188
if (_cd_task_matches_day (pTask, iDay, iMonth, iYear))
190
if (sTaskString == NULL)
191
sTaskString = g_string_new ("");
192
g_string_append_printf (sTaskString, "<b><u>%s</u></b>\n <i>at %d:%02d</i>\n %s\n", pTask->cTitle ? pTask->cTitle : D_("No title"), pTask->iHour, pTask->iMinute, pTask->cText?pTask->cText:"");
196
if (sTaskString == NULL)
199
gchar *cTasks = sTaskString->str;
200
g_string_free (sTaskString, FALSE);
204
gchar *cd_clock_get_tasks_for_this_week (GldiModuleInstance *myApplet)
206
guint iDay = myData.currentTime.tm_mday, iMonth = myData.currentTime.tm_mon, iYear = myData.currentTime.tm_year + 1900;
208
GDate* pCurrentDate = g_date_new_dmy (iDay, iMonth + 1, iYear);
209
GDate* pDate = g_date_new ();
212
GString *sTaskString = NULL;
215
for (t = myData.pTasks; t != NULL; t = t->next)
218
switch (pTask->iFrequency)
220
case CD_TASK_DONT_REPEAT:
225
g_date_set_dmy (pDate, d, m, y);
226
iDelta = g_date_days_between (pCurrentDate, pDate);
229
case CD_TASK_EACH_MONTH:
233
g_date_set_dmy (pDate, d, m, y);
234
iDelta = g_date_days_between (pCurrentDate, pDate);
235
if (iDelta < 0) // pDate est avant pCurrentDate => on teste le mois d'apres.
240
g_date_set_dmy (pDate, d, m, y);
245
y = pTask->iYear + 1;
246
g_date_set_dmy (pDate, d, m, y);
248
iDelta = g_date_days_between (pCurrentDate, pDate);
252
case CD_TASK_EACH_YEAR:
256
g_date_set_dmy (pDate, d, m, y);
257
iDelta = g_date_days_between (pCurrentDate, pDate);
258
//g_print ("iDelta : %d/%d/%d -> %d (%s)\n", d, m, y, iDelta, pTask->cTitle);
259
if (iDelta < 0) // pDate est avant pCurrentDate => on teste l'annee d'apres.
262
g_date_set_dmy (pDate, d, m, y);
263
iDelta = g_date_days_between (pCurrentDate, pDate);
268
if (iDelta >= 0 && iDelta < 7)
270
if (sTaskString == NULL)
271
sTaskString = g_string_new ("");
272
g_string_append_printf (sTaskString, "<b><u>%s</u></b>\n <i>%d/%d/%d at %d:%02d</i>\n %s\n",
273
pTask->cTitle ? pTask->cTitle : D_("No title"),
274
(myConfig.bNormalDate ? d : y), m, (myConfig.bNormalDate ? y : d),
275
pTask->iHour, pTask->iMinute,
276
pTask->cText?pTask->cText:"");
277
} // on n'arrete pas le parcours si iDelta > 7 pour prendre en compte aussi les anniv.
279
g_date_free (pCurrentDate);
282
if (sTaskString == NULL)
285
gchar *cTasks = sTaskString->str;
286
g_string_free (sTaskString, FALSE);
290
#define _compute_index(y,m,d,h,mi) ((((y*12+m)*32+d)*24+h)*60+mi)
291
CDClockTask *cd_clock_get_next_scheduled_task (GldiModuleInstance *myApplet)
293
if (myData.pTasks == NULL)
296
guint iDay = myData.currentTime.tm_mday;
297
guint iMonth = myData.currentTime.tm_mon;
298
guint iYear = myData.currentTime.tm_year + 1900;
299
guint iHour = myData.currentTime.tm_hour;
300
guint iMinute = myData.currentTime.tm_min;
301
gulong iIndex = _compute_index (iYear, iMonth, iDay, iHour, iMinute);
302
gulong i, iNextIndex=0;
303
//g_print ("%s (%d/%d/%d -> %ld)\n", __func__, iDay, iMonth, iYear, iIndex);
305
CDClockTask *pNextTask = NULL;
308
for (t = myData.pTasks; t != NULL; t = t->next)
311
//g_print ("test de %s (%d/%d/%d)\n", pTask->cTitle, pTask->iDay, pTask->iMonth, pTask->iYear);
312
switch (pTask->iFrequency)
314
case CD_TASK_DONT_REPEAT:
316
i = _compute_index (pTask->iYear, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
317
//g_print (" normal : %ld\n", i);
320
case CD_TASK_EACH_MONTH:
321
i = _compute_index (iYear, iMonth, pTask->iDay, pTask->iHour, pTask->iMinute); // index pour le mois courant.
322
if (i < iIndex) // on tombe avant, on calcule l'index pour le mois suivant.
325
i = _compute_index (iYear, iMonth+1, pTask->iDay, pTask->iHour, pTask->iMinute);
327
i = _compute_index (iYear+1, 0, pTask->iDay, pTask->iHour, pTask->iMinute);
329
//g_print (" mensuel : %ld\n", i);
332
case CD_TASK_EACH_YEAR:
333
i = _compute_index (iYear, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
334
if (i < iIndex) // on tombe avant, on calcule l'index pour l'annee suivante.
335
i = _compute_index (iYear+1, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
336
//g_print (" annuel : %ld\n", i);
339
if (i >= iIndex && (iNextIndex == 0 || i < iNextIndex))
343
//g_print ("pNextTask <- %s, index <- %ld\n", pNextTask->cTitle, iNextIndex);
349
CDClockTask *cd_clock_get_next_anniversary (GldiModuleInstance *myApplet)
351
if (myData.pTasks == NULL)
354
guint iDay = myData.currentTime.tm_mday;
355
guint iMonth = myData.currentTime.tm_mon;
356
guint iYear = myData.currentTime.tm_year + 1900;
357
guint iHour = myData.currentTime.tm_hour;
358
guint iMinute = myData.currentTime.tm_min;
359
gulong iIndex = _compute_index (iYear, iMonth, iDay, iHour, iMinute);
360
gulong i, iNextIndex=0;
361
//g_print ("%s (%d/%d/%d -> %ld)\n", __func__, iDay, iMonth, iYear, iIndex);
363
CDClockTask *pNextAnniversary = NULL;
366
for (t = myData.pTasks; t != NULL; t = t->next)
369
if (pTask->iFrequency != CD_TASK_EACH_YEAR)
371
//g_print ("test de %s (%d/%d/%d)\n", pTask->cTitle, pTask->iDay, pTask->iMonth, pTask->iYear);
373
i = _compute_index (iYear, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
374
if (i < iIndex) // on tombe avant, on calcule l'index pour l'annee suivante.
375
i = _compute_index (iYear+1, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
376
//g_print (" annuel : %ld\n", i);
378
if (i > iIndex && (iNextIndex == 0 || i < iNextIndex))
381
pNextAnniversary = pTask;
382
//g_print ("pNextTask <- %s, index <- %ld\n", pNextTask->cTitle, iNextIndex);
385
return pNextAnniversary;
389
GList *cd_clock_get_missed_tasks (GldiModuleInstance *myApplet)
391
GList *pTaskList = NULL;
392
guint iDay = myData.currentTime.tm_mday;
393
guint iMonth = myData.currentTime.tm_mon;
394
guint iYear = myData.currentTime.tm_year + 1900;
395
guint iHour = myData.currentTime.tm_hour;
396
guint iMinute = myData.currentTime.tm_min;
398
GDate* pCurrentDate = g_date_new_dmy (iDay, iMonth + 1, iYear);
399
GDate* pDate = g_date_new ();
404
for (t = myData.pTasks; t != NULL; t = t->next)
407
if (pTask->bAcknowledged)
410
switch (pTask->iFrequency)
412
case CD_TASK_DONT_REPEAT:
417
g_date_set_dmy (pDate, d, m, y);
418
iDelta = g_date_days_between (pCurrentDate, pDate);
421
case CD_TASK_EACH_MONTH:
425
g_date_set_dmy (pDate, d, m, y);
426
iDelta = g_date_days_between (pCurrentDate, pDate);
427
if (iDelta > 0) // pDate est apres pCurrentDate => on teste le mois d'avant.
432
g_date_set_dmy (pDate, d, m, y);
437
y = pTask->iYear - 1;
438
g_date_set_dmy (pDate, d, m, y);
440
iDelta = g_date_days_between (pCurrentDate, pDate);
444
case CD_TASK_EACH_YEAR:
448
g_date_set_dmy (pDate, d, m, y);
449
iDelta = g_date_days_between (pCurrentDate, pDate);
450
//g_print ("iDelta : %d/%d/%d -> %d (%s)\n", d, m, y, iDelta, pTask->cTitle);
451
if (iDelta > 0) // pDate est apres pCurrentDate => on teste l'annee d'avant.
454
g_date_set_dmy (pDate, d, m, y);
455
iDelta = g_date_days_between (pCurrentDate, pDate);
460
if (iDelta <= 0 && iDelta > -7)
462
if (iDelta == 0) // today's task, check time
464
if (pTask->iHour > iHour || (pTask->iHour == iHour && pTask->iMinute > iMinute)) // it's in the future, skip it.
467
pTaskList = g_list_prepend (pTaskList, pTask);
468
} // on n'arrete pas le parcours si iDelta > 7 pour prendre en compte aussi les anniv.
470
g_date_free (pCurrentDate);
481
static void _mark_days (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
483
guint iYear, iMonth, iDay;
484
gtk_calendar_get_date (GTK_CALENDAR (pCalendar),
491
for (t = myData.pTasks; t != NULL; t = t->next)
494
if (_cd_task_matches_month (pTask, iMonth, iYear))
496
gtk_calendar_mark_day (GTK_CALENDAR (pCalendar), pTask->iDay);
500
void cd_clock_update_calendar_marks (GldiModuleInstance *myApplet)
502
if (myData.pCalendarDialog != NULL)
504
gtk_calendar_clear_marks (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget));
505
_mark_days (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget), myApplet);
509
static gchar * _on_display_task_detail (GtkCalendar *calendar, guint iYear, guint iMonth, guint iDay, GldiModuleInstance *myApplet)
511
if (myData.pTasks == NULL)
514
//g_print ("%s (%d/%d/%d)\n", __func__, iDay, iMonth, iYear);
515
GString *sDetail = NULL;
518
for (t = myData.pTasks; t != NULL; t = t->next)
521
if (_cd_task_matches_day (pTask, iDay, iMonth, iYear))
524
sDetail = g_string_new ("");
525
if (pTask->iFrequency == CD_TASK_EACH_YEAR && iYear > pTask->iYear)
526
g_string_append_printf (sDetail, "<b><u>%s</u> (%d %s)</b>\n <i>at %d:%02d</i>\n %s\n",
527
pTask->cTitle ? pTask->cTitle : D_("No title"),
528
iYear - pTask->iYear, D_("years"),
529
pTask->iHour, pTask->iMinute,
530
pTask->cText?pTask->cText:"");
532
g_string_append_printf (sDetail, "<b><u>%s</u></b>\n <i>at %d:%02d</i>\n %s\n", pTask->cTitle ? pTask->cTitle : D_("No title"),
533
pTask->iHour, pTask->iMinute,
534
pTask->cText?pTask->cText:"");
540
gchar *cDetail= sDetail->str;
541
g_string_free (sDetail, FALSE);
542
//g_print ("* detail : %s\n", cDetail);
546
static void _on_day_selected_double_click (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
548
guint iDay, iMonth, iYear;
549
gtk_calendar_get_date (pCalendar,
553
cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
556
static void _on_date_changed (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
558
gtk_calendar_clear_marks (pCalendar);
559
_mark_days (pCalendar, myApplet);
562
static void _on_add_task (GtkWidget *pMenuItem, GldiModuleInstance *myApplet)
564
guint iDay, iMonth, iYear;
565
gtk_calendar_get_date (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget),
570
CDClockTask *pTask = g_new0 (CDClockTask, 1);
572
pTask->iMonth = iMonth;
573
pTask->iYear = iYear;
574
pTask->cTitle = g_strdup (D_("No title"));
576
gboolean bCreated = myData.pBackend->create_task (pTask, myApplet);
579
cd_clock_add_task_to_list (pTask, myApplet);
581
cd_clock_update_calendar_marks (myApplet);
584
cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
586
static void _on_edit_tasks (GtkWidget *pMenuItem, GldiModuleInstance *myApplet)
588
guint iDay, iMonth, iYear;
589
gtk_calendar_get_date (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget),
593
cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
595
static gboolean on_button_released_calendar (GtkWidget *widget,
596
GdkEventButton *pButton,
597
GldiModuleInstance *myApplet)
599
if (pButton->button == 3) // right-click
601
GtkWidget *pMenu = gldi_menu_new (NULL);
604
cairo_dock_add_in_menu_with_stock_and_data (D_("Add a new task"), GLDI_ICON_NAME_ADD, G_CALLBACK (_on_add_task), pMenu, myApplet);
607
gchar *cLabel = g_strdup_printf ("%s (%s)", D_("Edit tasks"), D_("double-click"));
608
cairo_dock_add_in_menu_with_stock_and_data (cLabel, GLDI_ICON_NAME_EDIT, G_CALLBACK (_on_edit_tasks), pMenu, myApplet);
611
gtk_widget_show_all (GTK_WIDGET (pMenu));
612
gtk_menu_popup (GTK_MENU (pMenu),
618
gtk_get_current_event_time ());
623
static GtkWidget *cd_clock_build_calendar (GldiModuleInstance *myApplet)
625
cd_message ("%s ()", __func__);
626
GtkWidget *pCalendar = gtk_calendar_new ();
627
g_object_set (G_OBJECT (pCalendar), "show-details", FALSE, NULL);
629
_mark_days (GTK_CALENDAR (pCalendar), myApplet);
631
// reload the marks when the month/year changes
632
g_signal_connect (G_OBJECT (pCalendar), "prev-month" , G_CALLBACK (_on_date_changed), myApplet);
633
g_signal_connect (G_OBJECT (pCalendar), "next-month" , G_CALLBACK (_on_date_changed), myApplet);
634
g_signal_connect (G_OBJECT (pCalendar), "prev-year" , G_CALLBACK (_on_date_changed), myApplet);
635
g_signal_connect (G_OBJECT (pCalendar), "next-year" , G_CALLBACK (_on_date_changed), myApplet);
636
// edit tasks on double-click or right-click
637
g_signal_connect (G_OBJECT (pCalendar), "day-selected-double-click" , G_CALLBACK (_on_day_selected_double_click), myApplet); // it's not a good idea to show the task-editor on left-click, because we can receve the 'click' event when clicking on the month/year buttons, and the 'day-selected' event when we change the month/year.
638
g_signal_connect (G_OBJECT (pCalendar),
639
"button-release-event",
640
G_CALLBACK (on_button_released_calendar),
643
gtk_calendar_set_detail_func (GTK_CALENDAR (pCalendar),
644
(GtkCalendarDetailFunc) _on_display_task_detail,
646
(GDestroyNotify) NULL);
650
void cd_clock_hide_dialogs (GldiModuleInstance *myApplet)
652
gldi_dialogs_remove_on_icon (myIcon);
653
myData.pCalendarDialog = NULL;
657
static void _on_dialog_destroyed (GldiModuleInstance *myApplet)
659
myData.pCalendarDialog = NULL;
661
void cd_clock_show_hide_calendar (GldiModuleInstance *myApplet)
663
cd_debug ("%s (%x)", __func__, myData.pCalendarDialog);
664
if (myData.pCalendarDialog != NULL)
666
gldi_object_unref (GLDI_OBJECT(myData.pCalendarDialog));
667
myData.pCalendarDialog = NULL;
668
if (myData.pTaskWindow != NULL)
670
gtk_widget_destroy (myData.pTaskWindow);
671
myData.pTaskWindow = NULL;
672
myData.pModel = NULL;
677
gldi_dialogs_remove_on_icon (myIcon);
678
GtkWidget *pCalendar = cd_clock_build_calendar (myApplet);
679
myData.pCalendarDialog = gldi_dialog_show (D_("Calendar and tasks"),
682
MY_APPLET_SHARE_DATA_DIR"/dates.svg",
686
(GFreeFunc)_on_dialog_destroyed);