[gnome-control-center] display: Add natural light UI to the display panel
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center] display: Add natural light UI to the display panel
- Date: Fri, 10 Feb 2017 09:07:41 +0000 (UTC)
commit ef4dfe88964da393dc9cf2bca72e5da83f43df5b
Author: Richard Hughes <richard hughsie com>
Date: Mon Feb 6 14:37:42 2017 +0000
display: Add natural light UI to the display panel
https://bugzilla.gnome.org/show_bug.cgi?id=778326
configure.ac | 1 +
panels/display/Makefile.am | 19 +-
panels/display/cc-display-panel.c | 81 ++++
panels/display/cc-natural-light-dialog.c | 683 ++++++++++++++++++++++++++++++
panels/display/cc-natural-light-dialog.h | 37 ++
panels/display/cc-natural-light-widget.c | 285 +++++++++++++
panels/display/cc-natural-light-widget.h | 41 ++
panels/display/display.gresource.xml | 8 +
panels/display/display.ui | 549 ++++++++++++++++++++++++
panels/display/icons/16x16/sunrise.png | Bin 0 -> 348 bytes
panels/display/icons/16x16/sunset.png | Bin 0 -> 361 bytes
11 files changed, 1703 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 01dd4a8..bb87390 100644
--- a/configure.ac
+++ b/configure.ac
@@ -129,6 +129,7 @@ PKG_CHECK_MODULES(DATETIME_PANEL, $COMMON_MODULES
polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION
gdk-pixbuf-2.0 >= $GDKPIXBUF_REQUIRED_VERSION)
PKG_CHECK_MODULES(DISPLAY_PANEL, $COMMON_MODULES gnome-desktop-3.0 >= 3.1.0
+ colord >= $COLORD_REQUIRED_VERSION
upower-glib >= 0.99.0)
PKG_CHECK_MODULES(INFO_PANEL, $COMMON_MODULES libgtop-2.0
polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION)
diff --git a/panels/display/Makefile.am b/panels/display/Makefile.am
index c03696f..a9b32a6 100644
--- a/panels/display/Makefile.am
+++ b/panels/display/Makefile.am
@@ -3,14 +3,29 @@ cappletname = display
noinst_LTLIBRARIES = libdisplay.la
+BUILT_SOURCES = \
+ cc-display-resources.h \
+ cc-display-resources.c
+
libdisplay_la_SOURCES = \
+ $(BUILT_SOURCES) \
cc-display-panel.c \
cc-display-panel.h \
+ cc-natural-light-dialog.c \
+ cc-natural-light-dialog.h \
+ cc-natural-light-widget.c \
+ cc-natural-light-widget.h \
scrollarea.c \
scrollarea.h
libdisplay_la_LIBADD = $(PANEL_LIBS) $(DISPLAY_PANEL_LIBS) $(LIBM)
+resource_files = $(shell glib-compile-resources --sourcedir=$(srcdir) --sourcedir=$(srcdir)/icons/16x16
--generate-dependencies $(srcdir)/display.gresource.xml)
+cc-display-resources.c: display.gresource.xml $(resource_files)
+ $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir)
--sourcedir=$(srcdir)/icons/16x16 --generate-source --c-name cc_display $<
+cc-display-resources.h: display.gresource.xml $(resource_files)
+ $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir)
--sourcedir=$(srcdir)/icons/16x16 --generate-header --c-name cc_display $<
+
# You will need a recent intltool or the patch from this bug
# http://bugzilla.gnome.org/show_bug.cgi?id=462312
@INTLTOOL_POLICY_RULE@
@@ -34,9 +49,11 @@ desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop)
AM_CPPFLAGS = $(PANEL_CFLAGS) \
$(DISPLAY_PANEL_CFLAGS) \
+ -DDATADIR="\"$(datadir)\""\
-DGNOMELOCALEDIR="\"$(datadir)/locale\""
-CLEANFILES = $(Desktop_in_files) $(desktop_DATA)
+CLEANFILES = $(Desktop_in_files) $(desktop_DATA) $(BUILT_SOURCES)
+EXTRA_DIST = $(resource_files) display.gresource.xml
if MAINTAINER_MODE
gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c
index 026e4ba..b132850 100644
--- a/panels/display/cc-display-panel.c
+++ b/panels/display/cc-display-panel.c
@@ -34,6 +34,9 @@
#include "shell/list-box-helper.h"
#include <libupower-glib/upower.h>
+#include "cc-natural-light-dialog.h"
+#include "cc-display-resources.h"
+
CC_PANEL_REGISTER (CcDisplayPanel, cc_display_panel)
#define DISPLAY_PANEL_PRIVATE(o) \
@@ -84,6 +87,10 @@ struct _CcDisplayPanelPrivate
GtkWidget *rotate_right_button;
GtkWidget *dialog;
GtkWidget *config_grid;
+ GtkWidget *natural_light_filter_label;
+ CcNaturalLightDialog *natural_light_dialog;
+
+ GSettings *settings_color;
UpClient *up_client;
gboolean lid_is_closed;
@@ -241,6 +248,8 @@ cc_display_panel_dispose (GObject *object)
g_clear_object (&priv->up_client);
g_clear_object (&priv->background);
g_clear_object (&priv->thumbnail_factory);
+ g_clear_object (&priv->settings_color);
+ g_clear_object (&priv->natural_light_dialog);
if (priv->dialog)
{
@@ -2604,6 +2613,17 @@ show_setup_dialog (CcDisplayPanel *panel)
}
static void
+cc_display_panel_natural_light_activated (GtkListBox *listbox,
+ GtkWidget *row,
+ CcDisplayPanel *panel)
+{
+ CcDisplayPanelPrivate *priv = panel->priv;
+ GtkWindow *toplevel;
+ toplevel = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel))));
+ cc_natural_light_dialog_present (priv->natural_light_dialog, toplevel);
+}
+
+static void
cc_display_panel_box_row_activated (GtkListBox *listbox,
GtkWidget *row,
CcDisplayPanel *panel)
@@ -2756,13 +2776,38 @@ sensor_proxy_vanished_cb (GDBusConnection *connection,
}
static void
+natural_light_enabled_recheck (CcDisplayPanel *self)
+{
+ CcDisplayPanelPrivate *priv = DISPLAY_PANEL_PRIVATE (self);
+ gboolean ret = g_settings_get_boolean (priv->settings_color,
+ "natural-light-enabled");
+ gtk_label_set_label (GTK_LABEL (priv->natural_light_filter_label),
+ /* TRANSLATORS: the state of the natural light setting */
+ ret ? _("On") : _("Off"));
+}
+
+static void
+settings_color_changed_cb (GSettings *settings, gchar *key, gpointer user_data)
+{
+ CcDisplayPanel *self = CC_DISPLAY_PANEL (user_data);
+ if (g_strcmp0 (key, "natural-light-enabled") == 0)
+ natural_light_enabled_recheck (self);
+}
+
+static void
cc_display_panel_init (CcDisplayPanel *self)
{
CcDisplayPanelPrivate *priv;
GtkWidget *frame, *vbox;
+ GtkListBoxRow *row;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *natural_light_listbox;
GError *error = NULL;
GSettings *settings;
+ g_resources_register (cc_display_get_resource ());
+
priv = self->priv = DISPLAY_PANEL_PRIVATE (self);
settings = g_settings_new ("org.gnome.desktop.background");
@@ -2772,6 +2817,7 @@ cc_display_panel_init (CcDisplayPanel *self)
priv->thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+ priv->natural_light_dialog = cc_natural_light_dialog_new ();
priv->screen = gnome_rr_screen_new (gdk_screen_get_default (), &error);
if (!priv->screen)
@@ -2814,6 +2860,36 @@ cc_display_panel_init (CcDisplayPanel *self)
gtk_widget_set_halign (priv->arrange_button, GTK_ALIGN_CENTER);
gtk_container_add (GTK_CONTAINER (vbox), priv->arrange_button);
+
+ /* natural light section */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ natural_light_listbox = gtk_list_box_new ();
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (natural_light_listbox),
+ GTK_SELECTION_NONE);
+ gtk_container_add (GTK_CONTAINER (frame), natural_light_listbox);
+ g_signal_connect (natural_light_listbox, "row-activated",
+ G_CALLBACK (cc_display_panel_natural_light_activated),
+ self);
+ row = GTK_LIST_BOX_ROW (gtk_list_box_row_new ());
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
+ gtk_container_add (GTK_CONTAINER (row), box);
+ gtk_container_add (GTK_CONTAINER (natural_light_listbox), GTK_WIDGET (row));
+ label = gtk_label_new (_("_Natural Light Filter"));
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
+ gtk_widget_set_margin_start (label, 20);
+ gtk_widget_set_margin_end (label, 20);
+ gtk_widget_set_margin_top (label, 6);
+ gtk_widget_set_margin_bottom (label, 6);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+ priv->natural_light_filter_label = label = gtk_label_new ("");
+ gtk_widget_set_halign (label, GTK_ALIGN_END);
+ gtk_widget_set_margin_start (label, 24);
+ gtk_widget_set_margin_end (label, 24);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), frame);
+
gtk_widget_show_all (vbox);
on_screen_changed (self);
@@ -2838,6 +2914,11 @@ cc_display_panel_init (CcDisplayPanel *self)
else
g_clear_object (&self->priv->up_client);
+ priv->settings_color = g_settings_new ("org.gnome.settings-daemon.plugins.color");
+ g_signal_connect (priv->settings_color, "changed",
+ G_CALLBACK (settings_color_changed_cb), self);
+ natural_light_enabled_recheck (self);
+
g_signal_connect (self, "map", G_CALLBACK (mapped_cb), NULL);
self->priv->shell_cancellable = g_cancellable_new ();
diff --git a/panels/display/cc-natural-light-dialog.c b/panels/display/cc-natural-light-dialog.c
new file mode 100644
index 0000000..9147698
--- /dev/null
+++ b/panels/display/cc-natural-light-dialog.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <gdesktop-enums.h>
+#include <gtk/gtk.h>
+#include <math.h>
+
+#include "cc-natural-light-dialog.h"
+#include "cc-natural-light-widget.h"
+
+struct _CcNaturalLightDialog {
+ GObject parent;
+ GtkBuilder *builder;
+ GSettings *settings_display;
+ GSettings *settings_clock;
+ GDBusProxy *proxy_color;
+ GDBusProxy *proxy_color_props;
+ GCancellable *cancellable;
+ GtkWidget *natural_light_widget;
+ GtkWidget *main_window;
+ gboolean ignore_value_changed;
+ guint timer_id;
+ GDesktopClockFormat clock_format;
+};
+
+G_DEFINE_TYPE (CcNaturalLightDialog, cc_natural_light_dialog, G_TYPE_OBJECT);
+
+#define CLOCK_SCHEMA "org.gnome.desktop.interface"
+#define DISPLAY_SCHEMA "org.gnome.settings-daemon.plugins.color"
+#define CLOCK_FORMAT_KEY "clock-format"
+
+void
+cc_natural_light_dialog_present (CcNaturalLightDialog *self, GtkWindow *parent)
+{
+ GtkWindow *window = GTK_WINDOW (self->main_window);
+ if (parent != NULL)
+ {
+ gtk_window_set_transient_for (window, parent);
+ gtk_window_set_modal (window, TRUE);
+ }
+ gtk_window_present (window);
+}
+
+static void
+cc_natural_light_dialog_finalize (GObject *object)
+{
+ CcNaturalLightDialog *self = CC_NATURAL_LIGHT_DIALOG (object);
+
+ if (self->cancellable != NULL)
+ {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ }
+
+ if (self->main_window)
+ {
+ gtk_widget_destroy (self->main_window);
+ self->main_window = NULL;
+ }
+
+ g_object_unref (self->builder);
+ g_object_unref (self->proxy_color);
+ g_object_unref (self->proxy_color_props);
+ g_object_unref (self->settings_display);
+ g_object_unref (self->settings_clock);
+ if (self->timer_id > 0)
+ g_source_remove (self->timer_id);
+
+ G_OBJECT_CLASS (cc_natural_light_dialog_parent_class)->finalize (object);
+}
+
+static void
+cc_natural_light_dialog_class_init (CcNaturalLightDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = cc_natural_light_dialog_finalize;
+}
+
+static gdouble
+frac_day_from_dt (GDateTime *dt)
+{
+ return g_date_time_get_hour (dt) +
+ (gdouble) g_date_time_get_minute (dt) / 60.f +
+ (gdouble) g_date_time_get_second (dt) / 3600.f;
+}
+
+static void
+dialog_adjustments_set_frac_hours (CcNaturalLightDialog *self,
+ gdouble value,
+ const gchar *id_hours,
+ const gchar *id_mins,
+ const gchar *id_stack)
+{
+ GtkAdjustment *adj;
+ gdouble hours;
+ gdouble mins = 0.f;
+ gboolean is_pm = FALSE;
+ gboolean is_24h;
+ GtkWidget *widget;
+
+ /* display the right thing for AM/PM */
+ is_24h = self->clock_format == G_DESKTOP_CLOCK_FORMAT_24H;
+ hours = floor (value);
+ if (!is_24h)
+ {
+ if (hours > 12)
+ {
+ hours -= 12;
+ is_pm = TRUE;
+ }
+ else if (hours < 1.0)
+ {
+ hours += 12;
+ is_pm = FALSE;
+ }
+ else if (hours == 12.f)
+ {
+ is_pm = TRUE;
+ }
+ }
+ if (value > 0.f)
+ {
+ mins = fmod (value, hours) * 60.f;
+ mins = fmod (mins, 60.f);
+ }
+ g_debug ("setting adjustment %.3f to %.0f:%02.0f", value, hours, mins);
+
+ self->ignore_value_changed = TRUE;
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, id_hours));
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (adj), hours);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, id_mins));
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (adj), mins);
+ self->ignore_value_changed = FALSE;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, id_stack));
+ if (is_24h)
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "blank");
+ else
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), is_pm ? "pm" : "am");
+}
+
+static void
+dialog_update_state (CcNaturalLightDialog *self)
+{
+ GtkWidget *widget;
+ gboolean automatic;
+ gboolean disabled_until_tomorrow = FALSE;
+ gboolean enabled;
+ gdouble value = 0.f;
+ g_autoptr(GDateTime) dt = g_date_time_new_now_local ();
+
+ /* only show the infobar if we are disabled */
+ if (self->proxy_color != NULL)
+ {
+ g_autoptr(GVariant) disabled = NULL;
+ disabled = g_dbus_proxy_get_cached_property (self->proxy_color,
+ "DisabledUntilTomorrow");
+ if (disabled != NULL)
+ disabled_until_tomorrow = g_variant_get_boolean (disabled);
+ }
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_disabled"));
+ gtk_widget_set_visible (widget, disabled_until_tomorrow);
+
+ /* make things insensitive if the switch is disabled */
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "switch_enable"));
+ enabled = gtk_switch_get_state (GTK_SWITCH (widget));
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "radio_automatic"));
+ gtk_widget_set_sensitive (widget, enabled);
+ automatic = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "radio_manual"));
+ gtk_widget_set_sensitive (widget, enabled);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_manual"));
+ gtk_widget_set_sensitive (widget, enabled && !automatic);
+
+ /* set from */
+ if (automatic && self->proxy_color != NULL)
+ {
+ g_autoptr(GVariant) sunset = NULL;
+ sunset = g_dbus_proxy_get_cached_property (self->proxy_color, "Sunset");
+ if (sunset != NULL)
+ {
+ value = g_variant_get_double (sunset);
+ }
+ else
+ {
+ value = 16.0f;
+ g_warning ("no sunset data, using %02.2f", value);
+ }
+ }
+ else
+ {
+ value = g_settings_get_double (self->settings_display, "natural-light-schedule-from");
+ value = fmod (value, 24.f);
+ }
+ dialog_adjustments_set_frac_hours (self, value,
+ "adjustment_from_hours",
+ "adjustment_from_minutes",
+ "stack_from");
+ cc_natural_light_widget_set_from (CC_NATURAL_LIGHT_WIDGET (self->natural_light_widget), value);
+
+ /* set to */
+ if (automatic && self->proxy_color != NULL)
+ {
+ g_autoptr(GVariant) sunset = NULL;
+ sunset = g_dbus_proxy_get_cached_property (self->proxy_color, "Sunrise");
+ if (sunset != NULL)
+ {
+ value = g_variant_get_double (sunset);
+ }
+ else
+ {
+ value = 8.0f;
+ g_warning ("no sunrise data, using %02.2f", value);
+ }
+ }
+ else
+ {
+ value = g_settings_get_double (self->settings_display, "natural-light-schedule-to");
+ value = fmod (value, 24.f);
+ }
+ dialog_adjustments_set_frac_hours (self, value,
+ "adjustment_to_hours",
+ "adjustment_to_minutes",
+ "stack_to");
+ cc_natural_light_widget_set_to (CC_NATURAL_LIGHT_WIDGET (self->natural_light_widget), value);
+
+ /* set new time */
+ cc_natural_light_widget_set_now (CC_NATURAL_LIGHT_WIDGET (self->natural_light_widget),
+ frac_day_from_dt (dt));
+}
+
+static gboolean
+dialog_tick_cb (gpointer user_data)
+{
+ CcNaturalLightDialog *self = (CcNaturalLightDialog *) user_data;
+ dialog_update_state (self);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+dialog_enabled_notify_cb (GtkSwitch *sw, GParamSpec *pspec, CcNaturalLightDialog *self)
+{
+ g_settings_set_boolean (self->settings_display, "natural-light-enabled",
+ gtk_switch_get_active (sw));
+ dialog_update_state (self);
+}
+
+static void
+dialog_mode_changed_cb (GtkToggleButton *togglebutton, CcNaturalLightDialog *self)
+{
+ GtkWidget *widget;
+ gboolean ret;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "radio_automatic"));
+ ret = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ g_settings_set_boolean (self->settings_display, "natural-light-schedule-automatic", ret);
+
+ dialog_update_state (self);
+}
+
+static void
+dialog_undisable_call_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ CcNaturalLightDialog *self = (CcNaturalLightDialog *) user_data;
+ g_autoptr(GVariant) val = NULL;
+ g_autoptr(GError) error = NULL;
+
+ val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+ res, &error);
+ if (val == NULL)
+ {
+ g_warning ("failed to undisable: %s", error->message);
+ return;
+ }
+ dialog_update_state (self);
+}
+
+static void
+dialog_undisable_clicked_cb (GtkButton *button, CcNaturalLightDialog *self)
+{
+ g_dbus_proxy_call (self->proxy_color_props,
+ "Set",
+ g_variant_new ("(ssv)",
+ "org.gnome.SettingsDaemon.Color",
+ "DisabledUntilTomorrow",
+ g_variant_new_boolean (FALSE)),
+ G_DBUS_CALL_FLAGS_NONE,
+ 5000,
+ self->cancellable,
+ dialog_undisable_call_cb,
+ self);
+}
+
+static gdouble
+dialog_adjustments_get_frac_hours (CcNaturalLightDialog *self,
+ const gchar *id_hours,
+ const gchar *id_mins,
+ const gchar *id_stack)
+{
+ GtkAdjustment *adj;
+ GtkWidget *widget;
+ gdouble value;
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, id_hours));
+ value = gtk_adjustment_get_value (adj);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, id_mins));
+ value += gtk_adjustment_get_value (adj) / 60.0f;
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, id_stack));
+ if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (widget)), "pm") == 0)
+ value += 12.f;
+ return value;
+}
+
+static void
+dialog_time_from_value_changed_cb (GtkAdjustment *adjustment, CcNaturalLightDialog *self)
+{
+ gdouble value;
+
+ if (self->ignore_value_changed)
+ return;
+
+ value = dialog_adjustments_get_frac_hours (self,
+ "adjustment_from_hours",
+ "adjustment_from_minutes",
+ "stack_from");
+ if (value >= 24.f)
+ value = fmod (value, 24);
+ g_debug ("new value = %.3f", value);
+ g_settings_set_double (self->settings_display, "natural-light-schedule-from", value);
+ dialog_update_state (self);
+}
+
+static void
+dialog_time_to_value_changed_cb (GtkAdjustment *adjustment, CcNaturalLightDialog *self)
+{
+ gdouble value;
+
+ if (self->ignore_value_changed)
+ return;
+
+ value = dialog_adjustments_get_frac_hours (self,
+ "adjustment_to_hours",
+ "adjustment_to_minutes",
+ "stack_to");
+ if (value >= 24.f)
+ value = fmod (value, 24);
+ g_debug ("new value = %.3f", value);
+ g_settings_set_double (self->settings_display, "natural-light-schedule-to", value);
+ dialog_update_state (self);
+}
+
+static void
+dialog_got_proxy_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ CcNaturalLightDialog *self = (CcNaturalLightDialog *) user_data;
+ g_autoptr(GError) error = NULL;
+ self->proxy_color = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (self->proxy_color == NULL)
+ {
+ g_warning ("failed to connect to g-s-d: %s", error->message);
+ return;
+ }
+ dialog_update_state (self);
+ self->timer_id = g_timeout_add_seconds (10, dialog_tick_cb, self);
+}
+
+static void
+dialog_got_proxy_props_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ CcNaturalLightDialog *self = (CcNaturalLightDialog *) user_data;
+ g_autoptr(GError) error = NULL;
+ self->proxy_color_props = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (self->proxy_color_props == NULL)
+ {
+ g_warning ("failed to connect to g-s-d: %s", error->message);
+ return;
+ }
+}
+
+static gboolean
+dialog_format_minutes_combobox (GtkSpinButton *spin, CcNaturalLightDialog *self)
+{
+ GtkAdjustment *adjustment;
+ g_autofree gchar *text = NULL;
+ adjustment = gtk_spin_button_get_adjustment (spin);
+ text = g_strdup_printf ("%02.0f", gtk_adjustment_get_value (adjustment));
+ gtk_entry_set_text (GTK_ENTRY (spin), text);
+ return TRUE;
+}
+
+static gboolean
+dialog_format_hours_combobox (GtkSpinButton *spin, CcNaturalLightDialog *self)
+{
+ GtkAdjustment *adjustment;
+ g_autofree gchar *text = NULL;
+ adjustment = gtk_spin_button_get_adjustment (spin);
+ if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_12H)
+ text = g_strdup_printf ("%.0f", gtk_adjustment_get_value (adjustment));
+ else
+ text = g_strdup_printf ("%02.0f", gtk_adjustment_get_value (adjustment));
+ gtk_entry_set_text (GTK_ENTRY (spin), text);
+ return TRUE;
+}
+
+static void
+dialog_update_adjustments (CcNaturalLightDialog *self)
+{
+ GtkAdjustment *adj;
+ GtkWidget *widget;
+
+ /* from */
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_from_hours"));
+ if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_24H)
+ {
+ gtk_adjustment_set_lower (adj, 0);
+ gtk_adjustment_set_upper (adj, 23);
+ }
+ else
+ {
+ if (gtk_adjustment_get_value (adj) > 12)
+ {
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "stack_from"));
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "pm");
+ }
+ gtk_adjustment_set_lower (adj, 1);
+ gtk_adjustment_set_upper (adj, 12);
+ }
+
+ /* to */
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_to_hours"));
+ if (self->clock_format == G_DESKTOP_CLOCK_FORMAT_24H)
+ {
+ gtk_adjustment_set_lower (adj, 0);
+ gtk_adjustment_set_upper (adj, 23);
+ }
+ else
+ {
+ if (gtk_adjustment_get_value (adj) > 12)
+ {
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "stack_to"));
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "pm");
+ }
+ gtk_adjustment_set_lower (adj, 1);
+ gtk_adjustment_set_upper (adj, 12);
+ }
+}
+
+static void
+dialog_clock_settings_changed_cb (GSettings *settings_display, gchar *key, CcNaturalLightDialog *self)
+{
+ GtkAdjustment *adj;
+ GtkWidget *widget;
+
+ self->clock_format = g_settings_get_enum (settings_display, CLOCK_FORMAT_KEY);
+
+ /* uncontionally widen this to avoid truncation */
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_from_hours"));
+ gtk_adjustment_set_lower (adj, 0);
+ gtk_adjustment_set_upper (adj, 23);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_to_hours"));
+ gtk_adjustment_set_lower (adj, 0);
+ gtk_adjustment_set_upper (adj, 23);
+
+ /* update spinbuttons */
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_from_hours"));
+ gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_to_hours"));
+ gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
+
+ /* update UI */
+ dialog_update_state (self);
+ dialog_update_adjustments (self);
+}
+
+static void
+dialog_am_pm_from_button_clicked_cb (GtkButton *button, CcNaturalLightDialog *self)
+{
+ gdouble value;
+ value = g_settings_get_double (self->settings_display, "natural-light-schedule-from");
+ if (value > 12.f)
+ value -= 12.f;
+ else
+ value += 12.f;
+ if (value >= 24.f)
+ value = fmod (value, 24);
+ g_settings_set_double (self->settings_display, "natural-light-schedule-from", value);
+ g_debug ("new value = %.3f", value);
+ dialog_update_state (self);
+}
+
+static void
+dialog_am_pm_to_button_clicked_cb (GtkButton *button, CcNaturalLightDialog *self)
+{
+ gdouble value;
+ value = g_settings_get_double (self->settings_display, "natural-light-schedule-to");
+ if (value > 12.f)
+ value -= 12.f;
+ else
+ value += 12.f;
+ if (value >= 24.f)
+ value = fmod (value, 24);
+ g_settings_set_double (self->settings_display, "natural-light-schedule-to", value);
+ g_debug ("new value = %.3f", value);
+ dialog_update_state (self);
+}
+
+static gboolean
+dialog_delete_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ CcNaturalLightDialog *self)
+{
+ gtk_widget_hide (widget);
+ return TRUE;
+}
+
+static void
+cc_natural_light_dialog_init (CcNaturalLightDialog *self)
+{
+ GdkScreen *screen;
+ GtkAdjustment *adj;
+ GtkBox *box;
+ GtkWidget *sw;
+ GtkWidget *widget;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GtkCssProvider) provider = NULL;
+
+ self->cancellable = g_cancellable_new ();
+ self->settings_display = g_settings_new (DISPLAY_SCHEMA);
+
+ self->builder = gtk_builder_new ();
+ gtk_builder_add_from_resource (self->builder,
+ "/org/gnome/control-center/display/display.ui",
+ &error);
+
+ if (error != NULL)
+ {
+ g_critical ("Could not load interface file: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* connect widgets */
+ sw = GTK_WIDGET (gtk_builder_get_object (self->builder, "switch_enable"));
+ gtk_switch_set_active (GTK_SWITCH (sw),
+ g_settings_get_boolean (self->settings_display, "natural-light-enabled"));
+ g_signal_connect (sw, "notify::active",
+ G_CALLBACK (dialog_enabled_notify_cb), self);
+ g_settings_bind_writable (self->settings_display, "natural-light-enabled",
+ sw, "sensitive",
+ FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "radio_automatic"));
+ g_signal_connect (widget, "toggled",
+ G_CALLBACK (dialog_mode_changed_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "radio_manual"));
+ g_signal_connect (widget, "toggled",
+ G_CALLBACK (dialog_mode_changed_cb), self);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_from_hours"));
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_time_from_value_changed_cb), self);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_from_minutes"));
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_time_from_value_changed_cb), self);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_to_hours"));
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_time_to_value_changed_cb), self);
+ adj = GTK_ADJUSTMENT (gtk_builder_get_object (self->builder, "adjustment_to_minutes"));
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_time_to_value_changed_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_undisable"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (dialog_undisable_clicked_cb), self);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_from_pm"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (dialog_am_pm_from_button_clicked_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_from_am"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (dialog_am_pm_from_button_clicked_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_to_pm"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (dialog_am_pm_to_button_clicked_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_to_am"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (dialog_am_pm_to_button_clicked_cb), self);
+
+ self->main_window = GTK_WIDGET (gtk_builder_get_object (self->builder, "window_natural_light"));
+ g_signal_connect (self->main_window, "delete-event",
+ G_CALLBACK (dialog_delete_event_cb), self);
+
+ /* use custom CSS */
+ provider = gtk_css_provider_new ();
+ if (!gtk_css_provider_load_from_data (provider,
+ ".padded-spinbutton {\n"
+ " font-size: 110%;\n"
+ " min-width: 50px;\n"
+ "}\n"
+ ".unpadded-button {\n"
+ " padding: 6px;\n"
+ "}\n",
+ -1,
+ &error))
+ {
+ g_error ("Failed to load CSS: %s", error->message);
+ }
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_from_hours"));
+ screen = gtk_widget_get_screen (widget);
+ gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_from_hours"));
+ g_signal_connect (widget, "output",
+ G_CALLBACK (dialog_format_hours_combobox), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_from_minutes"));
+ g_signal_connect (widget, "output",
+ G_CALLBACK (dialog_format_minutes_combobox), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_to_hours"));
+ g_signal_connect (widget, "output",
+ G_CALLBACK (dialog_format_hours_combobox), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "spinbutton_to_minutes"));
+ g_signal_connect (widget, "output",
+ G_CALLBACK (dialog_format_minutes_combobox), self);
+
+ /* add custom widget */
+ self->natural_light_widget = cc_natural_light_widget_new ();
+ gtk_widget_set_size_request (self->natural_light_widget, -1, 34);
+ box = GTK_BOX (gtk_builder_get_object (self->builder, "box_content"));
+ gtk_box_pack_start (box, self->natural_light_widget, FALSE, FALSE, 0);
+ gtk_widget_show (self->natural_light_widget);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.gnome.SettingsDaemon.Color",
+ "/org/gnome/SettingsDaemon/Color",
+ "org.gnome.SettingsDaemon.Color",
+ self->cancellable,
+ dialog_got_proxy_cb,
+ self);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.gnome.SettingsDaemon.Color",
+ "/org/gnome/SettingsDaemon/Color",
+ "org.freedesktop.DBus.Properties",
+ self->cancellable,
+ dialog_got_proxy_props_cb,
+ self);
+
+ /* clock settings_display */
+ self->settings_clock = g_settings_new (CLOCK_SCHEMA);
+ self->clock_format = g_settings_get_enum (self->settings_clock, CLOCK_FORMAT_KEY);
+ dialog_update_adjustments (self);
+ g_signal_connect (self->settings_clock, "changed::" CLOCK_FORMAT_KEY,
+ G_CALLBACK (dialog_clock_settings_changed_cb), self);
+
+ dialog_update_state (self);
+}
+
+CcNaturalLightDialog *
+cc_natural_light_dialog_new (void)
+{
+ return g_object_new (CC_TYPE_NATURAL_LIGHT_DIALOG, NULL);
+}
+
diff --git a/panels/display/cc-natural-light-dialog.h b/panels/display/cc-natural-light-dialog.h
new file mode 100644
index 0000000..8f5802a
--- /dev/null
+++ b/panels/display/cc-natural-light-dialog.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __CC_NATURAL_LIGHT_DIALOG_H__
+#define __CC_NATURAL_LIGHT_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_NATURAL_LIGHT_DIALOG (cc_natural_light_dialog_get_type ())
+G_DECLARE_FINAL_TYPE (CcNaturalLightDialog, cc_natural_light_dialog, CC, NATURAL_LIGHT_DIALOG, GObject)
+
+CcNaturalLightDialog *cc_natural_light_dialog_new (void);
+void cc_natural_light_dialog_present (CcNaturalLightDialog *self,
+ GtkWindow *parent);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/display/cc-natural-light-widget.c b/panels/display/cc-natural-light-widget.c
new file mode 100644
index 0000000..cb9be5a
--- /dev/null
+++ b/panels/display/cc-natural-light-widget.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <colord.h>
+
+#include "cc-natural-light-widget.h"
+#include "cc-display-resources.h"
+
+struct _CcNaturalLightWidget {
+ GtkDrawingArea parent;
+ gdouble to;
+ gdouble from;
+ gdouble now;
+ cairo_surface_t *surface_sunrise;
+ cairo_surface_t *surface_sunset;
+};
+
+G_DEFINE_TYPE (CcNaturalLightWidget, cc_natural_light_widget, GTK_TYPE_DRAWING_AREA);
+
+static gboolean cc_natural_light_widget_draw (GtkWidget *widget, cairo_t *cr);
+
+void
+cc_natural_light_widget_set_to (CcNaturalLightWidget *self, gdouble to)
+{
+ self->to = to;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+void
+cc_natural_light_widget_set_from (CcNaturalLightWidget *self, gdouble from)
+{
+ self->from = from;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+void
+cc_natural_light_widget_set_now (CcNaturalLightWidget *self, gdouble now)
+{
+ self->now = now;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+static void
+cc_natural_light_widget_finalize (GObject *object)
+{
+ CcNaturalLightWidget *self = CC_NATURAL_LIGHT_WIDGET (object);
+
+ g_clear_pointer (&self->surface_sunrise, (GDestroyNotify) cairo_surface_destroy);
+ g_clear_pointer (&self->surface_sunset, (GDestroyNotify) cairo_surface_destroy);
+
+ G_OBJECT_CLASS (cc_natural_light_widget_parent_class)->finalize (object);
+}
+
+static void
+cc_natural_light_widget_class_init (CcNaturalLightWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ object_class->finalize = cc_natural_light_widget_finalize;
+ widget_class->draw = cc_natural_light_widget_draw;
+}
+
+static cairo_status_t
+_png_read_func (void *closure, unsigned char *data, unsigned int length)
+{
+ GInputStream *stream = G_INPUT_STREAM (closure);
+ gssize read;
+ g_autoptr(GError) error = NULL;
+ read = g_input_stream_read (stream, data, (gsize) length, NULL, &error);
+ if (read < 0)
+ {
+ g_warning ("failed to read form stream: %s", error->message);
+ return CAIRO_STATUS_READ_ERROR;
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+read_surface_from_resource (const gchar *path)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GInputStream) stream = NULL;
+ stream = g_resource_open_stream (cc_display_get_resource (), path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+ if (stream == NULL)
+ {
+ g_error ("failed to load PNG data: %s", error->message);
+ return NULL;
+ }
+ return cairo_image_surface_create_from_png_stream (_png_read_func, stream);
+}
+
+static void
+cc_natural_light_widget_init (CcNaturalLightWidget *self)
+{
+ self->to = 8;
+ self->from = 16;
+ self->now = 11;
+ self->surface_sunrise = read_surface_from_resource ("/org/gnome/control-center/display/sunrise.png");
+ self->surface_sunset = read_surface_from_resource ("/org/gnome/control-center/display/sunset.png");
+}
+
+static gboolean
+is_frac_day_between (gdouble value, gdouble to, gdouble from)
+{
+ /* wraparound to the next day */
+ if (from < to)
+ from += 24;
+
+ /* wraparound to the previous day */
+ if (value < from && value < to)
+ value += 24;
+
+ /* test limits */
+ return value > to && value <= from;
+}
+
+
+static void
+rounded_rectangle (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ gdouble width,
+ gdouble height)
+{
+ gdouble degrees = G_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ x + width - radius,
+ y + radius,
+ radius,
+ -90 * degrees,
+ 0 * degrees);
+ cairo_arc (cr,
+ x + width - radius,
+ y + height - radius,
+ radius,
+ 0 * degrees,
+ 90 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + height - radius,
+ radius,
+ 90 * degrees,
+ 180 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + radius,
+ radius,
+ 180 * degrees,
+ 270 * degrees);
+ cairo_close_path (cr);
+}
+
+static gboolean
+cc_natural_light_widget_draw (GtkWidget *widget, cairo_t *cr)
+{
+ CdColorRGB color;
+ CdColorRGB color_temperature;
+ CdColorRGB color_unity;
+ GtkAllocation rect;
+ const guint arrow_sz = 5; /* px */
+ const guint icon_sz = 16; /* px */
+ guint line_x = 0; /* px */
+ const guint bar_voffset = arrow_sz + icon_sz;
+
+ CcNaturalLightWidget *self = (CcNaturalLightWidget*) widget;
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (CC_IS_NATURAL_LIGHT_WIDGET (self), FALSE);
+
+ cd_color_rgb_set (&color_temperature, 0.992, 0.796, 0.612);
+ cd_color_rgb_set (&color_unity, 0.773, 0.862, 0.953);
+
+ gtk_widget_get_allocation (widget, &rect);
+
+ /* clip to a rounded rectangle */
+ cairo_save (cr);
+ cairo_set_line_width (cr, 1);
+ rounded_rectangle (cr, 0, bar_voffset, 6,
+ rect.width, rect.height - bar_voffset - 1);
+ cairo_clip (cr);
+
+ /* draw each color line */
+ cairo_set_line_width (cr, 1);
+ gdouble subsect = 24.f / (gdouble) rect.width;
+ for (guint x = 0; x < rect.width; x += 1)
+ {
+ gdouble frac_hour = subsect * x;
+ if (frac_hour > self->now && line_x == 0)
+ {
+ cd_color_rgb_set (&color, 0.333, 0.333, 0.333);
+ line_x = x;
+ }
+ else if (is_frac_day_between (frac_hour, self->to - 1, self->to))
+ {
+ gdouble frac = 1.f - (self->to - frac_hour);
+ cd_color_rgb_interpolate (&color_temperature,
+ &color_unity,
+ frac,
+ &color);
+ }
+ else if (is_frac_day_between (frac_hour, self->from - 1, self->from))
+ {
+ gdouble frac = self->from - frac_hour;
+ cd_color_rgb_interpolate (&color_temperature,
+ &color_unity,
+ frac,
+ &color);
+ }
+ else if (is_frac_day_between (frac_hour, self->to, self->from))
+ {
+ cd_color_rgb_copy (&color_unity, &color);
+ }
+ else
+ {
+ cd_color_rgb_copy (&color_temperature, &color);
+ }
+ cairo_set_source_rgb (cr, color.R, color.G, color.B);
+ cairo_move_to (cr, x + 0.5, bar_voffset);
+ cairo_line_to (cr, x + 0.5, bar_voffset + rect.height);
+ cairo_stroke (cr);
+ }
+
+ /* apply border */
+ rounded_rectangle (cr, 0, bar_voffset, 6,
+ rect.width, rect.height - bar_voffset - 1);
+ cairo_set_source_rgb (cr, 0.65, 0.65, 0.65);
+ cairo_set_line_width (cr, 1);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ /* apply arrow */
+ cairo_move_to (cr, line_x - arrow_sz + 0.5, bar_voffset - arrow_sz);
+ cairo_line_to (cr, line_x + arrow_sz + 0.5, bar_voffset - arrow_sz);
+ cairo_line_to (cr, line_x + 0.5, arrow_sz + bar_voffset - arrow_sz);
+ cairo_close_path (cr);
+ cairo_set_source_rgb (cr, 0.333, 0.333, 0.333);
+ cairo_fill (cr);
+
+ /* draw icons */
+ if (self->to <= 0)
+ line_x = rect.width - icon_sz;
+ else
+ line_x = MIN (MAX ((self->to / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
+ cairo_set_source_surface (cr, self->surface_sunrise, line_x, 0);
+ cairo_paint (cr);
+ if (self->from <= 0)
+ line_x = rect.width - icon_sz;
+ else
+ line_x = MIN (MAX ((self->from / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
+ cairo_set_source_surface (cr, self->surface_sunset, line_x, 0);
+ cairo_paint (cr);
+
+ return FALSE;
+}
+
+GtkWidget *
+cc_natural_light_widget_new (void)
+{
+ return g_object_new (CC_TYPE_NATURAL_LIGHT_WIDGET, NULL);
+}
+
diff --git a/panels/display/cc-natural-light-widget.h b/panels/display/cc-natural-light-widget.h
new file mode 100644
index 0000000..022fe2f
--- /dev/null
+++ b/panels/display/cc-natural-light-widget.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __CC_NATURAL_LIGHT_WIDGET_H__
+#define __CC_NATURAL_LIGHT_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_NATURAL_LIGHT_WIDGET (cc_natural_light_widget_get_type ())
+G_DECLARE_FINAL_TYPE (CcNaturalLightWidget, cc_natural_light_widget, CC, NATURAL_LIGHT_WIDGET,
GtkDrawingArea)
+
+GtkWidget *cc_natural_light_widget_new (void);
+void cc_natural_light_widget_set_to (CcNaturalLightWidget *self,
+ gdouble to);
+void cc_natural_light_widget_set_from (CcNaturalLightWidget *self,
+ gdouble from);
+void cc_natural_light_widget_set_now (CcNaturalLightWidget *self,
+ gdouble now);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/display/display.gresource.xml b/panels/display/display.gresource.xml
new file mode 100644
index 0000000..342ed1e
--- /dev/null
+++ b/panels/display/display.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/control-center/display">
+ <file preprocess="xml-stripblanks">display.ui</file>
+ <file>sunrise.png</file>
+ <file>sunset.png</file>
+ </gresource>
+</gresources>
diff --git a/panels/display/display.ui b/panels/display/display.ui
new file mode 100644
index 0000000..ee4d6a7
--- /dev/null
+++ b/panels/display/display.ui
@@ -0,0 +1,549 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkSizeGroup">
+ <property name="mode">both</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_from_hours">
+ <property name="upper">23</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_from_minutes">
+ <property name="upper">59</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_to_hours">
+ <property name="upper">23</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_to_minutes">
+ <property name="upper">59</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkDialog" id="window_natural_light">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" comments="This is the redshift functionality where we supress
blue night when the sun has gone down">Natural Light Filter</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkInfoBar" id="infobar_disabled">
+ <property name="name">infobar_disabled</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">0</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button_undisable">
+ <property name="label" translatable="yes" comments="This cancels the redshift
inhibit.">Restart Filter</property>
+ <property name="name">button_undisable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="content_area">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="spacing">16</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_left">12</property>
+ <property name="hexpand">False</property>
+ <property name="label" translatable="yes" comments="Inhibit the redshift
functionality until the next day starts">Temporarily Disabled Until Tommorow</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">36</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">4</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Natural Light Filter</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="switch_enable">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">42</property>
+ <child>
+ <object class="GtkRadioButton" id="radio_automatic">
+ <property name="label" translatable="yes" comments="When the sun comes up in the
morning to the time the sun goes down in the evening">Sunset to sunrise</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">radio_manual</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="radio_manual">
+ <property name="label" translatable="yes" comments="This allows the user to schedule
when the redshift functionality is triggered">Manual schedule</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_manual">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">42</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">From</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSpinButton" id="spinbutton_from_hours">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">2</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">adjustment_from_hours</property>
+ <property name="numeric">True</property>
+ <property name="wrap">True</property>
+ <property name="value">4</property>
+ <style>
+ <class name="padded-spinbutton"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinbutton_from_minutes">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">2</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">adjustment_from_minutes</property>
+ <property name="numeric">True</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="padded-spinbutton"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack_from">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <child>
+ <object class="GtkButton" id="button_from_am">
+ <property name="label" translatable="yes" comments="This is the short
form for the time period in the morning">AM</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="unpadded-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="name">am</property>
+ <property name="title">page0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_from_pm">
+ <property name="label" translatable="yes" comments="This is the short
form for the time period in the afternoon">PM</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="unpadded-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="name">pm</property>
+ <property name="title">page1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="name">blank</property>
+ <property name="title">page0</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">To</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSpinButton" id="spinbutton_to_hours">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">2</property>
+ <property name="text" translatable="yes">4</property>
+ <property name="input_purpose">number</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">adjustment_to_hours</property>
+ <property name="numeric">True</property>
+ <property name="wrap">True</property>
+ <property name="value">4</property>
+ <style>
+ <class name="padded-spinbutton"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinbutton_to_minutes">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_width_chars">2</property>
+ <property name="text" translatable="yes">0</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">adjustment_to_minutes</property>
+ <property name="numeric">True</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="padded-spinbutton"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack_to">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <child>
+ <object class="GtkButton" id="button_to_am">
+ <property name="label" translatable="yes">AM</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="unpadded-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="name">am</property>
+ <property name="title">page0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_to_pm">
+ <property name="label" translatable="yes">PM</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="unpadded-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="name">pm</property>
+ <property name="title">page1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="name">blank</property>
+ <property name="title">page0</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/panels/display/icons/16x16/sunrise.png b/panels/display/icons/16x16/sunrise.png
new file mode 100644
index 0000000..a659b1d
Binary files /dev/null and b/panels/display/icons/16x16/sunrise.png differ
diff --git a/panels/display/icons/16x16/sunset.png b/panels/display/icons/16x16/sunset.png
new file mode 100644
index 0000000..bbdee49
Binary files /dev/null and b/panels/display/icons/16x16/sunset.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]