summaryrefslogtreecommitdiff
path: root/battstat/battstat_applet.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-11-14 18:24:48 -0300
committerPerberos <[email protected]>2011-11-14 18:24:48 -0300
commit312ba610a1e98fc656fb58178227d7d45a64494e (patch)
tree54a3c2b6084c80e63fb0526c6e7b8e01627acbd7 /battstat/battstat_applet.c
downloadmate-applets-312ba610a1e98fc656fb58178227d7d45a64494e.tar.bz2
mate-applets-312ba610a1e98fc656fb58178227d7d45a64494e.tar.xz
initial
Diffstat (limited to 'battstat/battstat_applet.c')
-rw-r--r--battstat/battstat_applet.c1714
1 files changed, 1714 insertions, 0 deletions
diff --git a/battstat/battstat_applet.c b/battstat/battstat_applet.c
new file mode 100644
index 00000000..f610b66c
--- /dev/null
+++ b/battstat/battstat_applet.c
@@ -0,0 +1,1714 @@
+/* battstat A MATE battery meter for laptops.
+ * Copyright (C) 2000 by Jörgen Pehrson <[email protected]>
+ * Copyright (C) 2002 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifdef HAVE_ERR_H
+#include <err.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <gtk/gtk.h>
+
+#include <mate-panel-applet.h>
+#include <mate-panel-applet-mateconf.h>
+
+#ifdef HAVE_LIBMATENOTIFY
+#include <libmatenotify/notify.h>
+#endif
+
+#include "battstat.h"
+#include "pixmaps.h"
+
+#ifndef gettext_noop
+#define gettext_noop(String) (String)
+#endif
+
+#define MATECONF_PATH ""
+
+static gboolean check_for_updates (gpointer data);
+static void about_cb( GtkAction *, ProgressData * );
+static void help_cb( GtkAction *, ProgressData * );
+
+static const GtkActionEntry battstat_menu_actions [] = {
+ { "BattstatProperties", GTK_STOCK_PROPERTIES, N_("_Preferences"),
+ NULL, NULL,
+ G_CALLBACK (prop_cb) },
+ { "BattstatHelp", GTK_STOCK_HELP, N_("_Help"),
+ NULL, NULL,
+ G_CALLBACK (help_cb) },
+ { "BattstatAbout", GTK_STOCK_ABOUT, N_("_About"),
+ NULL, NULL,
+ G_CALLBACK (about_cb) }
+};
+
+#define AC_POWER_STRING _("System is running on AC power")
+#define DC_POWER_STRING _("System is running on battery power")
+
+/* The icons for Battery, Critical, AC and Charging */
+static GdkPixmap *statusimage[STATUS_PIXMAP_NUM];
+static GdkBitmap *statusmask[STATUS_PIXMAP_NUM];
+
+/* Assuming a horizontal battery, the colour is drawn into it one horizontal
+ line at a time as a vertical gradient. The following arrays decide where
+ each horizontal line starts (the length of the lines varies with the
+ percentage battery life remaining).
+*/
+static const int pixel_offset_top[]={ 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5 };
+static const int pixel_top_length[]={ 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2 };
+static const int pixel_offset_bottom[]={ 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38 };
+
+
+/* The following array is the colour of each line. The (slightly) varying
+ colours are what makes for the gradient effect. The 'dark' colours are
+ used to draw the end of the bar, giving it more of a 3D look. The code
+ assumes that all of these arrays will have the same number of elements.
+*/
+static GdkColor green[] = {
+ {0,0x7A00,0xDB00,0x7000},
+ {0,0x9100,0xE900,0x8500},
+ {0,0xA000,0xF100,0x9500},
+ {0,0x9600,0xEE00,0x8A00},
+ {0,0x8E00,0xE900,0x8100},
+ {0,0x8500,0xE500,0x7700},
+ {0,0x7C00,0xDF00,0x6E00},
+ {0,0x7300,0xDA00,0x6500},
+ {0,0x6A00,0xD600,0x5B00},
+ {0,0x6000,0xD000,0x5100},
+ {0,0x5600,0xCA00,0x4600},
+ {0,0x5100,0xC100,0x4200},
+};
+
+static GdkColor red[] = {
+ {0,0xD900,0x7200,0x7400},
+ {0,0xE600,0x8800,0x8C00},
+ {0,0xF000,0x9600,0x9A00},
+ {0,0xEB00,0x8D00,0x9100},
+ {0,0xE700,0x8300,0x8800},
+ {0,0xE200,0x7A00,0x7F00},
+ {0,0xDD00,0x7100,0x7600},
+ {0,0xD800,0x6700,0x6D00},
+ {0,0xD300,0x5D00,0x6300},
+ {0,0xCD00,0x5400,0x5900},
+ {0,0xC800,0x4900,0x4F00},
+ {0,0xC100,0x4200,0x4700},
+};
+
+static GdkColor yellow[] = {
+ {0,0xD800,0xD900,0x7200},
+ {0,0xE600,0xE500,0x8800},
+ {0,0xF000,0xEF00,0x9600},
+ {0,0xEB00,0xEA00,0x8D00},
+ {0,0xE700,0xE600,0x8300},
+ {0,0xE200,0xE100,0x7A00},
+ {0,0xDD00,0xDC00,0x7100},
+ {0,0xD800,0xD700,0x6700},
+ {0,0xD300,0xD200,0x5D00},
+ {0,0xCD00,0xCC00,0x5400},
+ {0,0xC800,0xC600,0x4900},
+ {0,0xC100,0xBF00,0x4200},
+};
+
+static GdkColor orange[] = {
+ {0,0xD900,0xAD00,0x7200},
+ {0,0xE600,0xBB00,0x8800},
+ {0,0xF000,0xC700,0x9600},
+ {0,0xEB00,0xC000,0x8D00},
+ {0,0xE700,0xB900,0x8300},
+ {0,0xE200,0xB300,0x7A00},
+ {0,0xDD00,0xAB00,0x7100},
+ {0,0xD800,0xA400,0x6700},
+ {0,0xD300,0x9E00,0x5D00},
+ {0,0xCD00,0x9600,0x5400},
+ {0,0xC800,0x8D00,0x4900},
+ {0,0xC100,0x8600,0x4200},
+};
+
+static GdkColor darkgreen[] = {
+ {0,0x6500,0xC600,0x5B00},
+ {0,0x7B00,0xD300,0x6F00},
+ {0,0x8A00,0xDB00,0x7F00},
+ {0,0x8000,0xD800,0x7400},
+ {0,0x7800,0xD400,0x6B00},
+ {0,0x6F00,0xCF00,0x6200},
+ {0,0x6600,0xCA00,0x5900},
+ {0,0x5D00,0xC500,0x5000},
+ {0,0x5400,0xC100,0x4600},
+ {0,0x4B00,0xBB00,0x3C00},
+ {0,0x4100,0xB600,0x3100},
+ {0,0x3C00,0xAC00,0x2D00},
+};
+
+static GdkColor darkorange[] = {
+ {0,0xC400,0x9700,0x5C00},
+ {0,0xD000,0xA500,0x7200},
+ {0,0xDA00,0xB100,0x8000},
+ {0,0xD500,0xAA00,0x7700},
+ {0,0xD100,0xA300,0x6D00},
+ {0,0xCD00,0x9D00,0x6400},
+ {0,0xC700,0x9600,0x5B00},
+ {0,0xC300,0x8F00,0x5200},
+ {0,0xBE00,0x8800,0x4800},
+ {0,0xB800,0x8100,0x3F00},
+ {0,0xB300,0x7900,0x3400},
+ {0,0xAC00,0x7200,0x2D00},
+};
+
+static GdkColor darkyellow[] = {
+ {0,0xC200,0xC400,0x5C00},
+ {0,0xD000,0xCF00,0x7200},
+ {0,0xDA00,0xD900,0x8000},
+ {0,0xD500,0xD400,0x7700},
+ {0,0xD100,0xD000,0x6D00},
+ {0,0xCD00,0xCB00,0x6400},
+ {0,0xC700,0xC600,0x5B00},
+ {0,0xC300,0xC200,0x5200},
+ {0,0xBE00,0xBD00,0x4800},
+ {0,0xB800,0xB700,0x3F00},
+ {0,0xB300,0xB200,0x3400},
+ {0,0xAC00,0xAA00,0x2D00},
+};
+
+static GdkColor darkred[] = {
+ {0,0xC900,0x6200,0x6400},
+ {0,0xD600,0x7800,0x7C00},
+ {0,0xDA00,0x8000,0x8500},
+ {0,0xD500,0x7700,0x7B00},
+ {0,0xD100,0x6D00,0x7200},
+ {0,0xCD00,0x6400,0x6900},
+ {0,0xC700,0x5B00,0x6100},
+ {0,0xC300,0x5200,0x5700},
+ {0,0xBE00,0x4800,0x4E00},
+ {0,0xB800,0x3F00,0x4400},
+ {0,0xB100,0x3200,0x3700},
+ {0,0xA200,0x3200,0x3700},
+};
+
+/* Initialise the global static variables that store our status pixmaps from
+ their XPM format (as stored in pixmaps.h). This should only be done once
+ since they are global variables.
+*/
+static void
+initialise_global_pixmaps( void )
+{
+ GdkDrawable *defaults;
+
+ defaults = gdk_screen_get_root_window( gdk_screen_get_default() );
+
+ statusimage[STATUS_PIXMAP_BATTERY] =
+ gdk_pixmap_create_from_xpm_d( defaults, &statusmask[STATUS_PIXMAP_BATTERY],
+ NULL, battery_small_xpm );
+
+ statusimage[STATUS_PIXMAP_METER] =
+ gdk_pixmap_create_from_xpm_d( defaults, &statusmask[STATUS_PIXMAP_METER],
+ NULL, battery_small_meter_xpm );
+
+ statusimage[STATUS_PIXMAP_AC] =
+ gdk_pixmap_create_from_xpm_d( defaults, &statusmask[STATUS_PIXMAP_AC],
+ NULL, ac_small_xpm );
+
+ statusimage[STATUS_PIXMAP_CHARGE] =
+ gdk_pixmap_create_from_xpm_d( defaults, &statusmask[STATUS_PIXMAP_CHARGE],
+ NULL, charge_small_xpm );
+
+ statusimage[STATUS_PIXMAP_WARNING] =
+ gdk_pixmap_create_from_xpm_d( defaults, &statusmask[STATUS_PIXMAP_WARNING],
+ NULL, warning_small_xpm );
+}
+
+/* For non-truecolour displays, each GdkColor has to have a palette entry
+ allocated for it. This should only be done once for the entire display.
+*/
+static void
+allocate_battery_colours( void )
+{
+ GdkColormap *colourmap;
+ int i;
+
+ colourmap = gdk_colormap_get_system();
+
+ /* assumed: all the colour arrays have the same number of elements */
+ for( i = 0; i < G_N_ELEMENTS( orange ); i++ )
+ {
+ gdk_colormap_alloc_color( colourmap, &darkorange[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &darkyellow[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &darkred[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &darkgreen[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &orange[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &yellow[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &red[i], FALSE, TRUE );
+ gdk_colormap_alloc_color( colourmap, &green[i], FALSE, TRUE );
+ }
+}
+
+/* Our backends may be either event driven or poll-based.
+ * If they are event driven then we know this the first time we
+ * receive an event.
+ */
+static gboolean event_driven = FALSE;
+static GSList *instances;
+
+static void
+status_change_callback (void)
+{
+ GSList *instance;
+
+ for (instance = instances; instance; instance = instance->next)
+ {
+ ProgressData *battstat = instance->data;
+
+ if (battstat->timeout_id)
+ {
+ g_source_remove (battstat->timeout_id);
+ battstat->timeout_id = 0;
+ }
+
+ check_for_updates (battstat);
+ }
+
+ event_driven = TRUE;
+}
+
+/* The following two functions keep track of how many instances of the applet
+ are currently running. When the first instance is started, some global
+ initialisation is done. When the last instance exits, cleanup occurs.
+
+ The teardown code here isn't entirely complete (for example, it doesn't
+ deallocate the GdkColors or free the GdkPixmaps. This is OK so long
+ as the process quits immediately when the last applet is removed (which
+ it does.)
+*/
+static const char *
+static_global_initialisation (int no_hal, ProgressData *battstat)
+{
+ gboolean first_time;
+ const char *err;
+
+ first_time = !instances;
+
+ instances = g_slist_prepend (instances, battstat);
+
+ if (!first_time)
+ return NULL;
+
+ allocate_battery_colours();
+ initialise_global_pixmaps();
+ err = power_management_initialise (no_hal, status_change_callback);
+
+ return err;
+}
+
+static void
+static_global_teardown (ProgressData *battstat)
+{
+ instances = g_slist_remove (instances, battstat);
+
+ /* remaining instances... */
+ if (instances)
+ return;
+
+ /* instances == 0 */
+
+ power_management_cleanup();
+}
+
+/* Pop up an error dialog on the same screen as 'applet' saying 'msg'.
+ */
+static void
+battstat_error_dialog( GtkWidget *applet, const char *msg )
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new( NULL, 0, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK, "%s", msg);
+
+ gtk_window_set_screen( GTK_WINDOW (dialog),
+ gtk_widget_get_screen (GTK_WIDGET (applet)) );
+
+ g_signal_connect_swapped( GTK_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ GTK_OBJECT (dialog) );
+
+ gtk_widget_show_all( dialog );
+}
+
+/* Format a string describing how much time is left to fully (dis)charge
+ the battery. The return value must be g_free()d.
+*/
+static char *
+get_remaining (BatteryStatus *info)
+{
+ int hours;
+ int mins;
+
+ hours = info->minutes / 60;
+ mins = info->minutes % 60;
+
+ if (info->on_ac_power && !info->charging)
+ return g_strdup_printf (_("Battery charged (%d%%)"), info->percent);
+ else if (info->minutes < 0 && !info->on_ac_power)
+ return g_strdup_printf (_("Unknown time (%d%%) remaining"), info->percent);
+ else if (info->minutes < 0 && info->on_ac_power)
+ return g_strdup_printf (_("Unknown time (%d%%) until charged"), info->percent);
+ else
+ if (hours == 0)
+ if (!info->on_ac_power)
+ return g_strdup_printf (ngettext (
+ "%d minute (%d%%) remaining",
+ "%d minutes (%d%%) remaining",
+ mins), mins, info->percent);
+ else
+ return g_strdup_printf (ngettext (
+ "%d minute until charged (%d%%)",
+ "%d minutes until charged (%d%%)",
+ mins), mins, info->percent);
+ else if (mins == 0)
+ if (!info->on_ac_power)
+ return g_strdup_printf (ngettext (
+ "%d hour (%d%%) remaining",
+ "%d hours (%d%%) remaining",
+ hours), hours, info->percent);
+ else
+ return g_strdup_printf (ngettext (
+ "%d hour until charged (%d%%)",
+ "%d hours until charged (%d%%)",
+ hours), hours, info->percent);
+ else
+ if (!info->on_ac_power)
+ /* TRANSLATOR: "%d %s %d %s" are "%d hours %d minutes"
+ * Swap order with "%2$s %2$d %1$s %1$d if needed */
+ return g_strdup_printf (_("%d %s %d %s (%d%%) remaining"),
+ hours, ngettext ("hour", "hours", hours),
+ mins, ngettext ("minute", "minutes", mins),
+ info->percent);
+ else
+ /* TRANSLATOR: "%d %s %d %s" are "%d hours %d minutes"
+ * Swap order with "%2$s %2$d %1$s %1$d if needed */
+ return g_strdup_printf (_("%d %s %d %s until charged (%d%%)"),
+ hours, ngettext ("hour", "hours", hours),
+ mins, ngettext ("minute", "minutes", mins),
+ info->percent);
+}
+
+static gboolean
+battery_full_notify (GtkWidget *applet)
+{
+#ifdef HAVE_LIBMATENOTIFY
+ GError *error = NULL;
+ GdkPixbuf *icon;
+ gboolean result;
+
+ if (!notify_is_initted () && !notify_init (_("Battery Monitor")))
+ return FALSE;
+
+ icon = gtk_icon_theme_load_icon (
+ gtk_icon_theme_get_default (),
+ "battery",
+ 48,
+ GTK_ICON_LOOKUP_USE_BUILTIN,
+ NULL);
+
+ NotifyNotification *n = notify_notification_new (_("Your battery is now fully recharged"), "", /* "battery" */ NULL, applet);
+
+ /* XXX: it would be nice to pass this as a named icon */
+ notify_notification_set_icon_from_pixbuf (n, icon);
+ g_object_unref (icon);
+
+ result = notify_notification_show (n, &error);
+
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (G_OBJECT (n));
+
+ return result;
+#else
+ return FALSE;
+#endif
+}
+
+/* Show a dialog notifying the user that their battery is done charging.
+ */
+static void
+battery_full_dialog (GtkWidget *applet)
+{
+ /* first attempt to use libmatenotify */
+ if (battery_full_notify (applet))
+ return;
+
+ GtkWidget *dialog, *hbox, *image, *label;
+ GdkPixbuf *pixbuf;
+
+ gchar *new_label;
+ dialog = gtk_dialog_new_with_buttons (
+ _("Battery Notice"),
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ g_signal_connect_swapped (GTK_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ GTK_OBJECT (dialog));
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ hbox = gtk_hbox_new (FALSE, 6);
+ pixbuf = gtk_icon_theme_load_icon (
+ gtk_icon_theme_get_default (),
+ "battery",
+ 48,
+ GTK_ICON_LOOKUP_USE_BUILTIN,
+ NULL);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 6);
+ new_label = g_strdup_printf (
+ "<span weight=\"bold\" size=\"larger\">%s</span>",
+ _("Your battery is now fully recharged"));
+ label = gtk_label_new (new_label);
+ g_free (new_label);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 6);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox);
+ gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
+ gtk_window_stick (GTK_WINDOW (dialog));
+ gtk_window_set_skip_pager_hint (GTK_WINDOW (dialog), TRUE);
+ gtk_window_set_focus_on_map (GTK_WINDOW (dialog), FALSE);
+ gtk_widget_show_all (dialog);
+}
+
+/* Destroy the low battery notification dialog and mark it as such.
+ */
+static void
+battery_low_dialog_destroy( ProgressData *battstat )
+{
+ gtk_widget_destroy( battstat->battery_low_dialog );
+ battstat->battery_low_dialog = NULL;
+ battstat->battery_low_label = NULL;
+}
+
+/* Determine if suspend is unsupported. For the time being this involves
+ * distribution-specific magic :(
+ */
+/* #define HAVE_PMI */
+static gboolean
+is_suspend_unavailable( void )
+{
+#ifdef HAVE_PMI
+ int status;
+
+ status = system( "pmi query suspend" );
+
+ /* -1 - fail (pmi unavailable?). return 'false' since we don't know.
+ * 0 - success (can suspend). return 'false' since not unavailable.
+ * 1 - success (cannot suspend). return 'true' since unavailable.
+ */
+ if( WEXITSTATUS( status ) == 1 )
+ return TRUE;
+ else
+ return FALSE;
+#else
+ return FALSE; /* return 'false' since we don't know. */
+#endif
+}
+
+/* Update the text label in the battery low dialog.
+ */
+static void
+battery_low_update_text( ProgressData *battstat, BatteryStatus *info )
+{
+ const char *suggest;
+ gchar *remaining, *new_label;
+ GtkRequisition size;
+
+ /* If we're not displaying the dialog then don't update it. */
+ if( battstat->battery_low_label == NULL ||
+ battstat->battery_low_dialog == NULL )
+ return;
+
+ gtk_widget_size_request( GTK_WIDGET( battstat->battery_low_label ), &size );
+
+ /* If the label has never been set before, the width will be 0. If it
+ has been set before (width > 0) then we want to keep the size of
+ the old widget (to keep the dialog from changing sizes) so we set it
+ explicitly here.
+ */
+ if( size.width > 0 )
+ gtk_widget_set_size_request( GTK_WIDGET( battstat->battery_low_label ),
+ size.width, size.height );
+
+ if (info->minutes < 0 && !info->on_ac_power)
+ {
+ /* we don't know the remaining time */
+ remaining = g_strdup_printf (_("You have %d%% of your total battery "
+ "capacity remaining."), info->percent);
+ }
+ else
+ {
+ remaining = g_strdup_printf( ngettext(
+ "You have %d minute of battery power "
+ "remaining (%d%% of the total capacity).",
+ "You have %d minutes of battery power "
+ "remaining (%d%% of the total capacity).",
+ info->minutes ),
+ info->minutes,info->percent );
+ }
+
+ if( is_suspend_unavailable() )
+ /* TRANSLATORS: this is a list, it is left as a single string
+ * to allow you to make it appear like a list would in your
+ * locale. This is if the laptop does not support suspend. */
+ suggest = _("To avoid losing your work:\n"
+ " \xE2\x80\xA2 plug your laptop into external power, or\n"
+ " \xE2\x80\xA2 save open documents and shut your laptop down."
+ );
+ else
+ /* TRANSLATORS: this is a list, it is left as a single string
+ * to allow you to make it appear like a list would in your
+ * locale. This is if the laptop supports suspend. */
+ suggest = _("To avoid losing your work:\n"
+ " \xE2\x80\xA2 suspend your laptop to save power,\n"
+ " \xE2\x80\xA2 plug your laptop into external power, or\n"
+ " \xE2\x80\xA2 save open documents and shut your laptop down."
+ );
+
+ new_label = g_strdup_printf(
+ "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n\n%s",
+ _("Your battery is running low"), remaining, suggest );
+
+ gtk_label_set_markup( battstat->battery_low_label, new_label );
+ g_free( remaining );
+ g_free( new_label );
+}
+
+/* Show a dialog notifying the user that their battery is running low.
+ */
+static void
+battery_low_dialog( ProgressData *battery, BatteryStatus *info )
+{
+ GtkWidget *hbox, *image, *label;
+ GtkWidget *vbox;
+ GdkPixbuf *pixbuf;
+
+ /* If the dialog is already displayed then don't display it again. */
+ if( battery->battery_low_dialog != NULL )
+ return;
+
+ battery->battery_low_dialog = gtk_dialog_new_with_buttons (
+ _("Battery Notice"),
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_default_response( GTK_DIALOG (battery->battery_low_dialog),
+ GTK_RESPONSE_ACCEPT );
+
+ g_signal_connect_swapped( GTK_OBJECT (battery->battery_low_dialog),
+ "response",
+ G_CALLBACK (battery_low_dialog_destroy),
+ battery );
+
+ gtk_container_set_border_width (GTK_CONTAINER (battery->battery_low_dialog),
+ 6);
+ gtk_dialog_set_has_separator (GTK_DIALOG (battery->battery_low_dialog),
+ FALSE);
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ "battery",
+ 48,
+ GTK_ICON_LOOKUP_USE_BUILTIN,
+ NULL);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
+ label = gtk_label_new ("");
+ battery->battery_low_label = GTK_LABEL( label );
+ gtk_label_set_line_wrap( battery->battery_low_label, TRUE );
+ gtk_label_set_selectable( battery->battery_low_label, TRUE );
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (battery->battery_low_dialog))), hbox);
+
+ gtk_window_set_keep_above (GTK_WINDOW (battery->battery_low_dialog), TRUE);
+ gtk_window_stick (GTK_WINDOW (battery->battery_low_dialog));
+ gtk_window_set_focus_on_map (GTK_WINDOW (battery->battery_low_dialog),
+ FALSE);
+ gtk_window_set_skip_pager_hint (GTK_WINDOW (battery->battery_low_dialog),
+ TRUE);
+
+ battery_low_update_text( battery, info );
+
+ gtk_window_set_position (GTK_WINDOW (battery->battery_low_dialog),
+ GTK_WIN_POS_CENTER);
+ gtk_widget_show_all (battery->battery_low_dialog);
+}
+
+/* Update the text of the tooltip from the provided info.
+ */
+static void
+update_tooltip( ProgressData *battstat, BatteryStatus *info )
+{
+ gchar *powerstring;
+ gchar *remaining;
+ gchar *tiptext;
+
+ if (info->present)
+ {
+ if (info->on_ac_power)
+ powerstring = AC_POWER_STRING;
+ else
+ powerstring = DC_POWER_STRING;
+
+ remaining = get_remaining (info);
+
+ tiptext = g_strdup_printf ("%s\n%s", powerstring, remaining);
+ g_free (remaining);
+ }
+ else
+ {
+ if (info->on_ac_power)
+ tiptext = g_strdup_printf ("%s\n%s", AC_POWER_STRING,
+ _("No battery present"));
+ else
+ tiptext = g_strdup_printf ("%s\n%s", DC_POWER_STRING,
+ _("Battery status unknown"));
+ }
+
+ gtk_widget_set_tooltip_text (battstat->applet, tiptext);
+ g_free (tiptext);
+}
+
+/* Redraw the battery meter image.
+ */
+static void
+update_battery_image (ProgressData *battstat, int batt_percent, int batt_time)
+{
+ GdkColor *color, *darkcolor;
+ GdkPixmap *pixmap;
+ GdkBitmap *pixmask;
+ guint progress_value;
+ gint i, x;
+ int batt_life;
+
+ if (!battstat->showbattery)
+ return;
+
+ batt_life = !battstat->red_value_is_time ? batt_percent : batt_time;
+
+ if (batt_life <= battstat->red_val)
+ {
+ color = red;
+ darkcolor = darkred;
+ }
+ else if (batt_life <= battstat->orange_val)
+ {
+ color = orange;
+ darkcolor = darkorange;
+ }
+ else if (batt_life <= battstat->yellow_val)
+ {
+ color = yellow;
+ darkcolor = darkyellow;
+ }
+ else
+ {
+ color = green;
+ darkcolor = darkgreen;
+ }
+
+ /* We keep this pixgc allocated so we don't have to alloc/free it every
+ time. A widget has to be realized before it has a valid ->window so
+ we do that here for battstat->applet just in case it's not already done.
+ */
+ if( battstat->pixgc == NULL )
+ {
+ gtk_widget_realize( battstat->applet );
+ battstat->pixgc = gdk_gc_new( gtk_widget_get_window (battstat->applet) );
+ }
+
+ /* Depending on if the meter is horizontally oriented start out with the
+ appropriate XPM image (from pixmaps.h)
+ */
+ if (battstat->horizont)
+ pixmap = gdk_pixmap_create_from_xpm_d( gtk_widget_get_window (battstat->applet), &pixmask,
+ NULL, battery_gray_xpm );
+ else
+ pixmap = gdk_pixmap_create_from_xpm_d( gtk_widget_get_window (battstat->applet), &pixmask,
+ NULL, battery_y_gray_xpm );
+
+ /* The core code responsible for painting the battery meter. For each
+ colour in our gradient array, draw a vertical or horizontal line
+ depending on the current orientation of the meter.
+ */
+ if (battstat->draintop) {
+ progress_value = PROGLEN * batt_life / 100.0;
+
+ for( i = 0; i < G_N_ELEMENTS( orange ); i++ )
+ {
+ gdk_gc_set_foreground (battstat->pixgc, &color[i]);
+
+ if (battstat->horizont)
+ gdk_draw_line (pixmap, battstat->pixgc, pixel_offset_top[i], i + 2,
+ pixel_offset_top[i] + progress_value, i + 2);
+ else
+ gdk_draw_line (pixmap, battstat->pixgc, i + 2, pixel_offset_top[i],
+ i + 2, pixel_offset_top[i] + progress_value);
+ }
+ }
+ else
+ {
+ progress_value = PROGLEN * batt_life / 100.0;
+
+ for( i = 0; i < G_N_ELEMENTS( orange ); i++)
+ {
+ gdk_gc_set_foreground (battstat->pixgc, &color[i]);
+
+ if (battstat->horizont)
+ gdk_draw_line (pixmap, battstat->pixgc, pixel_offset_bottom[i], i + 2,
+ pixel_offset_bottom[i] - progress_value, i + 2);
+ else
+ gdk_draw_line (pixmap, battstat->pixgc, i + 2,
+ pixel_offset_bottom[i] - 1, i + 2,
+ pixel_offset_bottom[i] - progress_value);
+ }
+
+ for( i = 0; i < G_N_ELEMENTS( orange ); i++ )
+ {
+ x = pixel_offset_bottom[i] - progress_value - pixel_top_length[i];
+ if (x < pixel_offset_top[i])
+ x = pixel_offset_top[i];
+
+ if (progress_value < 33)
+ {
+ gdk_gc_set_foreground (battstat->pixgc, &darkcolor[i]);
+
+ if (battstat->horizont)
+ gdk_draw_line (pixmap, battstat->pixgc,
+ pixel_offset_bottom[i] - progress_value - 1,
+ i + 2, x, i + 2);
+ else
+ gdk_draw_line (pixmap, battstat->pixgc, i + 2,
+ pixel_offset_bottom[i] - progress_value - 1,
+ i + 2, x);
+ }
+ }
+ }
+
+ /* Store our newly created pixmap into the GtkImage. This results in
+ the last reference to the old pixmap/mask being dropped.
+ */
+ gtk_image_set_from_pixmap( GTK_IMAGE(battstat->battery),
+ pixmap, pixmask );
+
+ /* The GtkImage does not assume a reference to the pixmap or mask;
+ you still need to unref them if you own references. GtkImage will
+ add its own reference rather than adopting yours.
+ */
+ g_object_unref( G_OBJECT(pixmap) );
+ g_object_unref( G_OBJECT(pixmask) );
+}
+
+/* Update the text label that either shows the percentage of time left.
+ */
+static void
+update_percent_label( ProgressData *battstat, BatteryStatus *info )
+{
+ gchar *new_label;
+
+ if (info->present && battstat->showtext == APPLET_SHOW_PERCENT)
+ new_label = g_strdup_printf ("%d%%", info->percent);
+ else if (info->present && battstat->showtext == APPLET_SHOW_TIME)
+ {
+ /* Fully charged or unknown (-1) time remaining displays -:-- */
+ if ((info->on_ac_power && info->percent == 100) || info->minutes < 0)
+ new_label = g_strdup ("-:--");
+ else
+ {
+ int time;
+ time = info->minutes;
+ new_label = g_strdup_printf ("%d:%02d", time/60, time%60);
+ }
+ }
+ else
+ new_label = g_strdup (_("N/A"));
+
+ gtk_label_set_text (GTK_LABEL (battstat->percent), new_label);
+ g_free (new_label);
+}
+
+/* Utility function to create a copy of a GdkPixmap */
+static GdkPixmap *
+copy_gdk_pixmap( GdkPixmap *src, GdkGC *gc )
+{
+ gint height, width;
+ GdkPixmap *dest;
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ width = gdk_window_get_width(GDK_WINDOW(src));
+ height = gdk_window_get_height(GDK_WINDOW(src));
+ #else
+ gdk_drawable_get_size(GDK_DRAWABLE(src), &width, &height);
+ #endif
+
+ dest = gdk_pixmap_new( GDK_DRAWABLE( src ), width, height, -1 );
+
+ gdk_draw_drawable( GDK_DRAWABLE( dest ), gc, GDK_DRAWABLE( src ),
+ 0, 0, 0, 0, width, height );
+
+ return dest;
+}
+
+/* Determine what status icon we ought to be displaying and change the
+ status icon to display it if it is different from what we are currently
+ showing.
+ */
+static void
+possibly_update_status_icon( ProgressData *battstat, BatteryStatus *info )
+{
+ StatusPixmapIndex pixmap_index;
+ int batt_life, last_batt_life;
+
+ batt_life = !battstat->red_value_is_time ? info->percent : info->minutes;
+ last_batt_life = !battstat->red_value_is_time ? battstat->last_batt_life :
+ battstat->last_minutes;
+
+ if( info->on_ac_power )
+ {
+ if( info->charging )
+ pixmap_index = STATUS_PIXMAP_CHARGE;
+ else
+ pixmap_index = STATUS_PIXMAP_AC;
+ }
+ else /* on battery */
+ {
+ if (batt_life > battstat->red_val)
+ pixmap_index = STATUS_PIXMAP_BATTERY;
+ else
+ pixmap_index = STATUS_PIXMAP_WARNING;
+ }
+
+ /* If we are showing the full length battery meter then the status icon
+ should display static icons. If we are not showing the full meter
+ then the status icon will show a smaller meter if we are on battery.
+ */
+ if( !battstat->showbattery &&
+ (pixmap_index == STATUS_PIXMAP_BATTERY ||
+ pixmap_index == STATUS_PIXMAP_WARNING) )
+ pixmap_index = STATUS_PIXMAP_METER;
+
+
+ /* Take care of drawing the smaller meter. */
+ if( pixmap_index == STATUS_PIXMAP_METER &&
+ (batt_life != last_batt_life ||
+ battstat->last_pixmap_index != STATUS_PIXMAP_METER) )
+ {
+ GdkColor *colour;
+ GdkPixmap *meter;
+ guint progress_value;
+ gint i, x;
+
+ /* We keep this pixgc allocated so we don't have to alloc/free it every
+ time. A widget has to be realized before it has a valid ->window so
+ we do that here for battstat->applet just in case it's not already done.
+ */
+ if( battstat->pixgc == NULL )
+ {
+ gtk_widget_realize( battstat->applet );
+ battstat->pixgc = gdk_gc_new( gtk_widget_get_window (battstat->applet) );
+ }
+
+ /* Pull in a clean version of the icons so that we don't paint over
+ top of the same icon over and over. We neglect to free/update the
+ statusmask here since it will always stay the same.
+ */
+ meter = copy_gdk_pixmap( statusimage[STATUS_PIXMAP_METER],
+ battstat->pixgc );
+
+ if (batt_life <= battstat->red_val)
+ {
+ colour = red;
+ }
+ else if (batt_life <= battstat->orange_val)
+ {
+ colour = orange;
+ }
+ else if (batt_life <= battstat->yellow_val)
+ {
+ colour = yellow;
+ }
+ else
+ {
+ colour = green;
+ }
+
+ progress_value = 12 * info->percent / 100.0;
+
+ for( i = 0; i < 10; i++ )
+ {
+ gdk_gc_set_foreground( battstat->pixgc, &colour[(i * 13 / 10)] );
+
+ if( i >= 2 && i <= 7 )
+ x = 17;
+ else
+ x = 16;
+
+ gdk_draw_line( meter, battstat->pixgc,
+ i + 1, x - progress_value,
+ i + 1, x );
+ }
+
+ /* force a redraw immediately */
+ gtk_image_set_from_pixmap( GTK_IMAGE (battstat->status),
+ meter, statusmask[STATUS_PIXMAP_METER] );
+
+ /* free our private pixmap copy */
+ g_object_unref( G_OBJECT( meter ) );
+ battstat->last_pixmap_index = STATUS_PIXMAP_METER;
+ }
+ else if( pixmap_index != battstat->last_pixmap_index )
+ {
+ gtk_image_set_from_pixmap (GTK_IMAGE (battstat->status),
+ statusimage[pixmap_index],
+ statusmask[pixmap_index]);
+ battstat->last_pixmap_index = pixmap_index;
+ }
+}
+
+/* Gets called as a gtk_timeout once per second. Checks for updates and
+ makes any changes as appropriate.
+ */
+static gboolean
+check_for_updates( gpointer data )
+{
+ ProgressData *battstat = data;
+ BatteryStatus info;
+ const char *err;
+
+ if (DEBUG) g_print("check_for_updates()\n");
+
+ if( (err = power_management_getinfo( &info )) )
+ battstat_error_dialog( battstat->applet, err );
+
+ if (!event_driven)
+ {
+ int timeout;
+
+ /* if on AC and not event driven scale back the polls to once every 10 */
+ if (info.on_ac_power)
+ timeout = 10;
+ else
+ timeout = 2;
+
+ if (timeout != battstat->timeout)
+ {
+ battstat->timeout = timeout;
+
+ if (battstat->timeout_id)
+ g_source_remove (battstat->timeout_id);
+
+ battstat->timeout_id = g_timeout_add_seconds (battstat->timeout,
+ check_for_updates,
+ battstat);
+ }
+ }
+
+
+ possibly_update_status_icon( battstat, &info );
+
+ if (!info.on_ac_power &&
+ battstat->last_batt_life != 1000 &&
+ (
+ /* if percentage drops below red_val */
+ (!battstat->red_value_is_time &&
+ battstat->last_batt_life > battstat->red_val &&
+ info.percent <= battstat->red_val) ||
+ /* if time drops below red_val */
+ (battstat->red_value_is_time &&
+ battstat->last_minutes > battstat->red_val &&
+ info.minutes <= battstat->red_val)
+ ) &&
+ info.present)
+ {
+ /* Warn that battery dropped below red_val */
+ if(battstat->lowbattnotification)
+ {
+ battery_low_dialog(battstat, &info);
+
+ if(battstat->beep)
+ gdk_beep();
+ }
+#if 0
+ /* FIXME: mate-applets doesn't depend on libmate anymore */
+ mate_triggers_do ("", NULL, "battstat_applet", "LowBattery", NULL);
+#endif
+ }
+
+ if( battstat->last_charging &&
+ battstat->last_acline_status &&
+ battstat->last_acline_status!=1000 &&
+ !info.charging &&
+ info.on_ac_power &&
+ info.present &&
+ info.percent > 99)
+ {
+ /* Inform that battery now fully charged */
+#if 0
+ /* FIXME: mate-applets doesn't depend on libmate anymore */
+ mate_triggers_do ("", NULL, "battstat_applet", "BatteryFull", NULL);
+#endif
+
+ if(battstat->fullbattnot)
+ {
+ battery_full_dialog (battstat->applet);
+
+ if (battstat->beep)
+ gdk_beep();
+ }
+ }
+
+ /* If the warning dialog is displayed and we just got plugged in then
+ stop displaying it.
+ */
+ if( battstat->battery_low_dialog && info.on_ac_power )
+ battery_low_dialog_destroy( battstat );
+
+ if( info.on_ac_power != battstat->last_acline_status ||
+ info.percent != battstat->last_batt_life ||
+ info.minutes != battstat->last_minutes ||
+ info.charging != battstat->last_charging )
+ {
+ /* Update the tooltip */
+ update_tooltip( battstat, &info );
+
+ /* If the warning dialog box is currently displayed, update that too. */
+ if( battstat->battery_low_dialog != NULL )
+ battery_low_update_text( battstat, &info );
+ }
+
+ if( info.percent != battstat->last_batt_life )
+ {
+ /* Update the battery meter image */
+ update_battery_image (battstat, info.percent, info.minutes);
+ }
+
+ if( (battstat->showtext == APPLET_SHOW_PERCENT &&
+ battstat->last_batt_life != info.percent) ||
+ (battstat->showtext == APPLET_SHOW_TIME &&
+ battstat->last_minutes != info.minutes) ||
+ battstat->last_acline_status != info.on_ac_power ||
+ battstat->last_present != info.present ||
+ battstat->refresh_label ) /* set by properties dialog */
+ {
+ /* Update the label */
+ update_percent_label( battstat, &info );
+
+ /* done */
+ battstat->refresh_label = FALSE;
+ }
+
+ battstat->last_charging = info.charging;
+ battstat->last_batt_life = info.percent;
+ battstat->last_minutes = info.minutes;
+ battstat->last_acline_status = info.on_ac_power;
+ battstat->last_present = info.present;
+
+ return TRUE;
+}
+
+/* Gets called when the user removes the applet from the panel. Clean up
+ all instance-specific data and call the global teardown function to
+ decrease our applet count (and possibly perform global cleanup)
+ */
+static void
+destroy_applet( GtkWidget *widget, ProgressData *battstat )
+{
+ if (DEBUG) g_print("destroy_applet()\n");
+
+ if (battstat->prop_win)
+ gtk_widget_destroy (GTK_WIDGET (battstat->prop_win));
+
+ if( battstat->battery_low_dialog )
+ battery_low_dialog_destroy( battstat );
+
+ if (battstat->timeout_id)
+ g_source_remove (battstat->timeout_id);
+
+ if( battstat->pixgc )
+ g_object_unref( G_OBJECT(battstat->pixgc) );
+
+ g_object_unref( G_OBJECT(battstat->status) );
+ g_object_unref( G_OBJECT(battstat->percent) );
+ g_object_unref( G_OBJECT(battstat->battery) );
+
+ g_free( battstat );
+
+ static_global_teardown (battstat);
+}
+
+/* Common function invoked by the 'Help' context menu item and the 'Help'
+ * button in the preferences dialog.
+ */
+void
+battstat_show_help( ProgressData *battstat, const char *section )
+{
+ GError *error = NULL;
+ char *uri;
+
+ if (section)
+ uri = g_strdup_printf ("ghelp:battstat?%s", section);
+ else
+ uri = g_strdup ("ghelp:battstat");
+
+ gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (battstat->applet)),
+ uri,
+ gtk_get_current_event_time (),
+ &error);
+
+ g_free (uri);
+
+ if( error )
+ {
+ char *message;
+
+ message = g_strdup_printf( _("There was an error displaying help: %s"),
+ error->message );
+ battstat_error_dialog( battstat->applet, message );
+ g_error_free( error );
+ g_free( message );
+ }
+}
+
+
+/* Called when the user selects the 'help' menu item.
+ */
+static void
+help_cb( GtkAction *action, ProgressData *battstat )
+{
+ battstat_show_help( battstat, NULL );
+}
+
+/* Called when the user selects the 'about' menu item.
+ */
+static void
+about_cb( GtkAction *action, ProgressData *battstat )
+{
+ const gchar *authors[] = {
+ "J\xC3\xB6rgen Pehrson <[email protected]>",
+ "Lennart Poettering <[email protected]> (Linux ACPI support)",
+ "Seth Nickell <[email protected]> (MATE2 port)",
+ "Davyd Madeley <[email protected]>",
+ "Ryan Lortie <[email protected]>",
+ "Joe Marcus Clarke <[email protected]> (FreeBSD ACPI support)",
+ NULL
+ };
+
+ const gchar *documenters[] = {
+ "J\xC3\xB6rgen Pehrson <[email protected]>",
+ "Trevor Curtis <[email protected]>",
+ "Davyd Madeley <[email protected]>",
+ NULL
+ };
+
+ char *comments = g_strdup_printf ("%s\n\n%s",
+ _("This utility shows the status of your laptop battery."),
+ power_management_using_hal () ?
+ /* true */ _("HAL backend enabled.") :
+ /* false */ _("Legacy (non-HAL) backend enabled."));
+
+ gtk_show_about_dialog( NULL,
+ "version", VERSION,
+ "copyright", "\xC2\xA9 2000 The Gnulix Society, "
+ "\xC2\xA9 2002-2005 Free Software Foundation and "
+ "others",
+ "comments", comments,
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "battery",
+ NULL );
+
+ g_free (comments);
+}
+
+/* Rotate text on side panels. Called on initial startup and when the
+ * orientation changes (ie: the panel we were on moved or we moved to
+ * another panel).
+ */
+static void
+setup_text_orientation( ProgressData *battstat )
+{
+ if( battstat->orienttype == MATE_PANEL_APPLET_ORIENT_RIGHT )
+ gtk_label_set_angle( GTK_LABEL( battstat->percent ), 90 );
+ else if( battstat->orienttype == MATE_PANEL_APPLET_ORIENT_LEFT )
+ gtk_label_set_angle( GTK_LABEL( battstat->percent ), 270 );
+ else
+ gtk_label_set_angle( GTK_LABEL( battstat->percent ), 0 );
+}
+
+
+/* This signal is delivered by the panel when the orientation of the applet
+ has changed. This is either because the applet has just been created,
+ has just been moved to a new panel or the panel that the applet was on
+ has changed orientation.
+*/
+static void
+change_orient (MatePanelApplet *applet,
+ MatePanelAppletOrient orient,
+ ProgressData *battstat)
+{
+ if (DEBUG) g_print("change_orient()\n");
+
+ /* Ignore the update if we already know. */
+ if( orient != battstat->orienttype )
+ {
+ battstat->orienttype = orient;
+
+ /* The applet changing orientation very likely involves the layout
+ being changed to better fit the new shape of the panel.
+ */
+ setup_text_orientation( battstat );
+ reconfigure_layout( battstat );
+ }
+}
+
+/* This is delivered when our size has changed. This happens when the applet
+ is just created or if the size of the panel has changed.
+*/
+static void
+size_allocate( MatePanelApplet *applet, GtkAllocation *allocation,
+ ProgressData *battstat)
+{
+ if (DEBUG) g_print("applet_change_pixel_size()\n");
+
+ /* Ignore the update if we already know. */
+ if( battstat->width == allocation->width &&
+ battstat->height == allocation->height )
+ return;
+
+ battstat->width = allocation->width;
+ battstat->height = allocation->height;
+
+ /* The applet changing size could result in the layout changing. */
+ reconfigure_layout( battstat );
+}
+
+/* Get our settings out of mateconf.
+ */
+static void
+load_preferences(ProgressData *battstat)
+{
+ MatePanelApplet *applet = MATE_PANEL_APPLET (battstat->applet);
+
+ if (DEBUG) g_print("load_preferences()\n");
+
+ battstat->red_val = mate_panel_applet_mateconf_get_int (applet, MATECONF_PATH "red_value", NULL);
+ battstat->red_val = CLAMP (battstat->red_val, 0, 100);
+ battstat->red_value_is_time = mate_panel_applet_mateconf_get_bool (applet,
+ MATECONF_PATH "red_value_is_time",
+ NULL);
+
+ /* automatically calculate orangle and yellow values from the red value */
+ battstat->orange_val = battstat->red_val * ORANGE_MULTIPLIER;
+ battstat->orange_val = CLAMP (battstat->orange_val, 0, 100);
+
+ battstat->yellow_val = battstat->red_val * YELLOW_MULTIPLIER;
+ battstat->yellow_val = CLAMP (battstat->yellow_val, 0, 100);
+
+ battstat->lowbattnotification = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "low_battery_notification", NULL);
+ battstat->fullbattnot = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "full_battery_notification", NULL);
+ battstat->beep = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "beep", NULL);
+ battstat->draintop = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "drain_from_top", NULL);
+
+ battstat->showstatus = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "show_status", NULL);
+ battstat->showbattery = mate_panel_applet_mateconf_get_bool (applet, MATECONF_PATH "show_battery", NULL);
+
+ /* for miagration from older versions */
+ if (battstat->showstatus && battstat->showbattery)
+ battstat->showbattery = FALSE;
+
+ battstat->showtext = mate_panel_applet_mateconf_get_int (applet, MATECONF_PATH "show_text", NULL);
+}
+
+/* Convenience function to attach a child widget to a GtkTable in the
+ position indicated by 'loc'. This is very special-purpose for 3x3
+ tables and only supports positions that are used in this applet.
+ */
+static void
+table_layout_attach( GtkTable *table, LayoutLocation loc, GtkWidget *child )
+{
+ GtkAttachOptions flags;
+
+ flags = GTK_FILL | GTK_EXPAND;
+
+ switch( loc )
+ {
+ case LAYOUT_LONG:
+ gtk_table_attach( table, child, 1, 2, 0, 2, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_TOPLEFT:
+ gtk_table_attach( table, child, 0, 1, 0, 1, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_TOP:
+ gtk_table_attach( table, child, 1, 2, 0, 1, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_LEFT:
+ gtk_table_attach( table, child, 0, 1, 1, 2, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_CENTRE:
+ gtk_table_attach( table, child, 1, 2, 1, 2, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_RIGHT:
+ gtk_table_attach( table, child, 2, 3, 1, 2, flags, flags, 2, 2 );
+ break;
+
+ case LAYOUT_BOTTOM:
+ gtk_table_attach( table, child, 1, 2, 2, 3, flags, flags, 2, 2 );
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* The layout has (maybe) changed. Calculate what layout we ought to be
+ using and update some things if anything has changed. This is called
+ from size/orientation change callbacks and from the preferences dialog
+ when elements get added or removed.
+ */
+void
+reconfigure_layout( ProgressData *battstat )
+{
+ gboolean up_down_order = FALSE;
+ gboolean do_square = FALSE;
+ LayoutConfiguration c;
+ int battery_horiz = 0;
+ int needwidth;
+
+ /* Decide if we are doing to do 'square' mode. */
+ switch( battstat->orienttype )
+ {
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ up_down_order = TRUE;
+
+ /* Need to be at least 46px tall to do square mode on a horiz. panel */
+ if( battstat->height >= 46 )
+ do_square = TRUE;
+ break;
+
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ /* We need 64px to fix the text beside anything. */
+ if( battstat->showtext )
+ needwidth = 64;
+ /* We need 48px to fix the icon and battery side-by-side. */
+ else
+ needwidth = 48;
+
+ if( battstat->width >= needwidth )
+ do_square = TRUE;
+ break;
+ }
+
+ /* Default to no elements being displayed. */
+ c.status = c.text = c.battery = LAYOUT_NONE;
+
+ if( do_square )
+ {
+ /* Square mode is only useful if we have the battery meter shown. */
+ if( battstat->showbattery )
+ {
+ c.battery = LAYOUT_LONG;
+
+ /* if( battstat->showstatus ) */ /* make this always true */
+ c.status = LAYOUT_TOPLEFT;
+
+ if( battstat->showtext )
+ c.text = LAYOUT_LEFT;
+ }
+ else
+ {
+ /* We have enough room to do 'square' mode but the battery meter is
+ not requested. We can, instead, take up the extra space by stacking
+ our items in the opposite order that we normally would (ie: stack
+ horizontally on a vertical panel and vertically on horizontal).
+ */
+ up_down_order = !up_down_order;
+ do_square = FALSE;
+ }
+ }
+
+ if( !do_square )
+ {
+ if( up_down_order )
+ {
+ /* Stack horizontally for top and bottom panels. */
+ /* if( battstat->showstatus ) */ /* make this always true */
+ c.status = LAYOUT_LEFT;
+ if( battstat->showbattery )
+ c.battery = LAYOUT_CENTRE;
+ if( battstat->showtext )
+ c.text = LAYOUT_RIGHT;
+
+ battery_horiz = 1;
+ }
+ else
+ {
+ /* Stack vertically for left and right panels. */
+ /* if( battstat->showstatus ) */ /* make this always true */
+ c.status = LAYOUT_TOP;
+ if( battstat->showbattery )
+ c.battery = LAYOUT_CENTRE;
+ if( battstat->showtext )
+ c.text = LAYOUT_BOTTOM;
+ }
+ }
+
+ if( memcmp( &c, &battstat->layout, sizeof (LayoutConfiguration) ) )
+ {
+ /* Something in the layout has changed. Rebuild. */
+
+ /* Start by removing any elements in the table from the table. */
+ if( battstat->layout.text )
+ gtk_container_remove( GTK_CONTAINER( battstat->table ),
+ battstat->percent );
+ if( battstat->layout.status )
+ gtk_container_remove( GTK_CONTAINER( battstat->table ),
+ battstat->status );
+ if( battstat->layout.battery )
+ gtk_container_remove( GTK_CONTAINER( battstat->table ),
+ battstat->battery );
+
+ /* Attach the elements to their new locations. */
+ table_layout_attach( GTK_TABLE(battstat->table),
+ c.battery, battstat->battery );
+ table_layout_attach( GTK_TABLE(battstat->table),
+ c.status, battstat->status );
+ table_layout_attach( GTK_TABLE(battstat->table),
+ c.text, battstat->percent );
+
+ gtk_widget_show_all( battstat->applet );
+ }
+
+ /* If we are showing the battery meter and we weren't showing it before or
+ if the orientation has changed, we had better update it right now.
+ */
+ if( (c.battery && !battstat->layout.battery) ||
+ battery_horiz != battstat->horizont )
+ {
+ battstat->horizont = battery_horiz;
+ update_battery_image (battstat,
+ battstat->last_batt_life, battstat->last_minutes);
+ }
+
+ battstat->layout = c;
+
+ /* Check for generic updates. This is required, for example, to make sure
+ the text label is immediately updated to show the time remaining or
+ percentage.
+ */
+ check_for_updates( battstat );
+}
+
+/* Allocate the widgets for the applet and connect our signals.
+ */
+static gint
+create_layout(ProgressData *battstat)
+{
+ if (DEBUG) g_print("create_layout()\n");
+
+ /* Have our background automatically painted. */
+ mate_panel_applet_set_background_widget( MATE_PANEL_APPLET( battstat->applet ),
+ GTK_WIDGET( battstat->applet ) );
+
+ /* Allocate the four widgets that we need. */
+ battstat->table = gtk_table_new( 3, 3, FALSE );
+ battstat->percent = gtk_label_new( "" );
+ battstat->status = gtk_image_new();
+ battstat->battery = gtk_image_new();
+
+ /* When you first get a pointer to a newly created GtkWidget it has one
+ 'floating' reference. When you first add this widget to a container
+ the container adds a real reference and removes the floating reference
+ if one exists. Since we insert/remove these widgets from the table
+ when our layout is reconfigured, we need to keep our own 'real'
+ reference to each widget. This adds a real reference to each widget
+ and "sinks" the floating reference.
+ */
+ g_object_ref( battstat->status );
+ g_object_ref( battstat->percent );
+ g_object_ref( battstat->battery );
+ g_object_ref_sink( GTK_OBJECT( battstat->status ) );
+ g_object_ref_sink( GTK_OBJECT( battstat->percent ) );
+ g_object_ref_sink( GTK_OBJECT( battstat->battery ) );
+
+ /* Let reconfigure_layout know that the table is currently empty. */
+ battstat->layout.status = LAYOUT_NONE;
+ battstat->layout.text = LAYOUT_NONE;
+ battstat->layout.battery = LAYOUT_NONE;
+
+ /* Put the table directly inside the applet and show everything. */
+ gtk_container_add (GTK_CONTAINER (battstat->applet), battstat->table);
+ gtk_widget_show_all (battstat->applet);
+
+ /* Attach all sorts of signals to the applet. */
+ g_signal_connect(G_OBJECT(battstat->applet),
+ "destroy",
+ G_CALLBACK(destroy_applet),
+ battstat);
+
+ g_signal_connect (battstat->applet,
+ "change_orient",
+ G_CALLBACK(change_orient),
+ battstat);
+
+ g_signal_connect (battstat->applet,
+ "size_allocate",
+ G_CALLBACK (size_allocate),
+ battstat);
+
+ return FALSE;
+}
+
+/* Called by the factory to fill in the fields for the applet.
+ */
+static gboolean
+battstat_applet_fill (MatePanelApplet *applet)
+{
+ ProgressData *battstat;
+ AtkObject *atk_widget;
+ GtkActionGroup *action_group;
+ gchar *ui_path;
+ const char *err;
+ int no_hal;
+
+ if (DEBUG) g_print("main()\n");
+
+ g_set_application_name (_("Battery Charge Monitor"));
+
+ gtk_window_set_default_icon_name ("battery");
+
+ mate_panel_applet_add_preferences (applet, "/schemas/apps/battstat-applet/prefs",
+ NULL);
+ mate_panel_applet_set_flags (applet, MATE_PANEL_APPLET_EXPAND_MINOR);
+
+ battstat = g_new0 (ProgressData, 1);
+
+ /* Some starting values... */
+ battstat->applet = GTK_WIDGET (applet);
+ battstat->refresh_label = TRUE;
+ battstat->last_batt_life = 1000;
+ battstat->last_acline_status = 1000;
+ battstat->last_pixmap_index = 1000;
+ battstat->last_charging = 1000;
+ battstat->orienttype = mate_panel_applet_get_orient (applet);
+ battstat->horizont = TRUE;
+ battstat->battery_low_dialog = NULL;
+ battstat->battery_low_label = NULL;
+ battstat->pixgc = NULL;
+ battstat->timeout = -1;
+ battstat->timeout_id = 0;
+
+ /* The first received size_allocate event will cause a reconfigure. */
+ battstat->height = -1;
+ battstat->width = -1;
+
+ load_preferences (battstat);
+ create_layout (battstat);
+ setup_text_orientation( battstat );
+
+ action_group = gtk_action_group_new ("Battstat Applet Actions");
+ gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (action_group,
+ battstat_menu_actions,
+ G_N_ELEMENTS (battstat_menu_actions),
+ battstat);
+ ui_path = g_build_filename (BATTSTAT_MENU_UI_DIR, "battstat-applet-menu.xml", NULL);
+ mate_panel_applet_setup_menu_from_file (MATE_PANEL_APPLET (battstat->applet),
+ ui_path, action_group);
+ g_free (ui_path);
+
+ if (mate_panel_applet_get_locked_down (MATE_PANEL_APPLET (battstat->applet))) {
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (action_group, "BattstatProperties");
+ gtk_action_set_visible (action, FALSE);
+ }
+ g_object_unref (action_group);
+
+ atk_widget = gtk_widget_get_accessible (battstat->applet);
+ if (GTK_IS_ACCESSIBLE (atk_widget)) {
+ atk_object_set_name (atk_widget, _("Battery Charge Monitor"));
+ atk_object_set_description(atk_widget, _("Monitor a laptop's remaining power"));
+ }
+
+ no_hal = mate_panel_applet_mateconf_get_bool( applet, "no_hal", NULL );
+
+ if ((err = static_global_initialisation (no_hal, battstat)))
+ battstat_error_dialog (GTK_WIDGET (applet), err);
+
+ return TRUE;
+}
+
+/* Boilerplate... */
+static gboolean
+battstat_applet_factory (MatePanelApplet *applet,
+ const gchar *iid,
+ gpointer data)
+{
+ gboolean retval = FALSE;
+
+ if (!strcmp (iid, "BattstatApplet"))
+ retval = battstat_applet_fill (applet);
+
+ return retval;
+}
+
+
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("BattstatAppletFactory",
+ PANEL_TYPE_APPLET,
+ "battstat",
+ battstat_applet_factory,
+ NULL)
+
+