[gnome-control-center/wip/hadess/power-profiles] power: Add "Power Mode" section




commit 9d0f447f4c8e3838f57af87bbb8fa64299de3e74
Author: Bastien Nocera <hadess hadess net>
Date:   Thu Aug 6 23:33:10 2020 +0200

    power: Add "Power Mode" section
    
    Use power-profiles-daemon[1] to implement switchable power profiles.
    The performance profile will only be available on systems which provide
    this functionality.
    
    [1]: https://gitlab.freedesktop.org/hadess/power-profiles-daemon

 panels/power/cc-power-panel.c       | 316 ++++++++++++++++++++++++++++
 panels/power/cc-power-profile-row.c | 403 ++++++++++++++++++++++++++++++++++++
 panels/power/cc-power-profile-row.h |  54 +++++
 panels/power/meson.build            |   3 +-
 panels/power/power-profiles.css     |   7 +
 panels/power/power.gresource.xml    |   1 +
 po/POTFILES.in                      |   1 +
 7 files changed, 784 insertions(+), 1 deletion(-)
---
diff --git a/panels/power/cc-power-panel.c b/panels/power/cc-power-panel.c
index ec4d2e842..e1703f763 100644
--- a/panels/power/cc-power-panel.c
+++ b/panels/power/cc-power-panel.c
@@ -32,6 +32,7 @@
 #include "shell/cc-object-storage.h"
 #include "list-box-helper.h"
 #include "cc-brightness-scale.h"
+#include "cc-power-profile-row.h"
 #include "cc-power-panel.h"
 #include "cc-power-resources.h"
 #include "cc-util.h"
@@ -115,6 +116,11 @@ struct _CcPowerPanel
   GtkWidget     *als_switch;
   GtkWidget     *als_row;
 
+  GDBusProxy    *power_profiles_proxy;
+  guint          power_profiles_prop_id;
+  GtkWidget     *power_profiles_row[NUM_CC_POWER_PROFILES];
+  gboolean       power_profiles_in_update;
+
   GtkWidget     *power_button_combo;
   GtkWidget     *idle_delay_combo;
 
@@ -449,6 +455,7 @@ load_custom_css (CcPowerPanel *self)
   /* use custom CSS */
   provider = gtk_css_provider_new ();
   gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/power/battery-levels.css");
+  gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/power/power-profiles.css");
   gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
                                              GTK_STYLE_PROVIDER (provider),
                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -2119,6 +2126,314 @@ add_power_saving_section (CcPowerPanel *self)
 #endif
 }
 
