/* mate-rr-labeler.c - Utility to label monitors to identify them
 * while they are being configured.
 *
 * Copyright 2008, Novell, Inc.
 *
 * This file is part of the Mate Library.
 *
 * The Mate Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * The Mate Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with the Mate Library; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author: Federico Mena-Quintero <federico@novell.com>
 */

#define MATE_DESKTOP_USE_UNSTABLE_API

#include <config.h>
#include <glib/gi18n-lib.h>
#include "libmateui/mate-rr-labeler.h"
#include <gtk/gtk.h>

struct _MateRRLabeler {
	GObject parent;

	MateRRConfig *config;

	int num_outputs;

	GdkColor *palette;
	GtkWidget **windows;
};

struct _MateRRLabelerClass {
	GObjectClass parent_class;
};

G_DEFINE_TYPE (MateRRLabeler, mate_rr_labeler, G_TYPE_OBJECT);

static void mate_rr_labeler_finalize (GObject *object);

static void
mate_rr_labeler_init (MateRRLabeler *labeler)
{
	/* nothing */
}

static void
mate_rr_labeler_class_init (MateRRLabelerClass *class)
{
	GObjectClass *object_class;

	object_class = (GObjectClass *) class;

	object_class->finalize = mate_rr_labeler_finalize;
}

static void
mate_rr_labeler_finalize (GObject *object)
{
	MateRRLabeler *labeler;

	labeler = MATE_RR_LABELER (object);

	/* We don't destroy the labeler->config (a MateRRConfig*) here; let our
	 * caller do that instead.
	 */

	if (labeler->windows != NULL) {
		mate_rr_labeler_hide (labeler);
		g_free (labeler->windows);
		labeler->windows = NULL;
	}

	g_free (labeler->palette);
	labeler->palette = NULL;

	G_OBJECT_CLASS (mate_rr_labeler_parent_class)->finalize (object);
}

static int
count_outputs (MateRRConfig *config)
{
	int i;

	for (i = 0; config->outputs[i] != NULL; i++)
		;

	return i;
}

static void
make_palette (MateRRLabeler *labeler)
{
	/* The idea is that we go around an hue color wheel.  We want to start
	 * at red, go around to green/etc. and stop at blue --- because magenta
	 * is evil.  Eeeeek, no magenta, please!
	 *
	 * Purple would be nice, though.  Remember that we are watered down
	 * (i.e. low saturation), so that would be like Like berries with cream.
	 * Mmmmm, berries.
	 */
	double start_hue;
	double end_hue;
	int i;

	g_assert (labeler->num_outputs > 0);

	labeler->palette = g_new (GdkColor, labeler->num_outputs);

	start_hue = 0.0; /* red */
	end_hue   = 2.0/3; /* blue */

	for (i = 0; i < labeler->num_outputs; i++) {
		double h, s, v;
		double r, g, b;

		h = start_hue + (end_hue - start_hue) / labeler->num_outputs * i;
		s = 1.0 / 3;
		v = 1.0;

		gtk_hsv_to_rgb (h, s, v, &r, &g, &b);

		labeler->palette[i].red   = (int) (65535 * r + 0.5);
		labeler->palette[i].green = (int) (65535 * g + 0.5);
		labeler->palette[i].blue  = (int) (65535 * b + 0.5);
	}
}

#define LABEL_WINDOW_EDGE_THICKNESS 2
#define LABEL_WINDOW_PADDING 12

static gboolean
label_window_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
	cairo_t *cr;
	GdkColor *color;
	GtkAllocation allocation;

	color = g_object_get_data (G_OBJECT (widget), "color");
	gtk_widget_get_allocation (widget, &allocation);

	cr = gdk_cairo_create (gtk_widget_get_window (widget));

	/* edge outline */

	cairo_set_source_rgb (cr, 0, 0, 0);
	cairo_rectangle (cr,
			 LABEL_WINDOW_EDGE_THICKNESS / 2.0,
			 LABEL_WINDOW_EDGE_THICKNESS / 2.0,
			 allocation.width - LABEL_WINDOW_EDGE_THICKNESS,
			 allocation.height - LABEL_WINDOW_EDGE_THICKNESS);
	cairo_set_line_width (cr, LABEL_WINDOW_EDGE_THICKNESS);
	cairo_stroke (cr);

	/* fill */

	gdk_cairo_set_source_color (cr, color);
	cairo_rectangle (cr,
			 LABEL_WINDOW_EDGE_THICKNESS,
			 LABEL_WINDOW_EDGE_THICKNESS,
			 allocation.width - LABEL_WINDOW_EDGE_THICKNESS * 2,
			 allocation.height - LABEL_WINDOW_EDGE_THICKNESS * 2);
	cairo_fill (cr);

	cairo_destroy (cr);

	return FALSE;
}

