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 "string.h"
21 #include <glib/gi18n.h>
22 
23 #include "powermanager-struct.h"
24 #include "powermanager-draw.h"
25 
26 /* 3 states:
27 no battery -> empty gauge (0%), on-sector emblem, no alert
28 on battery -> gauge + %, no emblem, alert
29 on sector -> gauge + %, on-sector emblem, alert
30 */
31 
_set_data_renderer(GldiModuleInstance * myApplet)32 static void _set_data_renderer (GldiModuleInstance *myApplet)
33 {
34 	CairoDataRendererAttribute *pRenderAttr = NULL;  // generic attributes of the data-renderer.
35 	switch (myConfig.iDisplayType)
36 	{
37 		case CD_POWERMANAGER_GAUGE:
38 		{
39 			CairoGaugeAttribute *pGaugeAttr = g_new0 (CairoGaugeAttribute, 1);
40 			pRenderAttr = CAIRO_DATA_RENDERER_ATTRIBUTE (pGaugeAttr);
41 			pRenderAttr->cModelName = "gauge";
42 			pGaugeAttr->cThemePath = myConfig.cGThemePath;
43 		}
44 		break;
45 		case CD_POWERMANAGER_GRAPH:
46 		{
47 			CairoGraphAttribute *pGraphAttr = g_new0 (CairoGraphAttribute, 1);
48 			pRenderAttr = CAIRO_DATA_RENDERER_ATTRIBUTE (pGraphAttr);
49 			pRenderAttr->cModelName = "graph";
50 			pRenderAttr->iMemorySize = (myIcon->fWidth > 1 ? myIcon->fWidth : 32);  // fWidth peut etre <= 1 en mode desklet au chargement.
51 			pGraphAttr->iType = myConfig.iGraphType;
52 			pGraphAttr->fHighColor = myConfig.fHigholor;
53 			pGraphAttr->fLowColor = myConfig.fLowColor;
54 			memcpy (pGraphAttr->fBackGroundColor, myConfig.fBgColor, 4*sizeof (double));
55 		}
56 		break;
57 		case CD_POWERMANAGER_ICONS:
58 		{
59 			myData.iOnBatteryImage = -1;  // we'll set the image when we know the current state.
60 			CairoProgressBarAttribute *pBarAttr = g_new0 (CairoProgressBarAttribute, 1);
61 			pRenderAttr = CAIRO_DATA_RENDERER_ATTRIBUTE (pBarAttr);
62 			pRenderAttr->cModelName = "progressbar";
63 		}
64 		break;
65 
66 		default:
67 		return;
68 	}
69 
70 	if (myConfig.quickInfoType != 0)
71 	{
72 		pRenderAttr->bWriteValues = TRUE;
73 		pRenderAttr->format_value = (CairoDataRendererFormatValueFunc)cd_powermanager_format_value;
74 		pRenderAttr->pFormatData = myApplet;
75 	}
76 
77 	CD_APPLET_ADD_DATA_RENDERER_ON_MY_ICON (pRenderAttr);
78 	g_free (pRenderAttr);
79 }
80 
81 
update_icon(void)82 void update_icon (void)
83 {
84 	cd_debug ("%s (on battery: %d -> %d; time:%.1f -> %.1f ; charge:%.1f -> %.1f)", __func__, myData.bPrevOnBattery, myData.bOnBattery, (double)myData.iPrevTime, (double)myData.iTime, (double)myData.iPrevPercentage, (double)myData.iPercentage);
85 
86 	// hide the icon when not on battery or no information available (e.g. with a desktop)
87 	if (myConfig.bHideNotOnBattery && myDock && (! myData.bOnBattery))
88 	{
89 		if (! myData.bIsHidden)
90 		{ // we remove the icon
91 			gldi_icon_detach (myIcon);
92 			myData.bIsHidden = TRUE;
93 		}
94 		return; // no need any redraw if the icon is hidden, and can't display the dialog properly without the icon.
95 	}
96 	else if (myData.bIsHidden && myData.bOnBattery && myDock) // if the icon is hidden but we are now on battery, we (re-)insert the icon.
97 	{
98 		cd_debug ("Re-insert the icon");
99 		gldi_icon_insert_in_container (myIcon, myContainer, CAIRO_DOCK_ANIMATE_ICON);
100 		myData.bIsHidden = FALSE;
101 	}
102 
103 	// no information available or no battery, draw a default icon.
104 	if (! myData.bBatteryPresent)
105 	{
106 		CD_APPLET_REMOVE_MY_DATA_RENDERER;
107 		CD_APPLET_SET_IMAGE_ON_MY_ICON (MY_APPLET_SHARE_DATA_DIR"/sector.svg");
108 		CD_APPLET_REDRAW_MY_ICON;
109 		return;
110 	}
111 
112 	gboolean bInit = (cairo_dock_get_icon_data_renderer (myIcon) == NULL);
113 	if (bInit) // first time: init or reload
114 		_set_data_renderer (myApplet);
115 
116 	gboolean bChanged = (myData.bPrevOnBattery != myData.bOnBattery
117 	                     || myData.iPrevPercentage != myData.iPercentage
118 	                     || myData.iTime != myData.iPrevTime
119 	                     || bInit); // just to be sure
120 
121 	if (bChanged || myConfig.iDisplayType == CD_POWERMANAGER_GRAPH)  // graphs need to be updated each time, even if there is no change.
122 	{
123 		// update the icon representation
124 		if (myConfig.iDisplayType == CD_POWERMANAGER_ICONS)  // set the icon if needed
125 		{
126 			if (myData.iOnBatteryImage != myData.bOnBattery)
127 			{
128 				if (myData.bOnBattery)
129 					CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cUserBatteryIconName, "default-battery.svg");
130 				else
131 					CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cUserChargeIconName, "default-charge.svg");
132 				myData.iOnBatteryImage = myData.bOnBattery;
133 			}
134 		}
135 		// render the value
136 		double fPercent;
137 		if (! myData.bBatteryPresent)
138 			fPercent = CAIRO_DATA_RENDERER_UNDEF_VALUE;
139 		else
140 			fPercent = (double) myData.iPercentage / 100.;
141 		CD_APPLET_RENDER_NEW_DATA_ON_MY_ICON (&fPercent);
142 
143 		if (bChanged)
144 		{
145 			// add or remove the charge overlay if the 'on_battery' status has changed.
146 			if (myData.bPrevOnBattery != myData.bOnBattery || bInit)
147 			{
148 				if (! myData.bOnBattery)
149 				{
150 					CD_APPLET_ADD_OVERLAY_ON_MY_ICON (myConfig.cEmblemIconName ? myConfig.cEmblemIconName : MY_APPLET_SHARE_DATA_DIR"/charge.svg", CAIRO_OVERLAY_MIDDLE);
151 				}
152 				else
153 				{
154 					CD_APPLET_REMOVE_OVERLAY_ON_MY_ICON (CAIRO_OVERLAY_MIDDLE);
155 				}
156 			}
157 
158 			// trigger alarms.
159 			if (myData.bOnBattery)
160 			{
161 				// Alert when battery charge goes under a configured value in %
162 				if (myData.iPrevPercentage > myConfig.lowBatteryValue && myData.iPercentage <= myConfig.lowBatteryValue)
163 				{
164 					cd_powermanager_alert(POWER_MANAGER_CHARGE_LOW);
165 					if (myConfig.cSoundPath[POWER_MANAGER_CHARGE_LOW] != NULL)
166 						cairo_dock_play_sound (myConfig.cSoundPath[POWER_MANAGER_CHARGE_LOW]);
167 				}
168 				// Alert when battery charge is under 4%
169 				if (myData.iPrevPercentage > 4 && myData.iPercentage <= 4)
170 				{
171 					cd_powermanager_alert (POWER_MANAGER_CHARGE_CRITICAL);
172 					if (myConfig.cSoundPath[POWER_MANAGER_CHARGE_CRITICAL] != NULL)
173 						cairo_dock_play_sound (myConfig.cSoundPath[POWER_MANAGER_CHARGE_CRITICAL]);
174 				}
175 			}
176 			else
177 			{
178 				// Alert when battery is charged
179 				if(myData.iPrevPercentage > 0 && myData.iPrevPercentage < 100 && myData.iPercentage == 100)  // the first condition is to prevent the dialog on startup.
180 					cd_powermanager_alert (POWER_MANAGER_CHARGE_FULL);
181 			}
182 
183 			// update the icon's label.
184 			if (myConfig.defaultTitle == NULL || *myConfig.defaultTitle == '\0')
185 			{
186 				if (! myData.bOnBattery && myData.iPercentage > 99.9)
187 				{
188 					CD_APPLET_SET_NAME_FOR_MY_ICON_PRINTF ("%d%% - %s",
189 						(int)myData.iPercentage,
190 						D_("Battery charged"));
191 				}
192 				else
193 				{
194 					gchar cFormatBuffer[21];
195 					int iBufferLength = 20;
196 					if (myData.iTime != 0)
197 					{
198 						int time = myData.iTime;
199 						int hours = time / 3600;
200 						int minutes = (time % 3600) / 60;
201 						if (hours != 0)
202 							snprintf (cFormatBuffer, iBufferLength, "%d%s%02d", hours, D_("h"), abs (minutes));
203 						else
204 							snprintf (cFormatBuffer, iBufferLength, "%d%s", minutes, D_("mn"));
205 					}
206 					else
207 					{
208 						strncpy (cFormatBuffer, "-:--", iBufferLength);
209 					}
210 					CD_APPLET_SET_NAME_FOR_MY_ICON_PRINTF ("%d%% - %s %s",
211 						(int)myData.iPercentage,
212 						cFormatBuffer,
213 						myData.bOnBattery ? D_("remaining") : D_("until charged"));
214 				}
215 			}
216 
217 			myData.bPrevOnBattery = myData.bOnBattery;
218 			myData.iPrevPercentage = myData.iPercentage;
219 			myData.iPrevTime = myData.iTime;
220 		}
221 	}
222 }
223 
get_hours_minutes(int iTimeInSeconds)224 gchar *get_hours_minutes (int iTimeInSeconds)
225 {
226 	gchar *cTimeString;
227 	int h = iTimeInSeconds / 3600;
228 	int m = (iTimeInSeconds % 3600) / 60;
229 	if (h > 0) 		cTimeString = g_strdup_printf ("%dh%02dm", h, m);
230 	else if (m > 0) cTimeString = g_strdup_printf ("%dm", m);
231 	else 			cTimeString = g_strdup (D_("None"));
232 
233 	return cTimeString;
234 }
235 
_cd_powermanager_dialog(const gchar * cInfo,int iDuration)236 static void _cd_powermanager_dialog (const gchar *cInfo, int iDuration)
237 {
238 	gldi_dialogs_remove_on_icon (myIcon);
239 
240 	/**const gchar *cIconPath;
241 	if (!myData.bOnBattery)
242 		cIconPath = MY_APPLET_SHARE_DATA_DIR"/sector.svg";
243 	else
244 		cIconPath = MY_APPLET_SHARE_DATA_DIR"/default-battery.svg";
245 
246 	cd_debug ("%s (%s)", cInfo, cIconPath);*/
247 	gldi_dialog_show_temporary_with_icon (cInfo, myIcon, myContainer, 1000*iDuration, "same icon");
248 }
249 
cd_powermanager_bubble(void)250 void cd_powermanager_bubble (void)
251 {
252 	GString *sInfo = g_string_new ("");
253 	if (myData.cBatteryStateFilePath != NULL || myData.pBatteryDeviceList != NULL)
254 	{
255 		// time and charge.
256 		gchar *hms = NULL;
257 		if (myData.iTime > 0.)
258 			hms = get_hours_minutes (myData.iTime);
259 		else
260 			hms = g_strdup_printf ("%s", D_("Unknown"));
261 		if (myData.bOnBattery)
262 		{
263 			g_string_printf (sInfo, "%s\n"
264 				"%s %d%%\n"
265 				"%s %s",
266 				D_("Laptop on Battery."),
267 				D_("Battery charged at:"), (int)myData.iPercentage,
268 				D_("Estimated time before empty:"), hms);
269 		}
270 		else
271 		{
272 			g_string_printf (sInfo, "%s\n"
273 				"%s %d%%\n"
274 				"%s %s",
275 				D_("Laptop on Charge."),
276 				D_("Battery charged at:"), (int)myData.iPercentage,
277 				D_("Estimated time before full:"), (myData.iPercentage > 99.9 ? "0" : hms));
278 		}
279 		g_free (hms);
280 
281 		// static info
282 		if (myData.cVendor != NULL || myData.cModel != NULL)
283 		{
284 			g_string_append_printf (sInfo, "\n%s %s %s", D_("Model:"),
285 				myData.cVendor ? myData.cVendor : "",
286 				myData.cModel ? myData.cModel : "");
287 		}
288 		if (myData.cTechnology != NULL)
289 		{
290 			g_string_append_printf (sInfo, "\n%s %s", D_("Technology:"), myData.cTechnology);
291 		}
292 		if (myData.fMaxAvailableCapacity != 0)
293 		{
294 			g_string_append_printf (sInfo, "\n%s %d%%", D_("Maximum capacity:"), (int)myData.fMaxAvailableCapacity);
295 		}
296 	}
297 	else
298 	{
299 		g_string_assign (sInfo, D_("No battery found."));
300 	}
301 
302 	_cd_powermanager_dialog (sInfo->str, 7);
303 	g_string_free (sInfo, TRUE);
304 }
305 
cd_powermanager_alert(MyAppletCharge alert)306 gboolean cd_powermanager_alert (MyAppletCharge alert)
307 {
308 	cd_debug ("%s", __func__);
309 	GString *sInfo = g_string_new ("");
310 
311 	gchar *hms = NULL;
312 	if (myData.iTime > 0.)
313 		hms = get_hours_minutes (myData.iTime);
314 	else
315 		hms = g_strdup (D_("Unknown"));
316 
317 	if ((alert == POWER_MANAGER_CHARGE_LOW && myConfig.lowBatteryWitness) || (alert == POWER_MANAGER_CHARGE_CRITICAL && myConfig.criticalBatteryWitness))
318 	{
319 		if (myConfig.iNotificationType != 1)
320 		{
321 			g_string_printf (sInfo, "%s (%d%%) \n %s %s \n %s", D_("PowerManager.\nBattery charge seems to be low"), (int)myData.iPercentage, D_("Estimated time with charge:"), hms, D_("Please put your laptop on charge."));
322 			_cd_powermanager_dialog (sInfo->str, myConfig.iNotificationDuration);
323 		}
324 		if (myConfig.iNotificationType != 2)
325 		{
326 			CD_APPLET_DEMANDS_ATTENTION (myConfig.cNotificationAnimation, myConfig.iNotificationDuration);
327 		}
328 		if (myConfig.cSoundPath[alert] != NULL)
329 			cairo_dock_play_sound (myConfig.cSoundPath[alert]);
330 	}
331 	else if (alert == POWER_MANAGER_CHARGE_FULL && myConfig.highBatteryWitness)
332 	{
333 		if (myConfig.iNotificationType != 1)
334 		{
335 			g_string_printf (sInfo, "%s (%d%%)", D_("PowerManager.\nYour battery is now charged"), (int)myData.iPercentage);
336 			_cd_powermanager_dialog (sInfo->str, myConfig.iNotificationDuration);
337 		}
338 		if (! myData.bIsHidden && myConfig.iNotificationType != 2)
339 		{
340 			CD_APPLET_DEMANDS_ATTENTION (myConfig.cNotificationAnimation, myConfig.iNotificationDuration);
341 		}
342 		if (myConfig.cSoundPath[alert] != NULL)
343 			cairo_dock_play_sound (myConfig.cSoundPath[alert]);
344 	}
345 
346 	g_free (hms);
347 	g_string_free (sInfo, TRUE);
348 	return FALSE;
349 }
350 
351 
cd_powermanager_format_value(CairoDataRenderer * pRenderer,int iNumValue,gchar * cFormatBuffer,int iBufferLength,GldiModuleInstance * myApplet)352 void cd_powermanager_format_value (CairoDataRenderer *pRenderer, int iNumValue, gchar *cFormatBuffer, int iBufferLength, GldiModuleInstance *myApplet)
353 {
354 	// double fValue = cairo_data_renderer_get_normalized_current_value_with_latency (pRenderer, iNumValue);
355 	if(myConfig.quickInfoType == POWER_MANAGER_TIME)
356 	{
357 		if (myData.iTime != 0)
358 		{
359 			int time = myData.iTime;
360 			int hours = time / 3600;
361 			int minutes = (time % 3600) / 60;
362 			cd_debug ("time: %d -> %d;%d", time, hours, minutes);
363 			if (hours != 0)
364 				snprintf (cFormatBuffer, iBufferLength, "%dh%02d", hours, abs (minutes));
365 			else
366 				snprintf (cFormatBuffer, iBufferLength, "%dmn", minutes);
367 		}
368 		else
369 		{
370 			strncpy (cFormatBuffer, "-:--", iBufferLength);
371 		}
372 	}
373 	else if(myConfig.quickInfoType == POWER_MANAGER_CHARGE)
374 	{
375 		snprintf (cFormatBuffer, iBufferLength, "%d%%", (int)myData.iPercentage);
376 	}
377 	else
378 		cFormatBuffer[0] = '\0';
379 }
380