gboolean cd_weather_update_from_data (CairoDockModuleInstance *myApplet)
{
	g_return_val_if_fail (myIcon != NULL, FALSE);  // paranoia
	
	//\_______________________ On etablit le nom de l'icone.
	if (myIcon->acName == NULL && myDock)
	{
		CD_APPLET_SET_NAME_FOR_MY_ICON (myData.cLocation != NULL ? myData.cLocation : WEATHER_DEFAULT_NAME);
	}
	
	//\_______________________ On cree la liste des icones de prevision.
	GList *pIconList = _list_icons (myApplet);  // ne nous appartiendra plus, donc ne pas desallouer.
	
	//\_______________________ On efface l'ancienne liste.
	CD_APPLET_DELETE_MY_ICONS_LIST;
	
	//\_______________________ On charge la nouvelle liste.
	///if (myConfig.iDeskletRenderer == MY_DESKLET_CAROUSSEL)
	{
	  gpointer pConfig[2] = {GINT_TO_POINTER (myConfig.bDesklet3D), GINT_TO_POINTER (FALSE)};
	  CD_APPLET_LOAD_MY_ICONS_LIST (pIconList, myConfig.cRenderer, "Caroussel", pConfig);
	}
	
	//\_______________________ On recharge l'icone principale.
	_weather_draw_current_conditions (myApplet);  // ne lance pas le redraw.
	CD_APPLET_REDRAW_MY_ICON;
	
	myData.bErrorRetrievingData = myData.bErrorInThread;
	if (myData.bErrorRetrievingData && myData.pTask->iPeriod > 20)
	{
		cd_message ("no data, will re-try in 20s");
		cairo_dock_change_task_frequency (myData.pTask, 20);  // on re-essaiera dans 20s.
	}
	else if (myData.pTask->iPeriod != myConfig.iCheckInterval)
	{
		cd_message ("revert to normal frequency");
		cairo_dock_change_task_frequency (myData.pTask, myConfig.iCheckInterval);
	}
	
	return TRUE;
}
static gboolean _update_from_feeds (CDSharedMemory *pSharedMemory)
{
    GldiModuleInstance *myApplet = pSharedMemory->pApplet;
    CD_APPLET_ENTER;
    if (! myData.bInit)  // pas encore initialise, on vire le message d'attente.
    {
        cd_rssreader_free_item_list (myApplet);
        myData.pItemList = NULL;
        myData.bInit = TRUE;
    }

    // On parse le flux XML.
    if (pSharedMemory->cTaskBridge == NULL || *pSharedMemory->cTaskBridge == '\0')
    {
        cd_warning ("RSSresader : no data");
        const gchar *cErrorMessage = (myConfig.cUrl == NULL ?
                                      D_("No URL is defined.") :
                                      D_("No data (no connection?)"));
        _insert_error_message (myApplet, cErrorMessage);
        if (myDesklet)
        {
            cd_applet_update_my_icon (myApplet);
        }
        myData.bUpdateIsManual = FALSE;

        if (myData.pTask->iPeriod > 20)
        {
            cd_message ("no data, will re-try in 20s");
            gldi_task_change_frequency (myData.pTask, 20);  // on re-essaiera dans 20s.
        }

        CD_APPLET_LEAVE (TRUE);
    }

    if (myData.pTask->iPeriod != myConfig.iRefreshTime)
    {
        cd_message ("revert to normal frequency");
        gldi_task_change_frequency (myData.pTask, myConfig.iRefreshTime);
    }

    //g_print (" --> RSS: '%s'\n", myData.cTaskBridge);
    xmlDocPtr doc = xmlParseMemory (pSharedMemory->cTaskBridge, strlen (pSharedMemory->cTaskBridge));
    g_free (pSharedMemory->cTaskBridge);
    pSharedMemory->cTaskBridge = NULL;

    if (doc == NULL)
    {
        cd_warning ("RSSresader : got invalid XML data");
        const gchar *cErrorMessage = D_("Invalid data (invalid RSS/Atom feed?)");
        _insert_error_message (myApplet, cErrorMessage);
        if (myDesklet)
        {
            cd_applet_update_my_icon (myApplet);
        }
        g_free (myData.PrevFirstTitle);
        myData.PrevFirstTitle = NULL;
        myData.bUpdateIsManual = FALSE;
        CD_APPLET_LEAVE (TRUE);
    }

    xmlNodePtr rss = xmlDocGetRootElement (doc);
    if (rss == NULL || (xmlStrcmp (rss->name, BAD_CAST "rss") != 0 && xmlStrcmp (rss->name, BAD_CAST "feed") != 0 && xmlStrcmp (rss->name, BAD_CAST "RDF") != 0))
    {
        cd_warning ("RSSresader : got invalid XML data");
        ///xmlCleanupParser ();
        xmlFreeDoc (doc);

        const gchar *cErrorMessage = D_("Invalid data (invalid RSS/Atom feed?)");
        _insert_error_message (myApplet, cErrorMessage);
        if (myDesklet)
        {
            cd_applet_update_my_icon (myApplet);
        }
        g_free (myData.PrevFirstTitle);
        myData.PrevFirstTitle = NULL;
        myData.bUpdateIsManual = FALSE;
        CD_APPLET_LEAVE (TRUE);
    }

    // on extrait chaque item.
    GList *pNewItemList = NULL;
    CDRssItem *pItem = g_new0 (CDRssItem, 1);  // on commence au debut de la liste (c'est le titre).
    pNewItemList = g_list_prepend (pNewItemList, pItem);
    if (myConfig.cUserTitle != NULL)
        pItem->cTitle = g_strdup (myConfig.cUserTitle);  // ne sera pas ecrase par les donnees du flux.

    if (xmlStrcmp (rss->name, BAD_CAST "rss") == 0)  // RSS
    {
        xmlAttrPtr attr = xmlHasProp (rss, BAD_CAST "version");
        if (attr && attr->children)
        {
            cd_debug ("RSS version : %s", attr->children->content);
        }

        xmlNodePtr channel;
        for (channel = rss->children; channel != NULL; channel = channel->next)
        {
            if (xmlStrcmp (channel->name, BAD_CAST "channel") == 0)
            {
                pNewItemList = _parse_rss_item (channel, pItem, pNewItemList);  // on parse le channel comme un item, ce qui fait que le titre du flux est considere comme un simple item.
                break;  // un seul channel.
            }
        }
    }
    else if (xmlStrcmp (rss->name, BAD_CAST "RDF") == 0)  // RDF
    {
        pNewItemList = _parse_rss_item (rss, pItem, pNewItemList);  // on parse le premier groupe "channel" comme un item, ce qui fait que le titre du flux est considere comme un simple item.
    }
    else  // Atom
    {
        xmlNodePtr feed = rss;
        gchar *cBaseUrl = NULL;  // on recupere la base de l'URL, pour le cas ou les link seraient exprimes relativement a elle.
        gchar *str = g_strstr_len(myConfig.cUrl, 10, "://");
        if (str)
        {
            str = strchr (str + 3, '/');
            if (str)
                cBaseUrl = g_strndup (myConfig.cUrl, (gpointer)str-(gpointer)myConfig.cUrl);
        }
        pNewItemList = _parse_atom_item (feed, pItem, pNewItemList, cBaseUrl);  // on parse le feed comme un item, ce qui fait que le titre du flux est considere comme un simple item.
        g_free (cBaseUrl);
    }
    pNewItemList = g_list_reverse (pNewItemList);

    xmlFreeDoc (doc);  // ne pas utiliser xmlCleanupParser dans un thread !

    // si aucune donnee, on l'affiche et on quitte.
    if (pNewItemList == NULL)
    {
        cd_debug ("RSS: aucune donnee");

        const gchar *cErrorMessage = D_("No data");
        _insert_error_message (myApplet, cErrorMessage);
        if (myDesklet)
        {
            cd_applet_update_my_icon (myApplet);
        }
        g_free (myData.PrevFirstTitle);
        myData.PrevFirstTitle = NULL;
        myData.bUpdateIsManual = FALSE;
        CD_APPLET_LEAVE (TRUE);
    }

    // si on est arrive a ce point, c'est qu'il n'y a pas eu d'erreur.
    // on vide l'ancienne liste d'items.
    cd_rssreader_free_item_list (myApplet);
    myData.pItemList = pNewItemList;

    // on met a jour le titre.
    if (myIcon->cName == NULL)  // il faut mettre a jour le titre
    {
        if (myDock && myConfig.cUserTitle == NULL)  // en mode desklet inutile, le titre sera redessine avec le reste.
        {
            pItem = myData.pItemList->data;
            if (pItem != NULL && pItem->cTitle != NULL)
                CD_APPLET_SET_NAME_FOR_MY_ICON (pItem->cTitle);
        }
    }

    // si aucun changement, on le signale, et si aucune erreur precedente, on quitte.
    pItem = (myData.pItemList && myData.pItemList->next ? myData.pItemList->next->data : NULL);
    gchar *cFirstTitle = (pItem ? pItem->cTitle : NULL);
    if (! cairo_dock_strings_differ (myData.PrevFirstTitle, cFirstTitle))
    {
        cd_debug ("RSS: aucune modif");

        if (myData.bUpdateIsManual)  // L'update a ete manuel -> On affiche donc un dialogue meme s'il n'y a pas eu de changement
        {
            gldi_dialogs_remove_on_icon (myIcon);
            gldi_dialog_show_temporary_with_icon (D_("No modification"),
                                                  myIcon,
                                                  myContainer,
                                                  2000, // Suffisant vu que la MaJ est manuelle
                                                  myDock ? "same icon" : MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE);

            myData.bUpdateIsManual = FALSE;
        }

        if (! myData.bError)
            CD_APPLET_LEAVE (TRUE);
    }

    // on dessine le texte.
    if (myDesklet)
    {
        cd_applet_update_my_icon (myApplet);
    }

    // on avertit l'utilisateur.
    if (myData.PrevFirstTitle != NULL && myConfig.iNotificationType != 0)
    {
        if (myConfig.iNotificationType != 1)
        {
            gldi_dialogs_remove_on_icon (myIcon);
            gldi_dialog_show_temporary_with_icon (D_("This RSS feed has been modified..."),
                                                  myIcon,
                                                  myContainer,
                                                  1000*myConfig.iNotificationDuration,
                                                  myDock ? "same icon" : MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE);
        }
        if (myConfig.iNotificationType != 2)
        {
            CD_APPLET_DEMANDS_ATTENTION (myConfig.cNotificationAnimation, 3);  /// myConfig.iNotificationDuration ?...
        }
    }

    g_free (myData.PrevFirstTitle);
    myData.PrevFirstTitle = g_strdup (cFirstTitle);
    myData.bUpdateIsManual = FALSE;
    myData.bError = FALSE;
    CD_APPLET_LEAVE (TRUE);
}
void cd_xkbd_update_icon (const gchar *cGroupName, const gchar *cShortGroupName, gboolean bRedrawSurface)
{
	//g_print ("%s (%s;%s;%d)\n", __func__, cGroupName, cShortGroupName, bRedrawSurface);
	
	if (bRedrawSurface)  // group has changed -> update the icon and label
	{
		if (! cShortGroupName)
		{
			cShortGroupName = myData.cShortGroupName;
		}
		else
		{
			g_free (myData.cShortGroupName);
			myData.cShortGroupName = g_strdup (cShortGroupName);
		}
		if (! cGroupName)
		{
			cGroupName = myData.cGroupName;
		}
		else
		{
			g_free (myData.cGroupName);
			myData.cGroupName = g_strdup (cGroupName);
		}
		
		//\__________________ On sauvegarde l'ancienne surface/texture.
		cairo_dock_free_image_buffer (myData.pOldImage);
		myData.pOldImage = myData.pCurrentImage;
		myData.pCurrentImage = NULL;
		
		//\__________________ On cree la nouvelle surface (la taille du texte peut avoir change).
		int iWidth, iHeight;
		CD_APPLET_GET_MY_ICON_EXTENT (&iWidth, &iHeight);
		if (iWidth <= 1 && iHeight <= 1)  // peut arriver au lancement en mode desklet.
		{
			return;
		}
		int w, h;
		cairo_surface_t *pSurface = cairo_dock_create_surface_from_text_full (cShortGroupName,
			&myConfig.textDescription,
			1.,
			0,  /// iWidth
			&w, &h);
		myData.pCurrentImage = g_new0 (CairoDockImageBuffer, 1);
		cairo_dock_load_image_buffer_from_surface (myData.pCurrentImage, pSurface, w, h);
		
		//\__________________ On lance une transition entre ancienne et nouvelle surface/texture, ou on dessine direct.
		if (myConfig.iTransitionDuration != 0 && myData.pOldImage != NULL)
		{
			CD_APPLET_SET_TRANSITION_ON_MY_ICON (cd_xkbd_render_step_cairo,
				cd_xkbd_render_step_opengl,
				g_bUseOpenGL,  // bFastPace : vite si opengl, lent si cairo.
				myConfig.iTransitionDuration,
				TRUE);  // bRemoveWhenFinished
		}
		else
		{
			if (CD_APPLET_MY_CONTAINER_IS_OPENGL)
			{
				CD_APPLET_START_DRAWING_MY_ICON_OR_RETURN ();
				cd_xkbd_render_step_opengl (myIcon, myApplet);	
				CD_APPLET_FINISH_DRAWING_MY_ICON;
			}
			else
			{
				cd_xkbd_render_step_cairo (myIcon, myApplet);
			}
			CD_APPLET_REDRAW_MY_ICON;
		}
		
		//\__________________ update the label.
		CD_APPLET_SET_NAME_FOR_MY_ICON (cGroupName);
	}
	else  // only the indicators have changed -> trigger a redraw event only (overlay update).
	{
		CD_APPLET_REDRAW_MY_ICON;
	}
	
	//\__________________ lock indicators
	if (myConfig.bShowKbdIndicator)
	{
		cd_debug ("XKBD: caps-lock: %d; num-lock: %d", myData.iCurrentIndic & 1, myData.iCurrentIndic & 2);
		if (myData.iCurrentIndic & 1)  // caps-lock
		{
			if (! (myData.iPreviousIndic & 1)) // TODO: cairo_dock_search_icon_s_path in init? or here the first time (save to data) and reset in reload?
			{
				if (myConfig.cEmblemCapsLock && (myData.cEmblemCapsLock ||
					(myData.cEmblemCapsLock = cairo_dock_search_icon_s_path (myConfig.cEmblemCapsLock, // search for an icon only the first time
						MAX (myIcon->image.iWidth/2, myIcon->image.iHeight/2)))))
					CD_APPLET_ADD_OVERLAY_ON_MY_ICON (myData.cEmblemCapsLock, CAIRO_OVERLAY_UPPER_RIGHT);
				else
					CD_APPLET_ADD_OVERLAY_ON_MY_ICON (MY_APPLET_SHARE_DATA_DIR"/caps-lock.png", CAIRO_OVERLAY_UPPER_RIGHT);
			}
		}
		else
		{
			if (myData.iPreviousIndic & 1)
				CD_APPLET_REMOVE_OVERLAY_ON_MY_ICON (CAIRO_OVERLAY_UPPER_RIGHT);
		}

		if (myData.iCurrentIndic & 2)  // num-lock
		{
			if (! (myData.iPreviousIndic & 2))
			{
				if (myConfig.cEmblemNumLock &&(myData.cEmblemNumLock ||
					(myData.cEmblemNumLock = cairo_dock_search_icon_s_path (myConfig.cEmblemNumLock,
						MAX (myIcon->image.iWidth/2, myIcon->image.iHeight/2)))))
					CD_APPLET_ADD_OVERLAY_ON_MY_ICON (myData.cEmblemNumLock, CAIRO_OVERLAY_UPPER_LEFT);
				else
					CD_APPLET_ADD_OVERLAY_ON_MY_ICON (MY_APPLET_SHARE_DATA_DIR"/num-lock.png", CAIRO_OVERLAY_UPPER_LEFT);
			}
		}
		else
		{
			if (myData.iPreviousIndic & 2)
				CD_APPLET_REMOVE_OVERLAY_ON_MY_ICON (CAIRO_OVERLAY_UPPER_LEFT);
		}
		myData.iPreviousIndic = myData.iCurrentIndic;
	}
}
gboolean cd_clock_update_with_time (GldiModuleInstance *myApplet)
{
	CD_APPLET_ENTER;
	//\________________ On recupere l'heure courante.
	time_t epoch = (time_t) time (NULL);
	_get_current_time (epoch, myApplet);
	
	//\________________ On change la date si necessaire.
	int iWidth, iHeight;
	CD_APPLET_GET_MY_ICON_EXTENT (&iWidth, &iHeight);
	gboolean bNewDate = (myData.currentTime.tm_mday != myData.iLastCheckedDay || myData.currentTime.tm_mon != myData.iLastCheckedMonth || myData.currentTime.tm_year != myData.iLastCheckedYear);
	if (bNewDate)
	{
		strftime (s_cDateBuffer, CD_CLOCK_DATE_BUFFER_LENGTH, "%a %d %b", &myData.currentTime);
		myData.iLastCheckedDay = myData.currentTime.tm_mday;
		myData.iLastCheckedMonth = myData.currentTime.tm_mon;
		myData.iLastCheckedYear = myData.currentTime.tm_year;
	}
	if (CD_APPLET_MY_CONTAINER_IS_OPENGL && myConfig.bOldStyle && myConfig.iShowDate == CAIRO_DOCK_INFO_ON_ICON)
	{
		if (bNewDate || myData.iDateTexture == 0)
		{
			if (myData.iDateTexture != 0)
				_cairo_dock_delete_texture (myData.iDateTexture);
			
			double fScale = (double) iWidth / (double) myData.DimensionData.width;
			GldiTextDescription labelDescription;
			memset (&labelDescription, 0, sizeof (GldiTextDescription));
			gldi_text_description_set_font (&labelDescription, (gchar*)"Sans 8");  // casted and then set to null
			labelDescription.fColorStart.rgba.red = myConfig.fDateColor[0];
			labelDescription.fColorStart.rgba.green = myConfig.fDateColor[1];
			labelDescription.fColorStart.rgba.blue = myConfig.fDateColor[2];
			labelDescription.fColorStart.rgba.alpha = 1.;
			labelDescription.bNoDecorations = TRUE;
			cairo_surface_t *pDateSurface = cairo_dock_create_surface_from_text_full (s_cDateBuffer,
				&labelDescription,
				fScale,
				iWidth,
				&myData.iDateWidth, &myData.iDateHeight);
			//g_print ("date : %dx%d\n", myData.iDateWidth, myData.iDateHeight);
			myData.iDateTexture = cairo_dock_create_texture_from_surface (pDateSurface);
			cairo_surface_destroy (pDateSurface);
			labelDescription.cFont = NULL;
			gldi_text_description_reset (&labelDescription);
		}
	}
	if (bNewDate && myConfig.iShowDate == CAIRO_DOCK_INFO_ON_LABEL)
	{
		CD_APPLET_SET_NAME_FOR_MY_ICON (s_cDateBuffer);
	}
	
	//\________________ On dessine avec cette heure.
	myData.iSmoothAnimationStep = 0;
	if (myConfig.bOldStyle)
	{
		if (CD_APPLET_MY_CONTAINER_IS_OPENGL)
			cd_clock_render_analogic_to_texture (myApplet, iWidth, iHeight, &myData.currentTime, 0.);
		else
			cd_clock_draw_analogic (myApplet, iWidth, iHeight, &myData.currentTime);
	}
	else
	{
		cd_clock_draw_text (myApplet, iWidth, iHeight, &myData.currentTime);
		///if (CD_APPLET_MY_CONTAINER_IS_OPENGL)  // on ne sait pas bien dessiner du texte, donc on le fait en cairo, et on transfere tout sur notre texture.
		///	cairo_dock_update_icon_texture (myIcon);
	}
	
	///CD_APPLET_REDRAW_MY_ICON;
	
	//\________________ On teste les alarmes et les taches.
	if (!myConfig.bShowSeconds || myData.currentTime.tm_min != myData.iLastCheckedMinute)  // un g_timeout de 1min ne s'effectue pas forcement a exectement 1 minute d'intervalle, et donc pourrait "sauter" la minute de l'alarme, d'ou le test sur bShowSeconds dans le cas ou l'applet ne verifie que chaque minute.
	{
		myData.iLastCheckedMinute = myData.currentTime.tm_min;
		
		// les alarmes.
		CDClockAlarm *pAlarm;
		guint i;
		for (i = 0; i < myConfig.pAlarms->len; i ++)
		{
			pAlarm = g_ptr_array_index (myConfig.pAlarms, i);
			
			if (myData.currentTime.tm_hour == pAlarm->iHour && myData.currentTime.tm_min == pAlarm->iMinute)
			{
				gboolean bShowAlarm = FALSE, bRemoveAlarm = FALSE;
				if (pAlarm->iDayOfWeek > 0)
				{
					if (pAlarm->iDayOfWeek == 1)
						bShowAlarm = TRUE;
					else if (pAlarm->iDayOfWeek - 1 == myData.currentTime.tm_wday)
						bShowAlarm = TRUE;
					else if (myData.currentTime.tm_wday == 0 || myData.currentTime.tm_wday == 6)  // week-end
					{
						if (pAlarm->iDayOfWeek == 9)
							bShowAlarm = TRUE;
					}
					else if (pAlarm->iDayOfWeek == 8)
						bShowAlarm = TRUE;
				}
				else if (pAlarm->iDayOfMonth > 0)
					bShowAlarm = (pAlarm->iDayOfMonth - 1 == myData.currentTime.tm_mday);
				else  // c'est une alarme qui ne se repete pas.
				{
					bShowAlarm = TRUE;
					bRemoveAlarm = TRUE;
				}
				
				if (bShowAlarm)
				{
					cd_message ("Dring ! %s", pAlarm->cMessage);
					gldi_dialog_show_temporary (pAlarm->cMessage, myIcon, myContainer, 60e3);
					if (pAlarm->cCommand != NULL)
					{
						if (myData.iAlarmPID > 0)
						{
							kill (myData.iAlarmPID, 1);
							myData.iAlarmPID = 0;
						}
						GError *erreur = NULL;
						gchar **argv = g_strsplit (pAlarm->cCommand, " ", -1);
						g_spawn_async (NULL,
							argv,
							NULL,
							0,
							NULL,
							NULL,
							&myData.iAlarmPID,
							&erreur);
						if (erreur != NULL)
						{
							cd_warning ("clock : when trying to execute '%s' : %s", pAlarm->cCommand, erreur->message);
							g_error_free (erreur);
							myData.iAlarmPID = 0;
						}
						g_strfreev (argv);
						cd_message (" --> child_pid : %d", myData.iAlarmPID);
					}
				}
				
				if (bRemoveAlarm)
				{
					cd_message ("Cette alarme ne sera pas repetee");
					g_ptr_array_remove_index (myConfig.pAlarms, i);
					cd_clock_free_alarm (pAlarm);
					/// A FAIRE : effacer l'heure dans le fichier de conf pour cette alarme.
				}
			}
		}
		
		// display missed tasks.
		if (!myData.bTaskCheckedOnce)
		{
			myData.bTaskCheckedOnce = TRUE;
			myData.pMissedTasks = cd_clock_get_missed_tasks (myApplet);
		}
		if (myData.pMissedTasks != NULL)  // so if the dialog was closed before we could acknowledge all the tasks, it will re-open.
		{
			CDClockTask *pTask = myData.pMissedTasks->data;
			gchar *cMessage = _make_missed_task_message (pTask, myApplet);
			CairoDialogAttr attr;
			memset (&attr, 0, sizeof (CairoDialogAttr));
			attr.cText = cMessage;
			attr.bUseMarkup = TRUE;
			attr.cImageFilePath = (gchar *)MY_APPLET_SHARE_DATA_DIR"/icon-task.png";
			const gchar *cButtonsImage[3] = {"ok", NULL, NULL};
			if (myData.pMissedTasks->next != NULL)
			{
				cButtonsImage[0] = "cancel";
				cButtonsImage[1] = "next.png";
			}
			attr.cButtonsImage = cButtonsImage;
			attr.pActionFunc = (CairoDockActionOnAnswerFunc)_on_next_missed_task;
			attr.pUserData = myApplet;
			attr.pFreeDataFunc = NULL;
			attr.iTimeLength = 0;
			attr.pIcon = myIcon;
			attr.pContainer = myContainer;
			gldi_dialog_new (&attr);
			
			g_free (cMessage);
		}
		
		// display next task.
		if (myData.pNextTask != NULL)
		{
			//g_print ("next task : %s\n", myData.pNextTask->cTitle);
			struct tm st;
			st.tm_min = myData.pNextTask->iMinute;
			st.tm_hour = myData.pNextTask->iHour;
			st.tm_mday = myData.pNextTask->iDay;
			st.tm_mon = myData.pNextTask->iMonth;
			st.tm_year = myData.pNextTask->iYear - 1900;
			st.tm_sec = 0;
			st.tm_isdst = myData.currentTime.tm_isdst;
			time_t t = mktime (&st);
			//g_print ("time : %ld, task : %ld\n", epoch, t);
			if (t < epoch)  // la tache est depassee.
			{
				// acknowledge this task
				myData.pNextTask->bAcknowledged = TRUE;
				myData.pBackend->update_task (myData.pNextTask, myApplet);
				
				// look for next task.
				myData.pNextTask = cd_clock_get_next_scheduled_task (myApplet);
			}
			else if (t < epoch + 15*60 && t >= epoch)
			{
				if (t < epoch + 60)
				{
					if (! myData.pNextTask->bFirstWarning)
					{
						//g_print ("first warning\n");
						myData.pNextTask->bFirstWarning = TRUE;
						gchar *cText = g_strdup_printf ("%s\n<b>%s</b>\n %s\n\n%s",
							D_("It's time for the following task:"),
							myData.pNextTask->cTitle?myData.pNextTask->cTitle:D_("No title"),
							myData.pNextTask->cText?myData.pNextTask->cText:"",
							D_("Repeat this message every:"));
						_task_warning (myData.pNextTask, cText);
						g_free (cText);
					}
				}
				else if (! myData.pNextTask->b15mnWarning)
				{
					//g_print ("15 mn warning\n");
					myData.pNextTask->b15mnWarning = TRUE;
					
					gchar *cText = g_strdup_printf ("%s\n<b>%s</b>\n %s",
						D_("This task will begin in 15 minutes:"),
						myData.pNextTask->cTitle?myData.pNextTask->cTitle:D_("No title"),
						myData.pNextTask->cText?myData.pNextTask->cText:"");
					
					CairoDialogAttr attr;
					memset (&attr, 0, sizeof (CairoDialogAttr));
					attr.cText = (gchar *)cText;
					attr.cImageFilePath = (gchar *)MY_APPLET_SHARE_DATA_DIR"/icon-task.png";
					attr.iTimeLength = 60e3;
					attr.bUseMarkup = TRUE;
					attr.pIcon = myIcon;
					attr.pContainer = myContainer;
					gldi_dialog_new (&attr);
					CD_APPLET_DEMANDS_ATTENTION (NULL, 60);
				}
			}
			
			// display next anniversary if it is scheduled in less than 1 day, because anniversary require time to prepare.
			if (myData.pNextAnniversary != NULL)
			{
				if (!myData.pNextAnniversary->b1DayWarning && ! myData.pNextAnniversary->bFirstWarning && ! myData.pNextAnniversary->b15mnWarning)
				{
					GDate* pCurrentDate = g_date_new_dmy (myData.currentTime.tm_mday, myData.currentTime.tm_mon + 1, myData.currentTime.tm_year+1900);
					GDate* pAnnivDate = g_date_new_dmy (myData.pNextAnniversary->iDay, myData.pNextAnniversary->iMonth + 1, myData.currentTime.tm_year+1900);
					gint iDaysToNextAnniversary = g_date_days_between (pCurrentDate, pAnnivDate);
					if (iDaysToNextAnniversary >= 0 && iDaysToNextAnniversary <= 1)
					{
						myData.pNextAnniversary->b1DayWarning = TRUE;
						gchar *cText = g_strdup_printf ("%s\n<b>%s</b>\n %s\n\n%s",
							iDaysToNextAnniversary == 0 ? D_("Today is the following anniversary:") : D_("Tomorrow is the following anniversary:"),
							myData.pNextTask->cTitle?myData.pNextTask->cTitle:D_("No title"),
							myData.pNextTask->cText?myData.pNextTask->cText:"",
							D_("Repeat this message every:"));
						_task_warning (myData.pNextTask, cText);
						g_free (cText);
						myData.pNextAnniversary = cd_clock_get_next_anniversary (myApplet);
					}
					g_date_free (pCurrentDate);
					g_date_free (pAnnivDate);
				}
			}
		}
	}
	
	CD_APPLET_LEAVE(TRUE);
}
/* Update the icon on new song / status.
 */