+static void
+performance_profile_set_active (CcPowerPanel  *self,
+                                const char    *profile_str)
+{
+  CcPowerProfile profile = cc_power_profile_from_str (profile_str);
+  GtkRadioButton *button;
+
+  button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (self->power_profiles_row[profile]));
+  g_assert (button);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+}
+
+static void
+performance_profile_set_inhibited (CcPowerPanel  *self,
+                                   const char    *performance_inhibited)
+{
+  GtkWidget *row;
+
+  row = self->power_profiles_row[CC_POWER_PROFILE_PERFORMANCE];
+  g_assert (row != NULL);
+  cc_power_profile_row_set_performance_inhibited (CC_POWER_PROFILE_ROW (row),
+                                                  performance_inhibited);
+}
+
+static void
+power_profiles_row_activated_cb (GtkListBox    *box,
+                                 GtkListBoxRow *box_row,
+                                 gpointer       user_data)
+{
+  if (!gtk_widget_is_sensitive (GTK_WIDGET (box_row)))
+    return;
+
+  cc_power_profile_row_set_active (CC_POWER_PROFILE_ROW(box_row), TRUE);
+}
+
+static gint
+perf_profile_list_box_sort (GtkListBoxRow *row1,
+                            GtkListBoxRow *row2,
+                            gpointer       user_data)
+{
+  CcPowerProfile row1_profile, row2_profile;
+
+  row1_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row1));
+  row2_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row2));
+
+  if (row1_profile < row2_profile)
+    return -1;
+  if (row1_profile > row2_profile)
+    return 1;
+  return 0;
+}
+
+static const char *
+variant_lookup_string (GVariant   *dict,
+                       const char *key)
+{
+  GVariant *variant;
+
+  variant = g_variant_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
+  if (!variant)
+    return NULL;
+  return g_variant_get_string (variant, NULL);
+}
+
+static void
+power_profiles_properties_changed_cb (CcPowerPanel *self,
+                                      GVariant   *changed_properties,
+                                      GStrv       invalidated_properties,
+                                      GDBusProxy *proxy)
+{
+  g_autoptr(GVariantIter) iter = NULL;
+  const char *key;
+  g_autoptr(GVariant) value = NULL;
+
+  g_variant_get (changed_properties, "a{sv}", &iter);
+  while (g_variant_iter_next (iter, "{&sv}", &key, &value))
+    {
+      if (g_strcmp0 (key, "PerformanceInhibited") == 0)
+        {
+          performance_profile_set_inhibited (self,
+                                             g_variant_get_string (value, NULL));
+        }
+      else if (g_strcmp0 (key, "ActiveProfile") == 0)
+        {
+          self->power_profiles_in_update = TRUE;
+          performance_profile_set_active (self, g_variant_get_string (value, NULL));
+          self->power_profiles_in_update = FALSE;
+        }
+      else
+        {
+          g_debug ("Unhandled change on '%s' property", key);
+        }
+    }
+}
+
+static void
+set_active_profile_cb (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  g_autoptr(GVariant) variant = NULL;
+  g_autoptr(GError) error = NULL;
+
+  variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                           res, &error);
+  if (!variant)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Could not set active profile: %s", error->message);
+    }
+}
+
+static void
+power_profile_button_toggled_cb (CcPowerProfileRow *row,
+                                 gpointer         user_data)
+{
+  CcPowerPanel *self = user_data;
+  CcPowerProfile profile;
+  g_autoptr(GDBusConnection) connection = NULL;
+  g_autoptr(GError) error = NULL;
+
+  if (!cc_power_profile_row_get_active (row))
+    return;
+  if (self->power_profiles_in_update)
+    return;
+
+  profile = cc_power_profile_row_get_profile (row);
+
+  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+                               cc_panel_get_cancellable (CC_PANEL (self)),
+                               &error);
+  if (!connection)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("system bus not available: %s", error->message);
+      return;
+    }
+
+  g_dbus_connection_call (connection,
+                          "net.hadess.PowerProfiles",
+                          "/net/hadess/PowerProfiles",
+                          "org.freedesktop.DBus.Properties",
+                          "Set",
+                          g_variant_new ("(ssv)",
+                                         "net.hadess.PowerProfiles",
+                                         "ActiveProfile",
+                                         g_variant_new_string (cc_power_profile_to_str (profile))),
+                          NULL,
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          cc_panel_get_cancellable (CC_PANEL (self)),
+                          set_active_profile_cb,
+                          NULL);
+}
+
+static void
+add_power_profiles_section (CcPowerPanel *self)
+{
+  GtkWidget *widget, *box, *label, *row;
+  g_autofree gchar *s = NULL;
+  g_autoptr(GDBusConnection) connection = NULL;
+  g_autoptr(GVariant) variant = NULL;
+  g_autoptr(GVariant) props = NULL;
+  guint i, num_children;
+  g_autoptr(GError) error = NULL;
+  const char *performance_inhibited;
+  const char *active_profile;
+  g_autoptr(GVariant) profiles = NULL;
+  GtkRadioButton *last_button;
+
+  self->power_profiles_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
+                                                                         G_DBUS_PROXY_FLAGS_NONE,
+                                                                         "net.hadess.PowerProfiles",
+                                                                         "/net/hadess/PowerProfiles",
+                                                                         "net.hadess.PowerProfiles",
+                                                                         NULL,
+                                                                         &error);
+
+  if (!self->power_profiles_proxy)
+    {
+      g_debug ("Could not create Power Profiles proxy: %s", error->message);
+      return;
+    }
+
+  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+                               cc_panel_get_cancellable (CC_PANEL (self)),
+                               &error);
+  if (!connection)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("system bus not available: %s", error->message);
+      return;
+    }
+
+  variant = g_dbus_connection_call_sync (connection,
+                                         "net.hadess.PowerProfiles",
+                                         "/net/hadess/PowerProfiles",
+                                         "org.freedesktop.DBus.Properties",
+                                         "GetAll",
+                                         g_variant_new ("(s)",
+                                                        "net.hadess.PowerProfiles"),
+                                         NULL,
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1,
+                                         NULL,
+                                         &error);
+
+  if (!variant)
+    {
+      g_debug ("Failed to get properties for Power Profiles: %s",
+               error->message);
+      g_clear_object (&self->power_profiles_proxy);
+      return;
+    }
+
+  s = g_strdup_printf ("<b>%s</b>", _("Power Mode"));
+  label = gtk_label_new (s);
+  gtk_widget_show (label);
+  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_box_pack_start (GTK_BOX (self->vbox_power), label, FALSE, TRUE, 0);
+
+  label = gtk_label_new (_("Affects system performance and power usage."));
+  gtk_widget_show (label);
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_widget_set_margin_bottom (label, 6);
+  gtk_box_pack_start (GTK_BOX (self->vbox_power), label, FALSE, TRUE, 0);
+
+  widget = gtk_list_box_new ();
+  gtk_widget_show (widget);
+  self->boxes_reverse = g_list_prepend (self->boxes_reverse, widget);
+  g_signal_connect_object (widget, "keynav-failed", G_CALLBACK (keynav_failed), self, G_CONNECT_SWAPPED);
+  gtk_list_box_set_selection_mode (GTK_LIST_BOX (widget), GTK_SELECTION_NONE);
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
+                              perf_profile_list_box_sort,
+                              NULL, NULL);
+  g_signal_connect_object (G_OBJECT (widget), "row-activated",
+                           G_CALLBACK (power_profiles_row_activated_cb), NULL, 0);
+  gtk_list_box_set_header_func (GTK_LIST_BOX (widget),
+                                cc_list_box_update_header_func,
+                                NULL, NULL);
+
+  atk_object_add_relationship (ATK_OBJECT (gtk_widget_get_accessible (label)),
+                               ATK_RELATION_LABEL_FOR,
+                               ATK_OBJECT (gtk_widget_get_accessible (widget)));
+  atk_object_add_relationship (ATK_OBJECT (gtk_widget_get_accessible (widget)),
+                               ATK_RELATION_LABELLED_BY,
+                               ATK_OBJECT (gtk_widget_get_accessible (label)));
+
+  box = gtk_frame_new (NULL);
+  gtk_widget_show (box);
+  gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_IN);
+  gtk_widget_set_margin_bottom (box, 32);
+  gtk_container_add (GTK_CONTAINER (box), widget);
+  gtk_box_pack_start (GTK_BOX (self->vbox_power), box, FALSE, TRUE, 0);
+
+  props = g_variant_get_child_value (variant, 0);
+  performance_inhibited = variant_lookup_string (props, "PerformanceInhibited");
+  active_profile = variant_lookup_string (props, "ActiveProfile");
+
+  last_button = NULL;
+  profiles = g_variant_lookup_value (props, "Profiles", NULL);
+  num_children = g_variant_n_children (profiles);
+  for (i = 0; i < num_children; i++)
+    {
+      g_autoptr(GVariant) profile_variant;
+      const char *name;
+      GtkRadioButton *button;
+      CcPowerProfile profile;
+
+      profile_variant = g_variant_get_child_value (profiles, i);
+      if (!profile_variant ||
+          !g_variant_is_of_type (profile_variant, G_VARIANT_TYPE ("a{sv}")))
+        continue;
+
+      name = variant_lookup_string (profile_variant, "Profile");
+      if (!name)
+        continue;
+      g_debug ("Adding row for profile '%s' (driver: %s)",
+               name, variant_lookup_string (profile_variant, "Driver"));
+
+      profile = cc_power_profile_from_str (name);
+      row = cc_power_profile_row_new (cc_power_profile_from_str (name),
+                                      performance_inhibited);
+      g_signal_connect_object (G_OBJECT (row), "button-toggled",
+                               G_CALLBACK (power_profile_button_toggled_cb), self,
+                               0);
+      self->power_profiles_row[profile] = row;
+      gtk_widget_show (row);
+      gtk_container_add (GTK_CONTAINER (widget), row);
+      gtk_size_group_add_widget (self->row_sizegroup, row);
+
+      /* Connect radio button to group */
+      button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (row));
+      gtk_radio_button_join_group (button, last_button);
+      last_button = button;
+    }
+
+  self->power_profiles_in_update = TRUE;
+  performance_profile_set_active (self, active_profile);
+  self->power_profiles_in_update = FALSE;
+
+  self->power_profiles_prop_id = g_signal_connect_object (G_OBJECT (self->power_profiles_proxy), 
"g-properties-changed",
+                                                          G_CALLBACK (power_profiles_properties_changed_cb), 
self, G_CONNECT_SWAPPED);
+}
+
 static void
 add_battery_percentage (CcPowerPanel *self,
                         GtkListBox   *listbox)