static GtkWidget *
create_label_window (MateRRLabeler *labeler, MateOutputInfo *output, GdkColor *color)
{
	GtkWidget *window;
	GtkWidget *widget;
	char *str;
	const char *display_name;
	GdkColor black = { 0, 0, 0, 0 };

	window = gtk_window_new (GTK_WINDOW_POPUP);
	gtk_widget_set_app_paintable (window, TRUE);

	gtk_container_set_border_width (GTK_CONTAINER (window), LABEL_WINDOW_PADDING + LABEL_WINDOW_EDGE_THICKNESS);

	/* This is semi-dangerous.  The color is part of the labeler->palette
	 * array.  Note that in mate_rr_labeler_finalize(), we are careful to
	 * free the palette only after we free the windows.
	 */
	g_object_set_data (G_OBJECT (window), "color", color);

	g_signal_connect (window, "expose-event",
			  G_CALLBACK (label_window_expose_event_cb), labeler);

	if (labeler->config->clone) {
		/* Keep this string in sync with mate-control-center/capplets/display/xrandr-capplet.c:get_display_name() */

		/* Translators:  this is the feature where what you see on your laptop's
		 * screen is the same as your external monitor.  Here, "Mirror" is being
		 * used as an adjective, not as a verb.  For example, the Spanish
		 * translation could be "Pantallas en Espejo", *not* "Espejar Pantallas".
		 */
		display_name = _("Mirror Screens");
	} else
		display_name = output->display_name;

	str = g_strdup_printf ("<b>%s</b>", display_name);
	widget = gtk_label_new (NULL);
	gtk_label_set_markup (GTK_LABEL (widget), str);
	g_free (str);

	/* Make the label explicitly black.  We don't want it to follow the
	 * theme's colors, since the label is always shown against a light
	 * pastel background.  See bgo#556050
	 */
	gtk_widget_modify_fg (widget, gtk_widget_get_state (widget), &black);

	gtk_container_add (GTK_CONTAINER (window), widget);

	/* Should we center this at the top edge of the monitor, instead of using the upper-left corner? */
	gtk_window_move (GTK_WINDOW (window), output->x, output->y);

	gtk_widget_show_all (window);

	return window;
}

static void
create_label_windows (MateRRLabeler *labeler)
{
	int i;
	gboolean created_window_for_clone;

	labeler->windows = g_new (GtkWidget *, labeler->num_outputs);

	created_window_for_clone = FALSE;

	for (i = 0; i < labeler->num_outputs; i++) {
		if (!created_window_for_clone && labeler->config->outputs[i]->on) {
			labeler->windows[i] = create_label_window (labeler, labeler->config->outputs[i], labeler->palette + i);

			if (labeler->config->clone)
				created_window_for_clone = TRUE;
		} else
			labeler->windows[i] = NULL;
	}
}

static void
setup_from_config (MateRRLabeler *labeler)
{
	labeler->num_outputs = count_outputs (labeler->config);

	make_palette (labeler);

	create_label_windows (labeler);
}

MateRRLabeler *
mate_rr_labeler_new (MateRRConfig *config)
{
	MateRRLabeler *labeler;

	g_return_val_if_fail (config != NULL, NULL);

	labeler = g_object_new (MATE_TYPE_RR_LABELER, NULL);
	labeler->config = config;

	setup_from_config (labeler);

	return labeler;
}

void
mate_rr_labeler_hide (MateRRLabeler *labeler)
{
	int i;

	g_return_if_fail (MATE_IS_RR_LABELER (labeler));

	for (i = 0; i < labeler->num_outputs; i++)
		if (labeler->windows[i] != NULL) {
			gtk_widget_destroy (labeler->windows[i]);
			labeler->windows[i] = NULL;
		}
}

void
mate_rr_labeler_get_color_for_output (MateRRLabeler *labeler, MateOutputInfo *output, GdkColor *color_out)
{
	int i;

	g_return_if_fail (MATE_IS_RR_LABELER (labeler));
	g_return_if_fail (output != NULL);
	g_return_if_fail (color_out != NULL);

	for (i = 0; i < labeler->num_outputs; i++)
		if (labeler->config->outputs[i] == output) {
			*color_out = labeler->palette[i];
			return;
		}

	g_warning ("trying to get the color for unknown MateOutputInfo %p; returning magenta!", output);

	color_out->red   = 0xffff;
	color_out->green = 0;
	color_out->blue  = 0xffff;
}