void cd_musicplayer_update_icon (void)
{
	cd_message ("%s (uri : %s / title : %s)", __func__, myData.cPlayingUri, myData.cTitle);
	if (myData.cPlayingUri != NULL || myData.cTitle != NULL)
	{
		if (myData.iPlayingStatus == PLAYER_PLAYING || myData.iPlayingStatus == PLAYER_PAUSED)
		{
			// display current song on the label
			if (myDock)
			{
				if ((!myData.cArtist || !myData.cTitle) && myData.cPlayingUri)
				{
					gchar *str = strrchr (myData.cPlayingUri, '/');
					if (str)
						str ++;
					else
						str = myData.cPlayingUri;
					CD_APPLET_SET_NAME_FOR_MY_ICON (str);
				}
				else
					CD_APPLET_SET_NAME_FOR_MY_ICON_PRINTF ("%s - %s", myData.cArtist ? myData.cArtist : D_("Unknown artist"), myData.cTitle ? myData.cTitle : D_("Unknown title"));
			}
			
			// display current track on the quick-info.
			if (myConfig.iQuickInfoType == MY_APPLET_TRACK && myData.iTrackListLength > 0 && myData.iTrackListIndex > 0)
			{
				CD_APPLET_SET_QUICK_INFO_ON_MY_ICON_PRINTF ("%s%d", (myDesklet && myDesklet->container.iWidth >= 64 ? D_("Track") : ""), myData.iTrackListIndex);  // inutile de redessiner notre icone, ce sera fait plus loin.
			}
			else
			{
				CD_APPLET_SET_QUICK_INFO_ON_MY_ICON (NULL);
			}
			
			// animate the icon and pop-up the dialog.
			if (myData.iPlayingStatus == PLAYER_PLAYING)
			{
				cd_musicplayer_animate_icon (1);
				if(myConfig.bEnableDialogs)
				{
					cd_musicplayer_popup_info (myConfig.iDialogDuration);
				}
			}
		}
		/**else
		{
			cd_musicplayer_apply_status_surface (PLAYER_STOPPED);
			CD_APPLET_SET_NAME_FOR_MY_ICON (myData.cTitle ? myData.cTitle : myData.pCurrentHandler ? myData.pCurrentHandler->name : myConfig.cDefaultTitle);
		}*/
		
		// re-paint the icon if needed.
		if (myConfig.bEnableCover && myData.cover_exist && myData.cCoverPath != NULL)  // cover is available
		{
			if (cairo_dock_strings_differ (myData.cCoverPath, myData.cPreviousCoverPath))  // and cover has changed -> apply the new one
				cd_musiplayer_apply_cover ();
		}
		else  // no cover -> set the status surface.
		{
			if ((myConfig.bEnableCover && myData.cPreviousCoverPath != NULL)  // currently a cover is set
			|| myData.pPreviousPlayingStatus != myData.iPlayingStatus)  // or the status has changed
			cd_musicplayer_apply_status_surface (myData.iPlayingStatus);
		}
	}
	else  // aucune donnees, c'est soit un probleme soit le lecteur qui s'est ferme.
	{
		if (myData.bIsRunning)
		{
			cd_musicplayer_apply_status_surface (PLAYER_STOPPED);
			if (myConfig.cDefaultTitle)
			{
				CD_APPLET_SET_NAME_FOR_MY_ICON (myConfig.cDefaultTitle);
			}
			else if (myData.pCurrentHandler->cDisplayedName != NULL)
			{
				CD_APPLET_SET_NAME_FOR_MY_ICON (myData.pCurrentHandler->cDisplayedName);
			}
			else
			{
				CD_APPLET_SET_NAME_FOR_MY_ICON (myData.pCurrentHandler->name);
			}
		}
		else
		{
			cd_musicplayer_apply_status_surface (PLAYER_NONE);
			if (myConfig.cDefaultTitle)
			{
				CD_APPLET_SET_NAME_FOR_MY_ICON (myConfig.cDefaultTitle);
			}
			else
			{
				CD_APPLET_SET_NAME_FOR_MY_ICON (myApplet->pModule->pVisitCard->cTitle);
			}
		}
		CD_APPLET_SET_QUICK_INFO_ON_MY_ICON (NULL);
	}
}
gboolean cd_sysmonitor_update_from_data (CairoDockModuleInstance *myApplet)
{
	static double s_fValues[CD_SYSMONITOR_NB_MAX_VALUES];
	
	if ( ! myData.bAcquisitionOK)
	{
		cd_warning ("One or more datas couldn't be retrieved");
		CD_APPLET_SET_QUICK_INFO_ON_MY_ICON ("N/A");  // plus discret qu'une bulle de dialogue.
		if (myConfig.iInfoDisplay == CAIRO_DOCK_INFO_ON_LABEL)
			CD_APPLET_SET_NAME_FOR_MY_ICON (myConfig.defaultTitle);
		memset (s_fValues, 0, sizeof (s_fValues));
		CD_APPLET_RENDER_NEW_DATA_ON_MY_ICON (s_fValues);
	}
	else
	{
		if (! myData.bInitialized)
		{
			if (myConfig.iInfoDisplay == CAIRO_DOCK_INFO_ON_ICON)
				CD_APPLET_SET_QUICK_INFO_ON_MY_ICON (myDock ? "..." : D_("Loading"));
			memset (s_fValues, 0, sizeof (s_fValues));
			CD_APPLET_RENDER_NEW_DATA_ON_MY_ICON (s_fValues);
		}
		else
		{
			// Copier les donnes en memoire partagee...
			
			if (myConfig.iInfoDisplay == CAIRO_DOCK_INFO_ON_ICON || (myDock && myConfig.iInfoDisplay == CAIRO_DOCK_INFO_ON_LABEL))  // on affiche les valeurs soit en info-rapide, soit sur l'etiquette en mode dock.
			{
				gboolean bOneLine = (myConfig.iInfoDisplay == CAIRO_DOCK_INFO_ON_LABEL);
				GString *sInfo = g_string_new ("");
				if (myConfig.bShowCpu)
				{
					g_string_printf (sInfo, (myData.fCpuPercent < 10 ? "%s%.1f%%%s" : "%s%.0f%%%s"),
						(myDesklet ? "CPU:" : ""),
						myData.fCpuPercent,
						(bOneLine ? " - " : "\n"));
				}
				if (myConfig.bShowRam)
				{
					g_string_append_printf (sInfo, (myData.fRamPercent < 10 ? "%s%.1f%%%s" : "%s%.0f%%%s"),
						(myDesklet ? "RAM:" : ""),
						myData.fRamPercent,
						(bOneLine ? " - " : "\n"));
				}
				if (myConfig.bShowSwap)
				{
					g_string_append_printf (sInfo, (myData.fSwapPercent < 10 ? "%s%.1f%%%s" : "%s%.0f%%%s"),
						(myDesklet ? "SWAP:" : ""),
						myData.fSwapPercent,
						(bOneLine ? " - " : "\n"));
				}
				if (myConfig.bShowNvidia)
				{
					g_string_append_printf (sInfo, "%s%d°C%s",
						(myDesklet ? "GPU:" : ""),
						myData.iGPUTemp,
						(bOneLine ? " - " : "\n"));
				}
				sInfo->str[sInfo->len-(bOneLine?3:1)] = '\0';
				if (bOneLine)
					CD_APPLET_SET_NAME_FOR_MY_ICON (sInfo->str);
				else
					CD_APPLET_SET_QUICK_INFO_ON_MY_ICON (sInfo->str);
				g_string_free (sInfo, TRUE);
			}
			
			if (myData.bNeedsUpdate || myConfig.iDisplayType == CD_SYSMONITOR_GRAPH)
			{
				int i = 0;
				if (myConfig.bShowCpu)
				{
					s_fValues[i++] = (double) myData.fCpuPercent / 100;
				}
				if (myConfig.bShowRam)
				{
					s_fValues[i++] = myData.fRamPercent / 100;
				}
				if (myConfig.bShowSwap)
				{
					s_fValues[i++] = (double) (myData.swapTotal ? (myConfig.bShowFreeMemory ? myData.swapFree : myData.swapUsed) / myData.swapTotal : 0.);
				}
				if (myConfig.bShowNvidia)
				{
					s_fValues[i++] = myData.fGpuTempPercent / 100;
					if (myData.bAlerted && myData.iGPUTemp < myConfig.iAlertLimit)
						myData.bAlerted = FALSE; //On réinitialise l'alerte quand la température descend en dessou de la limite.
					
					if (!myData.bAlerted && myData.iGPUTemp >= myConfig.iAlertLimit)
						cd_nvidia_alert (myApplet);
				}
				CD_APPLET_RENDER_NEW_DATA_ON_MY_ICON (s_fValues);
			}
		}
	}
	return myData.bAcquisitionOK;
}