@@ -2377,6 +2692,7 @@ cc_power_panel_init (CcPowerPanel *self)
 
   add_battery_section (self);
   add_device_section (self);
+  add_power_profiles_section (self);
   add_power_saving_section (self);
   add_general_section (self);
 
diff --git a/panels/power/cc-power-profile-row.c b/panels/power/cc-power-profile-row.c
new file mode 100644
index 000000000..c6934f27a
--- /dev/null
+++ b/panels/power/cc-power-profile-row.c
@@ -0,0 +1,403 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* cc-list-row.c
+ *
+ * Copyright 2020 Red Hat Inc.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s):
+ *   Bastien Nocera <hadess hadess net>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "cc-power-profile-row"
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include "cc-power-profile-row.h"
+
+struct _CcPowerProfileRow
+{
+  GtkListBoxRow parent_instance;
+
+  CcPowerProfile power_profile;
+  char *performance_inhibited;
+  GtkRadioButton *button;
+  GtkWidget *subtext;
+};
+
+G_DEFINE_TYPE (CcPowerProfileRow, cc_power_profile_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+  PROP_0,
+  PROP_POWER_PROFILE,
+  PROP_PERFORMANCE_INHIBITED,
+  N_PROPS
+};
+
+enum {
+  BUTTON_TOGGLED,
+  N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPS];
+static guint signals[N_SIGNALS];
+
+static const char *
+get_performance_inhibited_text (const char *inhibited)
+{
+  if (!inhibited || *inhibited == '\0')
+    return NULL;
+
+  if (g_str_equal (inhibited, "lap-detected"))
+    return _("Lap detected: performance mode unavailable");
+  if (g_str_equal (inhibited, "high-operating-temperature"))
+    return _("High hardware temperature: performance mode unavailable");
+  return _("Performance mode unavailable");
+}
+
+static void
+performance_profile_set_inhibited (CcPowerProfileRow *row,
+                                   const char        *performance_inhibited)
+{
+  const char *text;
+  gboolean inhibited = FALSE;
+
+  if (row->power_profile != CC_POWER_PROFILE_PERFORMANCE)
+    return;
+
+  gtk_style_context_remove_class (gtk_widget_get_style_context (row->subtext),
+                                  GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_style_context_remove_class (gtk_widget_get_style_context (row->subtext),
+                                  GTK_STYLE_CLASS_ERROR);
+
+  text = get_performance_inhibited_text (performance_inhibited);
+  if (text)
+    inhibited = TRUE;
+  else
+    text = _("High performance and power usage.");
+  gtk_label_set_text (GTK_LABEL (row->subtext), text);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (row->subtext),
+                               inhibited ? GTK_STYLE_CLASS_ERROR : GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_sensitive (GTK_WIDGET (row), !inhibited);
+}
+
+static void
+cc_power_profile_row_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  CcPowerProfileRow *self = (CcPowerProfileRow *)object;
+
+  switch (prop_id)
+    {
+    case PROP_POWER_PROFILE:
+      g_value_set_int (value, self->power_profile);
+      break;
+
+    case PROP_PERFORMANCE_INHIBITED:
+      g_value_set_string (value, self->performance_inhibited);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_power_profile_row_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  CcPowerProfileRow *row = (CcPowerProfileRow *)object;
+
+  switch (prop_id)
+    {
+    case PROP_POWER_PROFILE:
+      g_assert (row->power_profile == -1);
+      row->power_profile = g_value_get_int (value);
+      g_assert (row->power_profile != -1);
+      break;
+
+    case PROP_PERFORMANCE_INHIBITED:
+      cc_power_profile_row_set_performance_inhibited (row, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static GtkWidget *
+performance_row_new (const gchar  *title,
+                     const gchar  *icon_name,
+                     const gchar  *class_name,
+                     const gchar  *subtitle)
+{
+  PangoAttrList *attributes;
+  GtkWidget *grid, *button, *label, *image;
+  GtkStyleContext *context;
+
+  grid = gtk_grid_new ();
+  g_object_set (G_OBJECT (grid),
+                "margin-top", 6,
+                "margin-bottom", 6,
+                NULL);
+  gtk_widget_show (grid);
+
+  button = gtk_radio_button_new (NULL);
+  g_object_set (G_OBJECT (button),
+                "margin-end", 18,
+                "margin-start", 6,
+                NULL);
+  gtk_widget_show (button);
+  g_object_set_data (G_OBJECT (grid), "button", button);
+  gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 2);
+
+  image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+  gtk_widget_set_margin_end (image, 6);
+  gtk_widget_show (image);
+  gtk_grid_attach (GTK_GRID (grid), image, 1, 0, 1, 1);
+
+  context = gtk_widget_get_style_context (image);
+  gtk_style_context_add_class (context, "power-profile");
+  if (class_name != NULL)
+    gtk_style_context_add_class (context, class_name);
+
+  label = gtk_label_new (title);
+  g_object_set (G_OBJECT (label),
+                "ellipsize", PANGO_ELLIPSIZE_END,
+                "halign", GTK_ALIGN_START,
+                "expand", TRUE,
+                "use-markup", TRUE,
+                "use-underline", TRUE,
+                "visible", TRUE,
+                "xalign", 0.0,
+                NULL);
+  gtk_widget_show (label);
+  gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
+
+  attributes = pango_attr_list_new ();
+  pango_attr_list_insert (attributes, pango_attr_scale_new (0.9));
+
+  label = gtk_label_new (subtitle);
+  g_object_set (G_OBJECT (label),
+                "ellipsize", PANGO_ELLIPSIZE_END,
+                "halign", GTK_ALIGN_START,
+                "expand", TRUE,
+                "use-markup", TRUE,
+                "use-underline", TRUE,
+                "visible", TRUE,
+                "xalign", 0.0,
+                "attributes", attributes,
+                NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (label),
+                               GTK_STYLE_CLASS_DIM_LABEL);
+  g_object_set_data (G_OBJECT (grid), "subtext", label);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 2, 1);
+
+  pango_attr_list_unref (attributes);
+
+  return grid;
+}
+
+static void
+cc_power_profile_row_button_toggled_cb (GObject *row)
+{
+  g_signal_emit (row, signals[BUTTON_TOGGLED], 0);
+}
+
+static void
+cc_power_profile_row_constructed (GObject *object)
+{
+  CcPowerProfileRow *row;
+  GtkWidget *box, *title;
+  const char *text, *subtext, *icon_name, *class_name;
+
+  row = CC_POWER_PROFILE_ROW (object);
+
+  switch (row->power_profile)
+    {
+      case CC_POWER_PROFILE_PERFORMANCE:
+        text = _("Performance");
+        subtext = _("High performance and power usage.");
+        icon_name = "power-profile-performance-symbolic";
+        class_name = "performance";
+        break;
+      case CC_POWER_PROFILE_BALANCED:
+        text = _("Balanced Power");
+        subtext = _("Standard performance and power usage.");
+        icon_name = "power-profile-balanced-symbolic";
+        class_name = NULL;
+        break;
+      case CC_POWER_PROFILE_POWER_SAVER:
+        text = _("Power Saver");
+        subtext = _("Reduced performance and power usage.");
+        icon_name = "power-profile-power-saver-symbolic";
+        class_name = "low-power";
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  gtk_list_box_row_set_selectable (GTK_LIST_BOX_ROW (row), FALSE);
+  gtk_widget_show (GTK_WIDGET (row));
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  g_object_set (G_OBJECT (box),
+                "margin-end", 12,
+                "margin-start", 12,
+                "visible", TRUE,
+                NULL);
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  title = performance_row_new (text, icon_name, class_name, subtext);
+  row->subtext = g_object_get_data (G_OBJECT (title), "subtext");
+  row->button = g_object_get_data (G_OBJECT (title), "button");
+  g_signal_connect_object (G_OBJECT (row->button), "toggled",
+                           G_CALLBACK (cc_power_profile_row_button_toggled_cb),
+                           row, G_CONNECT_SWAPPED);
+  if (row->power_profile == CC_POWER_PROFILE_PERFORMANCE)
+    performance_profile_set_inhibited (row, row->performance_inhibited);
+  gtk_box_pack_start (GTK_BOX (box), title, TRUE, TRUE, 0);
+}
+
+static void
+cc_power_profile_row_class_init (CcPowerProfileRowClass *klass)
+{
+  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = cc_power_profile_row_get_property;
+  object_class->set_property = cc_power_profile_row_set_property;
+  object_class->constructed = cc_power_profile_row_constructed;
+
+  properties[PROP_POWER_PROFILE] =
+    g_param_spec_int ("power-profile",
+                      "Power Profile",
+                      "Power profile for the row",
+                      -1, CC_POWER_PROFILE_POWER_SAVER,
+                      -1,
+                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+  properties[PROP_PERFORMANCE_INHIBITED] =
+    g_param_spec_string ("performance-inhibited",
+                         "Performance Inhibited",
+                         "Performance inhibition reason",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  signals[BUTTON_TOGGLED] =
+    g_signal_new ("button-toggled",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  0, NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE, 0);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+cc_power_profile_row_init (CcPowerProfileRow *row)
+{
+  row->power_profile = -1;
+}
+
+CcPowerProfile
+cc_power_profile_row_get_profile (CcPowerProfileRow *row)
+{
+  g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (row), -1);
+
+  return row->power_profile;
+}
+
+GtkRadioButton *
+cc_power_profile_row_get_radio_button (CcPowerProfileRow *row)
+{
+  g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (row), NULL);
+
+  return row->button;
+}
+
+void
+cc_power_profile_row_set_active (CcPowerProfileRow *row,
+                                 gboolean           active)
+{
+  g_return_if_fail (CC_IS_POWER_PROFILE_ROW (row));
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row->button), active);
+}
+
+void
+cc_power_profile_row_set_performance_inhibited (CcPowerProfileRow *row,
+                                                const char        *performance_inhibited)
+{
+  g_return_if_fail (CC_IS_POWER_PROFILE_ROW (row));
+
+  g_clear_pointer (&row->performance_inhibited, g_free);
+  row->performance_inhibited = g_strdup (performance_inhibited);
+  performance_profile_set_inhibited (row, row->performance_inhibited);
+}
+
+gboolean
+cc_power_profile_row_get_active (CcPowerProfileRow *self)
+{
+  g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (self), FALSE);
+
+  return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button));
+}
+
+GtkWidget *
+cc_power_profile_row_new (CcPowerProfile  power_profile,
+                          const char     *performance_inhibited)
+{
+  return g_object_new (CC_TYPE_POWER_PROFILE_ROW,
+                       "power-profile", power_profile,
+                       "performance-inhibited", performance_inhibited,
+                       NULL);
+}
+
+CcPowerProfile
+cc_power_profile_from_str (const char *profile)
+{
+  if (g_strcmp0 (profile, "power-saver") == 0)
+    return CC_POWER_PROFILE_POWER_SAVER;
+  if (g_strcmp0 (profile, "balanced") == 0)
+    return CC_POWER_PROFILE_BALANCED;
+  if (g_strcmp0 (profile, "performance") == 0)
+    return CC_POWER_PROFILE_PERFORMANCE;
+
+  g_assert_not_reached ();
+}
+
+const char *
+cc_power_profile_to_str (CcPowerProfile profile)
+{
+  switch (profile)
+  {
+  case CC_POWER_PROFILE_POWER_SAVER:
+    return "power-saver";
+  case CC_POWER_PROFILE_BALANCED:
+    return "balanced";
+  case CC_POWER_PROFILE_PERFORMANCE:
+    return "performance";
+  default:
+    g_assert_not_reached ();
+  }
+}
diff --git a/panels/power/cc-power-profile-row.h b/panels/power/cc-power-profile-row.h
new file mode 100644
index 000000000..72b3df93f
--- /dev/null
+++ b/panels/power/cc-power-profile-row.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* cc-list-row.h
+ *
+ * Copyright 2020 Red Hat Inc
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s):
+ *   Bastien Nocera <hadess hadess net>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  CC_POWER_PROFILE_PERFORMANCE = 0,
+  CC_POWER_PROFILE_BALANCED    = 1,
+  CC_POWER_PROFILE_POWER_SAVER = 2,
+  NUM_CC_POWER_PROFILES
+} CcPowerProfile;
+
+#define CC_TYPE_POWER_PROFILE_ROW (cc_power_profile_row_get_type())
+G_DECLARE_FINAL_TYPE (CcPowerProfileRow, cc_power_profile_row, CC, POWER_PROFILE_ROW, GtkListBoxRow)
+
+GtkWidget *cc_power_profile_row_new                   (CcPowerProfile  power_profile,
+                                                       const char     *performance_inhibited);
+CcPowerProfile cc_power_profile_row_get_profile       (CcPowerProfileRow *row);
+GtkRadioButton *cc_power_profile_row_get_radio_button (CcPowerProfileRow *row);
+void cc_power_profile_row_set_active                  (CcPowerProfileRow *row, gboolean active);
+gboolean cc_power_profile_row_get_active              (CcPowerProfileRow *self);
+void cc_power_profile_row_set_performance_inhibited   (CcPowerProfileRow *row,
+                                                       const char        *performance_inhibited);
+
+CcPowerProfile cc_power_profile_from_str (const char *profile);
+const char *cc_power_profile_to_str      (CcPowerProfile profile);
+
+G_END_DECLS
diff --git a/panels/power/meson.build b/panels/power/meson.build
index 1700c91e8..90317af21 100644
--- a/panels/power/meson.build
+++ b/panels/power/meson.build
@@ -19,7 +19,8 @@ i18n.merge_file(
 
 sources = files(
   'cc-brightness-scale.c',
-  'cc-power-panel.c'
+  'cc-power-panel.c',
+  'cc-power-profile-row.c',
 )
 
 sources += gnome.mkenums_simple(
diff --git a/panels/power/power-profiles.css b/panels/power/power-profiles.css
new file mode 100644
index 000000000..1c3149320
--- /dev/null
+++ b/panels/power/power-profiles.css
@@ -0,0 +1,7 @@
+.power-profile.low-power {
+  color: @success_color;
+}
+
+.power-profile.performance {
+  color: @error_color;
+}
diff --git a/panels/power/power.gresource.xml b/panels/power/power.gresource.xml
index f0bcb1a62..3c78a16e4 100644
--- a/panels/power/power.gresource.xml
+++ b/panels/power/power.gresource.xml
@@ -3,5 +3,6 @@
   <gresource prefix="/org/gnome/control-center/power">
     <file preprocess="xml-stripblanks">cc-power-panel.ui</file>
     <file>battery-levels.css</file>
+    <file>power-profiles.css</file>
   </gresource>
 </gresources>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8873e50bf..a66018e8f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -151,6 +151,7 @@ panels/online-accounts/gnome-online-accounts-panel.desktop.in.in
 panels/online-accounts/online-accounts.ui
 panels/power/cc-power-panel.c
 panels/power/cc-power-panel.ui
+panels/power/cc-power-profile-row.c
 panels/power/gnome-power-panel.desktop.in.in
 panels/printers/authentication-dialog.ui
 panels/printers/cc-printers-panel.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]