1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail    : see the 'copyright' file.
6 *
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.
11 *
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/>.
18 */
19 
20 #include <stdlib.h>
21 #include <math.h>
22 #define __USE_POSIX
23 #include <signal.h>
24 
25 #include "applet-struct.h"
26 #include "applet-task-editor.h"
27 #include "applet-calendar.h"
28 
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))
31 
32 
33   /////////////
34  // BACKEND //
35 /////////////
36 
cd_clock_register_backend(GldiModuleInstance * myApplet,const gchar * cBackendName,CDClockTaskBackend * pBackend)37 void cd_clock_register_backend (GldiModuleInstance *myApplet, const gchar *cBackendName, CDClockTaskBackend *pBackend)
38 {
39 	if (myData.pBackends == NULL)
40 		myData.pBackends = g_hash_table_new_full (g_str_hash,
41 			g_str_equal,
42 			g_free,
43 			g_free);
44 	g_hash_table_insert (myData.pBackends, g_strdup (cBackendName), pBackend);
45 }
46 
cd_clock_get_backend(GldiModuleInstance * myApplet,const gchar * cBackendName)47 CDClockTaskBackend *cd_clock_get_backend (GldiModuleInstance *myApplet, const gchar *cBackendName)
48 {
49 	CDClockTaskBackend *pBackend = NULL;
50 	if (cBackendName != NULL)
51 		pBackend = g_hash_table_lookup (myData.pBackends, cBackendName);
52 
53 	return pBackend;
54 }
55 
cd_clock_set_current_backend(GldiModuleInstance * myApplet)56 void cd_clock_set_current_backend (GldiModuleInstance *myApplet)
57 {
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);
65 }
66 
67 
68   ///////////
69  // TASKS //
70 ///////////
71 
_compare_task(CDClockTask * pTask1,CDClockTask * pTask2,gpointer data)72 static int _compare_task (CDClockTask *pTask1, CDClockTask *pTask2, gpointer data)
73 {
74 	if (pTask1->iYear < pTask2->iYear)
75 		return -1;
76 	if (pTask1->iYear > pTask2->iYear)
77 		return 1;
78 
79 	if (pTask1->iMonth < pTask2->iMonth)
80 		return -1;
81 	if (pTask1->iMonth > pTask2->iMonth)
82 		return 1;
83 
84 	if (pTask1->iDay < pTask2->iDay)
85 		return -1;
86 	if (pTask1->iDay > pTask2->iDay)
87 		return 1;
88 
89 	if (pTask1->iHour < pTask2->iHour)
90 		return -1;
91 	if (pTask1->iHour > pTask2->iHour)
92 		return 1;
93 
94 	if (pTask1->iMinute < pTask2->iMinute)
95 		return -1;
96 	if (pTask1->iMinute > pTask2->iMinute)
97 		return 1;
98 
99 	return 0;
100 
101 }
cd_clock_list_tasks(GldiModuleInstance * myApplet)102 void cd_clock_list_tasks (GldiModuleInstance *myApplet)
103 {
104 	cd_message ("%s ()", __func__);
105 	if (myData.pTasks != NULL)
106 		cd_clock_reset_tasks_list (myApplet);
107 
108 	myData.pTasks = myData.pBackend->get_tasks (myApplet);
109 	CDClockTask *pTask;
110 	GList *t;
111 	for (t = myData.pTasks; t != NULL; t = t->next)
112 	{
113 		pTask = t->data;
114 		pTask->pApplet = myApplet;
115 	}
116 	myData.pTasks = g_list_sort_with_data (myData.pTasks,
117 		(GCompareDataFunc) _compare_task,
118 		NULL);
119 	myData.pNextTask = cd_clock_get_next_scheduled_task (myApplet);
120 	myData.pNextAnniversary = cd_clock_get_next_anniversary (myApplet);
121 }
122 
cd_clock_add_task_to_list(CDClockTask * pTask,GldiModuleInstance * myApplet)123 void cd_clock_add_task_to_list (CDClockTask *pTask, GldiModuleInstance *myApplet)
124 {
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);
129 }
130 
cd_clock_remove_task_from_list(CDClockTask * pTask,GldiModuleInstance * myApplet)131 void cd_clock_remove_task_from_list (CDClockTask *pTask, GldiModuleInstance *myApplet)
132 {
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);
137 }
138 
cd_clock_free_task(CDClockTask * pTask)139 void cd_clock_free_task (CDClockTask *pTask)
140 {
141 	if (pTask == NULL)
142 		return;
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);
149 	g_free (pTask->cID);
150 	g_free (pTask);
151 }
152 
cd_clock_reset_tasks_list(GldiModuleInstance * myApplet)153 void cd_clock_reset_tasks_list (GldiModuleInstance *myApplet)
154 {
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;
161 }
162 
cd_clock_get_task_by_id(const gchar * cID,GldiModuleInstance * myApplet)163 CDClockTask *cd_clock_get_task_by_id (const gchar *cID, GldiModuleInstance *myApplet)
164 {
165 	if (cID == NULL)
166 		return NULL;
167 	CDClockTask *pTask;
168 	GList *t;
169 	for (t = myData.pTasks; t != NULL; t = t->next)
170 	{
171 		pTask = t->data;
172 		if (strcmp (pTask->cID, cID) == 0)
173 			return pTask;
174 	}
175 	return NULL;
176 }
177 
cd_clock_get_tasks_for_today(GldiModuleInstance * myApplet)178 gchar *cd_clock_get_tasks_for_today (GldiModuleInstance *myApplet)
179 {
180 	guint iDay = myData.currentTime.tm_mday, iMonth = myData.currentTime.tm_mon, iYear = myData.currentTime.tm_year + 1900;
181 
182 	GString *sTaskString = NULL;
183 	CDClockTask *pTask;
184 	GList *t;
185 	for (t = myData.pTasks; t != NULL; t = t->next)
186 	{
187 		pTask = t->data;
188 		if (_cd_task_matches_day (pTask, iDay, iMonth, iYear))
189 		{
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:"");
193 		}
194 	}
195 
196 	if (sTaskString == NULL)
197 		return NULL;
198 
199 	gchar *cTasks = sTaskString->str;
200 	g_string_free (sTaskString, FALSE);
201 	return cTasks;
202 }
203 
cd_clock_get_tasks_for_this_week(GldiModuleInstance * myApplet)204 gchar *cd_clock_get_tasks_for_this_week (GldiModuleInstance *myApplet)
205 {
206 	guint iDay = myData.currentTime.tm_mday, iMonth = myData.currentTime.tm_mon, iYear = myData.currentTime.tm_year + 1900;
207 
208 	GDate* pCurrentDate = g_date_new_dmy (iDay, iMonth + 1, iYear);
209 	GDate* pDate = g_date_new ();
210 	guint d, m, y;
211 	int iDelta;
212 	GString *sTaskString = NULL;
213 	CDClockTask *pTask;
214 	GList *t;
215 	for (t = myData.pTasks; t != NULL; t = t->next)
216 	{
217 		pTask = t->data;
218 		switch (pTask->iFrequency)
219 		{
220 			case CD_TASK_DONT_REPEAT:
221 			default:
222 				d = pTask->iDay;
223 				m = pTask->iMonth+1;
224 				y = pTask->iYear;
225 				g_date_set_dmy (pDate, d, m, y);
226 				iDelta = g_date_days_between (pCurrentDate, pDate);
227 			break;
228 
229 			case CD_TASK_EACH_MONTH:
230 				d = pTask->iDay;
231 				m = iMonth+1;
232 				y = iYear;
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.
236 				{
237 					if (iMonth < 11)
238 					{
239 						m = iMonth+2;
240 						g_date_set_dmy (pDate, d, m, y);
241 					}
242 					else
243 					{
244 						m = 1;
245 						y = pTask->iYear + 1;
246 						g_date_set_dmy (pDate, d, m, y);
247 					}
248 					iDelta = g_date_days_between (pCurrentDate, pDate);
249 				}
250 			break;
251 
252 			case CD_TASK_EACH_YEAR:
253 				d = pTask->iDay;
254 				m = pTask->iMonth+1;
255 				y = iYear;
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.
260 				{
261 					y = iYear + 1;
262 					g_date_set_dmy (pDate, d, m, y);
263 					iDelta = g_date_days_between (pCurrentDate, pDate);
264 				}
265 			break;
266 		}
267 
268 		if (iDelta >= 0 && iDelta < 7)
269 		{
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.
278 	}
279 	g_date_free (pCurrentDate);
280 	g_date_free (pDate);
281 
282 	if (sTaskString == NULL)
283 		return NULL;
284 
285 	gchar *cTasks = sTaskString->str;
286 	g_string_free (sTaskString, FALSE);
287 	return cTasks;
288 }
289 
290 #define _compute_index(y,m,d,h,mi) ((((y*12+m)*32+d)*24+h)*60+mi)
cd_clock_get_next_scheduled_task(GldiModuleInstance * myApplet)291 CDClockTask *cd_clock_get_next_scheduled_task (GldiModuleInstance *myApplet)
292 {
293 	if (myData.pTasks == NULL)
294 		return NULL;
295 
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);
304 
305 	CDClockTask *pNextTask = NULL;
306 	CDClockTask *pTask;
307 	GList *t;
308 	for (t = myData.pTasks; t != NULL; t = t->next)
309 	{
310 		pTask = t->data;
311 		//g_print ("test de %s (%d/%d/%d)\n", pTask->cTitle, pTask->iDay, pTask->iMonth, pTask->iYear);
312 		switch (pTask->iFrequency)
313 		{
314 			case CD_TASK_DONT_REPEAT:
315 			default:
316 				i = _compute_index (pTask->iYear, pTask->iMonth, pTask->iDay, pTask->iHour, pTask->iMinute);
317 				//g_print (" normal : %ld\n", i);
318 			break;
319 
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.
323 				{
324 					if (iMonth < 11)
325 						i = _compute_index (iYear, iMonth+1, pTask->iDay, pTask->iHour, pTask->iMinute);
326 					else
327 						i = _compute_index (iYear+1, 0, pTask->iDay, pTask->iHour, pTask->iMinute);
328 				}
329 				//g_print (" mensuel : %ld\n", i);
330 			break;
331 
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);
337 			break;
338 		}
339 		if (i >= iIndex && (iNextIndex == 0 || i < iNextIndex))
340 		{
341 			iNextIndex = i;
342 			pNextTask = pTask;
343 			//g_print ("pNextTask <- %s, index <- %ld\n", pNextTask->cTitle, iNextIndex);
344 		}
345 	}
346 	return pNextTask;
347 }
348 
cd_clock_get_next_anniversary(GldiModuleInstance * myApplet)349 CDClockTask *cd_clock_get_next_anniversary (GldiModuleInstance *myApplet)
350 {
351 	if (myData.pTasks == NULL)
352 		return NULL;
353 
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);
362 
363 	CDClockTask *pNextAnniversary = NULL;
364 	CDClockTask *pTask;
365 	GList *t;
366 	for (t = myData.pTasks; t != NULL; t = t->next)
367 	{
368 		pTask = t->data;
369 		if (pTask->iFrequency != CD_TASK_EACH_YEAR)
370 			continue;
371 		//g_print ("test de %s (%d/%d/%d)\n", pTask->cTitle, pTask->iDay, pTask->iMonth, pTask->iYear);
372 
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);
377 
378 		if (i > iIndex && (iNextIndex == 0 || i < iNextIndex))
379 		{
380 			iNextIndex = i;
381 			pNextAnniversary = pTask;
382 			//g_print ("pNextTask <- %s, index <- %ld\n", pNextTask->cTitle, iNextIndex);
383 		}
384 	}
385 	return pNextAnniversary;
386 }
387 
388 
cd_clock_get_missed_tasks(GldiModuleInstance * myApplet)389 GList *cd_clock_get_missed_tasks (GldiModuleInstance *myApplet)
390 {
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;
397 
398 	GDate* pCurrentDate = g_date_new_dmy (iDay, iMonth + 1, iYear);
399 	GDate* pDate = g_date_new ();
400 	guint d, m, y;
401 	int iDelta;
402 	CDClockTask *pTask;
403 	GList *t;
404 	for (t = myData.pTasks; t != NULL; t = t->next)
405 	{
406 		pTask = t->data;
407 		if (pTask->bAcknowledged)
408 			continue;
409 
410 		switch (pTask->iFrequency)
411 		{
412 			case CD_TASK_DONT_REPEAT:
413 			default:
414 				d = pTask->iDay;
415 				m = pTask->iMonth+1;
416 				y = pTask->iYear;
417 				g_date_set_dmy (pDate, d, m, y);
418 				iDelta = g_date_days_between (pCurrentDate, pDate);
419 			break;
420 
421 			case CD_TASK_EACH_MONTH:
422 				d = pTask->iDay;
423 				m = iMonth+1;
424 				y = iYear;
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.
428 				{
429 					if (iMonth > 0)
430 					{
431 						m = iMonth;
432 						g_date_set_dmy (pDate, d, m, y);
433 					}
434 					else
435 					{
436 						m = 12;
437 						y = pTask->iYear - 1;
438 						g_date_set_dmy (pDate, d, m, y);
439 					}
440 					iDelta = g_date_days_between (pCurrentDate, pDate);
441 				}
442 			break;
443 
444 			case CD_TASK_EACH_YEAR:
445 				d = pTask->iDay;
446 				m = pTask->iMonth+1;
447 				y = iYear;
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.
452 				{
453 					y = iYear - 1;
454 					g_date_set_dmy (pDate, d, m, y);
455 					iDelta = g_date_days_between (pCurrentDate, pDate);
456 				}
457 			break;
458 		}
459 
460 		if (iDelta <= 0 && iDelta > -7)
461 		{
462 			if (iDelta == 0)  // today's task, check time
463 			{
464 				if (pTask->iHour > iHour || (pTask->iHour == iHour && pTask->iMinute > iMinute))  // it's in the future, skip it.
465 					continue;
466 			}
467 			pTaskList = g_list_prepend (pTaskList, pTask);
468 		}  // on n'arrete pas le parcours si iDelta > 7 pour prendre en compte aussi les anniv.
469 	}
470 	g_date_free (pCurrentDate);
471 	g_date_free (pDate);
472 
473 	return pTaskList;
474 }
475 
476 
477   //////////////
478  // CALENDAR //
479 //////////////
480 
_mark_days(GtkCalendar * pCalendar,GldiModuleInstance * myApplet)481 static void _mark_days (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
482 {
483 	guint iYear, iMonth, iDay;
484 	gtk_calendar_get_date (GTK_CALENDAR (pCalendar),
485 		&iYear,
486 		&iMonth,
487 		&iDay);
488 
489 	CDClockTask *pTask;
490 	GList *t;
491 	for (t = myData.pTasks; t != NULL; t = t->next)
492 	{
493 		pTask = t->data;
494 		if (_cd_task_matches_month (pTask, iMonth, iYear))
495 		{
496 			gtk_calendar_mark_day (GTK_CALENDAR (pCalendar), pTask->iDay);
497 		}
498 	}
499 }
cd_clock_update_calendar_marks(GldiModuleInstance * myApplet)500 void cd_clock_update_calendar_marks (GldiModuleInstance *myApplet)
501 {
502 	if (myData.pCalendarDialog != NULL)
503 	{
504 		gtk_calendar_clear_marks (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget));
505 		_mark_days (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget), myApplet);
506 	}
507 }
508 
_on_display_task_detail(GtkCalendar * calendar,guint iYear,guint iMonth,guint iDay,GldiModuleInstance * myApplet)509 static gchar * _on_display_task_detail (GtkCalendar *calendar, guint iYear, guint iMonth, guint iDay, GldiModuleInstance *myApplet)
510 {
511 	if (myData.pTasks == NULL)
512 		return NULL;
513 
514 	//g_print ("%s (%d/%d/%d)\n", __func__, iDay, iMonth, iYear);
515 	GString *sDetail = NULL;
516 	CDClockTask *pTask;
517 	GList *t;
518 	for (t = myData.pTasks; t != NULL; t = t->next)
519 	{
520 		pTask = t->data;
521 		if (_cd_task_matches_day (pTask, iDay, iMonth, iYear))
522 		{
523 			if (sDetail == NULL)
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:"");
531 			else
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:"");
535 		}
536 	}
537 
538 	if (sDetail == NULL)
539 		return NULL;
540 	gchar *cDetail= sDetail->str;
541 	g_string_free (sDetail, FALSE);
542 	//g_print ("* detail : %s\n", cDetail);
543 	return cDetail;
544 }
545 
_on_day_selected_double_click(GtkCalendar * pCalendar,GldiModuleInstance * myApplet)546 static void _on_day_selected_double_click (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
547 {
548 	guint iDay, iMonth, iYear;
549 	gtk_calendar_get_date (pCalendar,
550 		&iYear,
551 		&iMonth,
552 		&iDay);
553 	cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
554 }
555 
_on_date_changed(GtkCalendar * pCalendar,GldiModuleInstance * myApplet)556 static void _on_date_changed (GtkCalendar *pCalendar, GldiModuleInstance *myApplet)
557 {
558 	gtk_calendar_clear_marks (pCalendar);
559 	_mark_days (pCalendar, myApplet);
560 }
561 
_on_add_task(GtkWidget * pMenuItem,GldiModuleInstance * myApplet)562 static void _on_add_task (GtkWidget *pMenuItem, GldiModuleInstance *myApplet)
563 {
564 	guint iDay, iMonth, iYear;
565 	gtk_calendar_get_date (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget),
566 		&iYear,
567 		&iMonth,
568 		&iDay);
569 
570 	CDClockTask *pTask = g_new0 (CDClockTask, 1);
571 	pTask->iDay = iDay;
572 	pTask->iMonth = iMonth;
573 	pTask->iYear = iYear;
574 	pTask->cTitle = g_strdup (D_("No title"));
575 	pTask->iHour = 12;
576 	gboolean bCreated = myData.pBackend->create_task (pTask, myApplet);
577 	if (bCreated)
578 	{
579 		cd_clock_add_task_to_list (pTask, myApplet);
580 
581 		cd_clock_update_calendar_marks (myApplet);
582 	}
583 
584 	cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
585 }
_on_edit_tasks(GtkWidget * pMenuItem,GldiModuleInstance * myApplet)586 static void _on_edit_tasks (GtkWidget *pMenuItem, GldiModuleInstance *myApplet)
587 {
588 	guint iDay, iMonth, iYear;
589 	gtk_calendar_get_date (GTK_CALENDAR (myData.pCalendarDialog->pInteractiveWidget),
590 		&iYear,
591 		&iMonth,
592 		&iDay);
593 	cd_clock_build_task_editor (iDay, iMonth, iYear, myApplet);
594 }
on_button_released_calendar(GtkWidget * widget,GdkEventButton * pButton,GldiModuleInstance * myApplet)595 static gboolean on_button_released_calendar (GtkWidget *widget,
596 	GdkEventButton *pButton,
597 	GldiModuleInstance *myApplet)
598 {
599 	if (pButton->button == 3)  // right-click
600 	{
601 		GtkWidget *pMenu = gldi_menu_new (NULL);
602 
603 		// add a task
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);
605 
606 		// edit tasks
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);
609 		g_free (cLabel);
610 
611 		gtk_widget_show_all (GTK_WIDGET (pMenu));
612 		gtk_menu_popup (GTK_MENU (pMenu),
613 			NULL,
614 			NULL,
615 			NULL,
616 			NULL,  // data
617 			1,
618 			gtk_get_current_event_time ());
619 	}
620 	return FALSE;
621 }
622 
cd_clock_build_calendar(GldiModuleInstance * myApplet)623 static GtkWidget *cd_clock_build_calendar (GldiModuleInstance *myApplet)
624 {
625 	cd_message ("%s ()", __func__);
626 	GtkWidget *pCalendar = gtk_calendar_new ();
627 	g_object_set (G_OBJECT (pCalendar), "show-details", FALSE, NULL);
628 
629 	_mark_days (GTK_CALENDAR (pCalendar), myApplet);
630 
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),
641 		myApplet);
642 
643 	gtk_calendar_set_detail_func (GTK_CALENDAR (pCalendar),
644 		(GtkCalendarDetailFunc) _on_display_task_detail,
645 		myApplet,
646 		(GDestroyNotify) NULL);
647 	return pCalendar;
648 }
649 
cd_clock_hide_dialogs(GldiModuleInstance * myApplet)650 void cd_clock_hide_dialogs (GldiModuleInstance *myApplet)
651 {
652 	gldi_dialogs_remove_on_icon (myIcon);
653 	myData.pCalendarDialog = NULL;
654 }
655 
656 
_on_dialog_destroyed(GldiModuleInstance * myApplet)657 static void _on_dialog_destroyed (GldiModuleInstance *myApplet)
658 {
659 	myData.pCalendarDialog = NULL;
660 }
cd_clock_show_hide_calendar(GldiModuleInstance * myApplet)661 void cd_clock_show_hide_calendar (GldiModuleInstance *myApplet)
662 {
663 	cd_debug ("%s (%x)", __func__, myData.pCalendarDialog);
664 	if (myData.pCalendarDialog != NULL)
665 	{
666 		gldi_object_unref (GLDI_OBJECT(myData.pCalendarDialog));
667 		myData.pCalendarDialog = NULL;
668 		if (myData.pTaskWindow != NULL)
669 		{
670 			gtk_widget_destroy (myData.pTaskWindow);
671 			myData.pTaskWindow = NULL;
672 			myData.pModel = NULL;
673 		}
674 	}
675 	else
676 	{
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"),
680 			myIcon, myContainer,
681 			0,
682 			MY_APPLET_SHARE_DATA_DIR"/dates.svg",
683 			pCalendar,
684 			NULL,
685 			myApplet,
686 			(GFreeFunc)_on_dialog_destroyed);
687 	}
688 }
689