2010-06-07 16:51:11 +01:00
|
|
|
|
/*
|
2011-08-25 18:32:15 +01:00
|
|
|
|
* Copyright (C) 2007, 2008 Red Hat, Inc.
|
2013-08-20 14:48:04 +01:00
|
|
|
|
* Copyright (C) 2013 Intel, Inc.
|
2010-06-07 16:51:11 +01:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2013-08-20 14:48:04 +01:00
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2010-06-07 16:51:11 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "cc-display-panel.h"
|
2013-01-04 14:52:41 +01:00
|
|
|
|
#include "cc-display-resources.h"
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
#include "scrollarea.h"
|
|
|
|
|
#define GNOME_DESKTOP_USE_UNSTABLE_API
|
|
|
|
|
#include <libgnome-desktop/gnome-rr.h>
|
|
|
|
|
#include <libgnome-desktop/gnome-rr-config.h>
|
2013-08-27 11:52:20 +01:00
|
|
|
|
#include <libgnome-desktop/gnome-bg.h>
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#include <glib/gi18n.h>
|
2013-08-20 14:48:04 +01:00
|
|
|
|
#include <stdlib.h>
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#include <gdesktop-enums.h>
|
2013-08-20 14:48:04 +01:00
|
|
|
|
#include <math.h>
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
2012-11-21 22:13:44 +01:00
|
|
|
|
#include "cc-rr-labeler.h"
|
2013-07-19 17:07:56 +01:00
|
|
|
|
#include <libupower-glib/upower.h>
|
2012-11-21 22:13:44 +01:00
|
|
|
|
|
2012-08-21 14:29:22 -04:00
|
|
|
|
CC_PANEL_REGISTER (CcDisplayPanel, cc_display_panel)
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
|
|
|
|
#define DISPLAY_PANEL_PRIVATE(o) \
|
|
|
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_DISPLAY_PANEL, CcDisplayPanelPrivate))
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#define WID(s) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, s))
|
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
#define TOP_BAR_HEIGHT 5
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
/* The minimum supported size for the panel, see:
|
|
|
|
|
* http://live.gnome.org/Design/SystemSettings */
|
|
|
|
|
#define MINIMUM_WIDTH 675
|
|
|
|
|
#define MINIMUM_HEIGHT 530
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
|
|
|
|
|
#define DISPLAY_PREVIEW_SETUP_HEIGHT 140
|
|
|
|
|
#define DISPLAY_PREVIEW_LIST_HEIGHT 55
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
DISPLAY_MODE_PRIMARY,
|
|
|
|
|
DISPLAY_MODE_SECONDARY,
|
|
|
|
|
/* DISPLAY_MODE_PRESENTATION, */
|
|
|
|
|
DISPLAY_MODE_MIRROR
|
2011-08-26 19:58:57 +01:00
|
|
|
|
};
|
|
|
|
|
|
2010-06-07 16:51:11 +01:00
|
|
|
|
struct _CcDisplayPanelPrivate
|
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRRScreen *screen;
|
|
|
|
|
GnomeRRConfig *current_configuration;
|
|
|
|
|
CcRRLabeler *labeler;
|
|
|
|
|
GnomeRROutputInfo *current_output;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
GnomeBG *background;
|
|
|
|
|
GnomeDesktopThumbnailFactory *thumbnail_factory;
|
|
|
|
|
|
2011-09-01 13:53:45 +01:00
|
|
|
|
guint focus_id;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
guint screen_changed_handler_id;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GtkWidget *displays_listbox;
|
|
|
|
|
GtkWidget *arrange_button;
|
|
|
|
|
GtkWidget *res_combo;
|
|
|
|
|
GtkWidget *rotate_left_button;
|
|
|
|
|
GtkWidget *rotate_right_button;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-07-19 17:07:56 +01:00
|
|
|
|
UpClient *up_client;
|
|
|
|
|
gboolean lid_is_closed;
|
2010-06-07 16:51:11 +01:00
|
|
|
|
};
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
int grab_x;
|
|
|
|
|
int grab_y;
|
|
|
|
|
int output_x;
|
|
|
|
|
int output_y;
|
|
|
|
|
} GrabInfo;
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static GHashTable *output_ids;
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gint
|
|
|
|
|
cc_display_panel_get_output_id (GnomeRROutputInfo *output)
|
2010-06-07 16:51:11 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (output_ids)
|
|
|
|
|
return GPOINTER_TO_INT (g_hash_table_lookup (output_ids, output));
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
2010-06-07 16:51:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
cc_display_panel_dispose (GObject *object)
|
|
|
|
|
{
|
2013-07-19 17:07:56 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = CC_DISPLAY_PANEL (object)->priv;
|
2011-09-01 13:53:45 +01:00
|
|
|
|
CcShell *shell;
|
|
|
|
|
GtkWidget *toplevel;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (output_ids)
|
|
|
|
|
{
|
|
|
|
|
g_hash_table_destroy (output_ids);
|
|
|
|
|
output_ids = NULL;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (priv->focus_id)
|
2012-08-23 14:22:19 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
shell = cc_panel_get_shell (CC_PANEL (object));
|
2012-08-23 14:22:19 +01:00
|
|
|
|
toplevel = cc_shell_get_toplevel (shell);
|
|
|
|
|
if (toplevel != NULL)
|
|
|
|
|
g_signal_handler_disconnect (G_OBJECT (toplevel),
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->focus_id);
|
|
|
|
|
cc_rr_labeler_hide (priv->labeler);
|
|
|
|
|
priv->focus_id = 0;
|
2012-08-23 14:22:19 +01:00
|
|
|
|
}
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_clear_object (&priv->labeler);
|
2011-09-01 13:53:45 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (priv->screen_changed_handler_id)
|
|
|
|
|
{
|
|
|
|
|
g_signal_handler_disconnect (priv->screen, priv->screen_changed_handler_id);
|
|
|
|
|
priv->screen_changed_handler_id = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_clear_object (&priv->screen);
|
|
|
|
|
g_clear_object (&priv->up_client);
|
2013-08-27 11:52:20 +01:00
|
|
|
|
g_clear_object (&priv->background);
|
|
|
|
|
g_clear_object (&priv->thumbnail_factory);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
G_OBJECT_CLASS (cc_display_panel_parent_class)->dispose (object);
|
2010-06-07 16:51:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-18 17:03:34 +02:00
|
|
|
|
static const char *
|
|
|
|
|
cc_display_panel_get_help_uri (CcPanel *panel)
|
|
|
|
|
{
|
|
|
|
|
return "help:gnome-help/prefs-display";
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-07 16:51:11 +01:00
|
|
|
|
static void
|
|
|
|
|
cc_display_panel_class_init (CcDisplayPanelClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
2012-05-18 17:03:34 +02:00
|
|
|
|
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
|
|
|
|
g_type_class_add_private (klass, sizeof (CcDisplayPanelPrivate));
|
|
|
|
|
|
2012-05-18 17:03:34 +02:00
|
|
|
|
panel_class->get_help_uri = cc_display_panel_get_help_uri;
|
|
|
|
|
|
2010-06-07 16:51:11 +01:00
|
|
|
|
object_class->dispose = cc_display_panel_dispose;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
should_show_resolution (gint output_width,
|
|
|
|
|
gint output_height,
|
|
|
|
|
gint width,
|
|
|
|
|
gint height)
|
|
|
|
|
{
|
|
|
|
|
if (width >= MIN (output_width, MINIMUM_WIDTH) &&
|
|
|
|
|
height >= MIN (output_height, MINIMUM_HEIGHT))
|
|
|
|
|
{
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
on_viewport_changed (FooScrollArea *scroll_area,
|
|
|
|
|
GdkRectangle *old_viewport,
|
|
|
|
|
GdkRectangle *new_viewport)
|
|
|
|
|
{
|
|
|
|
|
foo_scroll_area_set_size (scroll_area,
|
|
|
|
|
new_viewport->width,
|
|
|
|
|
new_viewport->height);
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_invalidate (scroll_area);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-27 11:52:20 +01:00
|
|
|
|
paint_output (CcDisplayPanel *panel,
|
|
|
|
|
cairo_t *cr,
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRRConfig *configuration,
|
|
|
|
|
GnomeRROutputInfo *output,
|
|
|
|
|
gint num,
|
|
|
|
|
gint allocated_width,
|
|
|
|
|
gint allocated_height)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRRRotation rotation;
|
2013-08-27 11:52:20 +01:00
|
|
|
|
GdkPixbuf *pixbuf;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gint x, y, width, height;
|
|
|
|
|
gboolean active;
|
|
|
|
|
|
|
|
|
|
active = gnome_rr_output_info_is_active (output);
|
|
|
|
|
if (active)
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, NULL, NULL, &width, &height);
|
|
|
|
|
else
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = gnome_rr_output_info_get_preferred_width (output);
|
|
|
|
|
height = gnome_rr_output_info_get_preferred_height (output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
x = y = 0;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if ((rotation & GNOME_RR_ROTATION_90) || (rotation & GNOME_RR_ROTATION_270))
|
|
|
|
|
{
|
|
|
|
|
gint tmp;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* swap width and height */
|
|
|
|
|
tmp = width;
|
|
|
|
|
width = height;
|
|
|
|
|
height = tmp;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* scale to fit allocation */
|
|
|
|
|
if (width / (double) height < allocated_width / (double) allocated_height)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = allocated_height * (width / (double) height);
|
|
|
|
|
height = allocated_height;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
height = allocated_width * (height / (double) width);
|
|
|
|
|
width = allocated_width;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
x = (allocated_width / 2.0) - (width / 2.0);
|
|
|
|
|
cairo_set_source_rgb (cr, 0, 0, 0);
|
|
|
|
|
cairo_rectangle (cr, x, y, width, height);
|
|
|
|
|
cairo_fill (cr);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
pixbuf = gnome_bg_create_thumbnail (panel->priv->background,
|
|
|
|
|
panel->priv->thumbnail_factory,
|
|
|
|
|
gdk_screen_get_default (), width, height);
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (gnome_rr_output_info_get_primary (output)
|
|
|
|
|
|| gnome_rr_config_get_clone (configuration))
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-27 11:52:20 +01:00
|
|
|
|
y += TOP_BAR_HEIGHT;
|
|
|
|
|
height -= TOP_BAR_HEIGHT;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
if (pixbuf)
|
|
|
|
|
gdk_cairo_set_source_pixbuf (cr, pixbuf, x + 1, y + 1);
|
|
|
|
|
else
|
|
|
|
|
cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cairo_rectangle (cr, x + 1, y + 1, width - 2, height - 2);
|
|
|
|
|
cairo_fill (cr);
|
2011-08-26 21:11:35 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
g_clear_object (&pixbuf);
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (num > 0)
|
2011-08-26 21:11:35 +01:00
|
|
|
|
{
|
2013-08-27 11:52:20 +01:00
|
|
|
|
PangoLayout *layout;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gchar *number_str;
|
2013-08-27 11:52:20 +01:00
|
|
|
|
gdouble r = 3, r2 = r / 2.0, x1, y1, x2, y2;
|
|
|
|
|
PangoRectangle extents;
|
|
|
|
|
gdouble max_extent;
|
|
|
|
|
|
|
|
|
|
number_str = g_strdup_printf ("<small>%d</small>", num);
|
|
|
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET (panel), "");
|
|
|
|
|
pango_layout_set_markup (layout, number_str, -1);
|
|
|
|
|
pango_layout_get_extents (layout, NULL, &extents);
|
|
|
|
|
g_free (number_str);
|
2011-08-26 21:11:35 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 0.75);
|
|
|
|
|
max_extent = MAX ((extents.width - extents.x)/ PANGO_SCALE,
|
|
|
|
|
(extents.height - extents.y) / PANGO_SCALE);
|
|
|
|
|
|
|
|
|
|
x += 5;
|
|
|
|
|
y += 5;
|
|
|
|
|
x1 = x;
|
|
|
|
|
x2 = x1 + max_extent + 1;
|
|
|
|
|
y1 = y;
|
|
|
|
|
y2 = y1 + max_extent + 1;
|
|
|
|
|
cairo_move_to (cr, x1 + r, y1);
|
|
|
|
|
cairo_line_to (cr, x2 - r, y1);
|
|
|
|
|
cairo_curve_to (cr, x2 - r2, y1, x2, y1 + r2, x2, y1 + r);
|
|
|
|
|
cairo_line_to (cr, x2, y2 - r);
|
|
|
|
|
cairo_curve_to (cr, x2, y2 - r2, x2 - r2, y2, x2 - r, y2);
|
|
|
|
|
cairo_line_to (cr, x1 + r, y2);
|
|
|
|
|
cairo_curve_to (cr, x1 + r2, y2, x1, y2 - r2, x1, y2 - r);
|
|
|
|
|
cairo_line_to (cr, x1, y1 + r);
|
|
|
|
|
cairo_curve_to (cr, x1, y1 + r2, x1 + r2, y1, x1 + r, y1);
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cairo_fill (cr);
|
2011-08-26 21:11:35 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cairo_set_source_rgb (cr, 1, 1, 1);
|
2013-08-27 11:52:20 +01:00
|
|
|
|
cairo_move_to (cr,
|
|
|
|
|
x + (max_extent / 2.0) - ((extents.width / PANGO_SCALE) / 2.0),
|
|
|
|
|
y + (max_extent / 2.0) - ((extents.height / PANGO_SCALE) / 2.0));
|
|
|
|
|
pango_cairo_show_layout (cr, layout);
|
|
|
|
|
cairo_fill (cr);
|
|
|
|
|
g_object_unref (layout);
|
2011-08-26 21:11:35 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
static gboolean
|
2013-08-20 14:48:04 +01:00
|
|
|
|
display_preview_draw (GtkWidget *widget,
|
|
|
|
|
cairo_t *cr,
|
2013-08-27 11:52:20 +01:00
|
|
|
|
CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRROutputInfo *output;
|
|
|
|
|
GnomeRRConfig *config;
|
|
|
|
|
gint num, width, height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output = g_object_get_data (G_OBJECT (widget), "output");
|
|
|
|
|
config = g_object_get_data (G_OBJECT (widget), "config");
|
|
|
|
|
num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "number"));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = gtk_widget_get_allocated_width (widget);
|
|
|
|
|
height = gtk_widget_get_allocated_height (widget);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
paint_output (panel, cr, config, output, num, width, height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static GtkWidget*
|
2013-08-27 11:52:20 +01:00
|
|
|
|
display_preview_new (CcDisplayPanel *panel,
|
|
|
|
|
GnomeRROutputInfo *output,
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRRConfig *config,
|
|
|
|
|
gint num,
|
|
|
|
|
gint base_height)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GtkWidget *area;
|
|
|
|
|
gint width, height, x, y;
|
|
|
|
|
gboolean active;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
active = gnome_rr_output_info_is_active (output);
|
|
|
|
|
if (active)
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &x, &y, &width, &height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = gnome_rr_output_info_get_preferred_width (output);
|
|
|
|
|
height = gnome_rr_output_info_get_preferred_height (output);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
area = gtk_drawing_area_new ();
|
2013-08-27 11:52:20 +01:00
|
|
|
|
g_signal_connect (area, "draw", G_CALLBACK (display_preview_draw), panel);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_widget_set_size_request (area, base_height * (width / (gdouble) height), base_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_widget_set_valign (area, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_halign (area, GTK_ALIGN_CENTER);
|
|
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (area), "output", output);
|
|
|
|
|
g_object_set_data (G_OBJECT (area), "config", config);
|
|
|
|
|
g_object_set_data (G_OBJECT (area), "number", GINT_TO_POINTER (num));
|
|
|
|
|
|
|
|
|
|
return area;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
on_screen_changed (CcDisplayPanel *panel)
|
|
|
|
|
{
|
|
|
|
|
GnomeRRConfig *current;
|
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
|
|
|
|
gint i, num_active_outputs = 0, num_connected_outputs = 0, number = 0;
|
|
|
|
|
gboolean clone, combined = FALSE;
|
|
|
|
|
GtkSizeGroup *sizegroup;
|
|
|
|
|
|
|
|
|
|
gnome_rr_screen_refresh (priv->screen, NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
current = gnome_rr_config_new_current (priv->screen, NULL);
|
|
|
|
|
gnome_rr_config_ensure_primary (current);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (priv->displays_listbox),
|
|
|
|
|
(GtkCallback) gtk_widget_destroy, NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (priv->current_configuration)
|
|
|
|
|
g_object_unref (priv->current_configuration);
|
|
|
|
|
|
|
|
|
|
priv->current_configuration = current;
|
|
|
|
|
|
|
|
|
|
clone = gnome_rr_config_get_clone (current);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
outputs = gnome_rr_config_get_outputs (current);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_hash_table_remove_all (output_ids);
|
|
|
|
|
|
|
|
|
|
/* count the number of active and connected outputs */
|
|
|
|
|
for (i = 0; outputs[i]; i++)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (gnome_rr_output_info_is_active (outputs[i]))
|
|
|
|
|
num_active_outputs++;
|
|
|
|
|
num_connected_outputs++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; outputs[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *row, *item, *preview, *label;
|
|
|
|
|
gboolean primary, active;
|
|
|
|
|
gint x, y, width, height;
|
|
|
|
|
const gchar *status;
|
|
|
|
|
gboolean display_closed = FALSE;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (priv->lid_is_closed)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRROutput *output;
|
|
|
|
|
|
|
|
|
|
output = gnome_rr_screen_get_output_by_name (priv->screen,
|
|
|
|
|
gnome_rr_output_info_get_name (outputs[i]));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
display_closed = gnome_rr_output_is_builtin_display (output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
row = gtk_list_box_row_new ();
|
|
|
|
|
item = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (item), 12);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
active = gnome_rr_output_info_is_active (outputs[i]);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (outputs[i], &x, &y, &width, &height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!active)
|
|
|
|
|
{
|
|
|
|
|
width = gnome_rr_output_info_get_preferred_width (outputs[i]);
|
|
|
|
|
height = gnome_rr_output_info_get_preferred_height (outputs[i]);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
preview = display_preview_new (panel, outputs[i], current, ++number,
|
2013-08-20 14:48:04 +01:00
|
|
|
|
DISPLAY_PREVIEW_LIST_HEIGHT);
|
|
|
|
|
gtk_size_group_add_widget (sizegroup, preview);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (display_closed)
|
|
|
|
|
gtk_widget_set_sensitive (row, FALSE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_hash_table_insert (output_ids, outputs[i], GINT_TO_POINTER (number));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), preview);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
label = gtk_label_new (gnome_rr_output_info_get_display_name (outputs[i]));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), label);
|
|
|
|
|
|
|
|
|
|
primary = gnome_rr_output_info_get_primary (outputs[i]);
|
|
|
|
|
active = gnome_rr_output_info_is_active (outputs[i]);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (num_connected_outputs > 1)
|
|
|
|
|
{
|
|
|
|
|
if (display_closed)
|
|
|
|
|
status = _("Lid Closed");
|
|
|
|
|
else if (clone)
|
|
|
|
|
/* translators: "Mirrored" describes when both displays show the same view */
|
|
|
|
|
status = _("Mirrored");
|
|
|
|
|
else if (primary)
|
|
|
|
|
status = _("Primary");
|
|
|
|
|
else if (!active)
|
|
|
|
|
status = _("Off");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
status = _("Secondary");
|
|
|
|
|
combined = TRUE;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
label = gtk_label_new (status);
|
|
|
|
|
gtk_widget_set_hexpand (label, TRUE);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_END);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), label);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_object_set_data (G_OBJECT (row), "gnome-rr-output", outputs[i]);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (row), item);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->displays_listbox), row);
|
|
|
|
|
gtk_widget_show_all (row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (combined)
|
|
|
|
|
gtk_widget_show (priv->arrange_button);
|
|
|
|
|
else
|
|
|
|
|
gtk_widget_hide (priv->arrange_button);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_clear_object (&priv->labeler);
|
|
|
|
|
priv->labeler = cc_rr_labeler_new (priv->current_configuration);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
realign_outputs_after_resolution_change (CcDisplayPanel *self, GnomeRROutputInfo *output_that_changed, int old_width, int old_height)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* We find the outputs that were below or to the right of the output that
|
|
|
|
|
* changed, and realign them; we also do that for outputs that shared the
|
|
|
|
|
* right/bottom edges with the output that changed. The outputs that are
|
|
|
|
|
* above or to the left of that output don't need to change.
|
|
|
|
|
*/
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int i;
|
|
|
|
|
int old_right_edge, old_bottom_edge;
|
|
|
|
|
int dx, dy;
|
|
|
|
|
int x, y, width, height;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_assert (self->priv->current_configuration != NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (output_that_changed, &x, &y, &width, &height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (width == old_width && height == old_height)
|
|
|
|
|
return;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
old_right_edge = x + old_width;
|
|
|
|
|
old_bottom_edge = y + old_height;
|
2012-09-03 14:07:29 -05:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
dx = width - old_width;
|
|
|
|
|
dy = height - old_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
outputs = gnome_rr_config_get_outputs (self->priv->current_configuration);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; outputs[i] != NULL; i++)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int output_x, output_y;
|
|
|
|
|
int output_width, output_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (outputs[i] == output_that_changed || !gnome_rr_output_info_is_connected (outputs[i]))
|
|
|
|
|
continue;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (outputs[i], &output_x, &output_y, &output_width, &output_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (output_x >= old_right_edge)
|
|
|
|
|
output_x += dx;
|
|
|
|
|
else if (output_x + output_width == old_right_edge)
|
|
|
|
|
output_x = x + width - output_width;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (output_y >= old_bottom_edge)
|
|
|
|
|
output_y += dy;
|
|
|
|
|
else if (output_y + output_height == old_bottom_edge)
|
|
|
|
|
output_y = y + height - output_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_set_geometry (outputs[i], output_x, output_y, output_width, output_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
lay_out_outputs_horizontally (CcDisplayPanel *self)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int i;
|
|
|
|
|
int x;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* Lay out all the monitors horizontally when "mirror screens" is turned
|
|
|
|
|
* off, to avoid having all of them overlapped initially. We put the
|
|
|
|
|
* outputs turned off on the right-hand side.
|
|
|
|
|
*/
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
x = 0;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* First pass, all "on" outputs */
|
|
|
|
|
outputs = gnome_rr_config_get_outputs (self->priv->current_configuration);
|
2013-07-19 17:07:56 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; outputs[i]; ++i)
|
|
|
|
|
{
|
|
|
|
|
int width, height;
|
|
|
|
|
if (gnome_rr_output_info_is_connected (outputs[i]) && gnome_rr_output_info_is_active (outputs[i]))
|
2013-07-19 17:07:56 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (outputs[i], NULL, NULL, &width, &height);
|
|
|
|
|
gnome_rr_output_info_set_geometry (outputs[i], x, 0, width, height);
|
|
|
|
|
x += width;
|
2013-07-19 17:07:56 +01:00
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* Second pass, all the black screens */
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
for (i = 0; outputs[i]; ++i)
|
|
|
|
|
{
|
|
|
|
|
int width, height;
|
|
|
|
|
if (!(gnome_rr_output_info_is_connected (outputs[i]) && gnome_rr_output_info_is_active (outputs[i])))
|
|
|
|
|
{
|
|
|
|
|
gnome_rr_output_info_get_geometry (outputs[i], NULL, NULL, &width, &height);
|
|
|
|
|
gnome_rr_output_info_set_geometry (outputs[i], x, 0, width, height);
|
|
|
|
|
x += width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
apply_rotation_to_geometry (GnomeRROutputInfo *output, int *w, int *h)
|
|
|
|
|
{
|
|
|
|
|
GnomeRRRotation rotation;
|
|
|
|
|
|
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (output);
|
|
|
|
|
if ((rotation & GNOME_RR_ROTATION_90) || (rotation & GNOME_RR_ROTATION_270))
|
|
|
|
|
{
|
|
|
|
|
int tmp;
|
|
|
|
|
tmp = *h;
|
|
|
|
|
*h = *w;
|
|
|
|
|
*w = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_geometry (GnomeRROutputInfo *output, int *w, int *h)
|
|
|
|
|
{
|
|
|
|
|
if (gnome_rr_output_info_is_active (output))
|
|
|
|
|
{
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, NULL, NULL, w, h);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*h = gnome_rr_output_info_get_preferred_height (output);
|
|
|
|
|
*w = gnome_rr_output_info_get_preferred_width (output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apply_rotation_to_geometry (output, w, h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SPACE 15
|
|
|
|
|
#define MARGIN 15
|
|
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
|
list_connected_outputs (CcDisplayPanel *self, int *total_w, int *total_h)
|
|
|
|
|
{
|
|
|
|
|
int i, dummy;
|
|
|
|
|
GList *result = NULL;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
|
|
|
|
|
|
|
|
|
if (!total_w)
|
|
|
|
|
total_w = &dummy;
|
|
|
|
|
if (!total_h)
|
|
|
|
|
total_h = &dummy;
|
|
|
|
|
|
|
|
|
|
*total_w = 0;
|
|
|
|
|
*total_h = 0;
|
|
|
|
|
|
|
|
|
|
outputs = gnome_rr_config_get_outputs (self->priv->current_configuration);
|
|
|
|
|
for (i = 0; outputs[i] != NULL; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (gnome_rr_output_info_is_connected (outputs[i]))
|
|
|
|
|
{
|
|
|
|
|
int w, h;
|
|
|
|
|
|
|
|
|
|
result = g_list_prepend (result, outputs[i]);
|
|
|
|
|
|
|
|
|
|
get_geometry (outputs[i], &w, &h);
|
|
|
|
|
|
|
|
|
|
*total_w += w;
|
|
|
|
|
*total_h += h;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_list_reverse (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_n_connected (CcDisplayPanel *self)
|
|
|
|
|
{
|
|
|
|
|
GList *connected_outputs = list_connected_outputs (self, NULL, NULL);
|
|
|
|
|
int n = g_list_length (connected_outputs);
|
|
|
|
|
|
|
|
|
|
g_list_free (connected_outputs);
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double
|
2013-08-20 14:48:04 +01:00
|
|
|
|
compute_scale (CcDisplayPanel *self, FooScrollArea *area)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
|
|
|
|
int available_w, available_h;
|
|
|
|
|
int total_w, total_h;
|
|
|
|
|
int n_monitors;
|
|
|
|
|
GdkRectangle viewport;
|
|
|
|
|
GList *connected_outputs;
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
foo_scroll_area_get_viewport (area, &viewport);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
connected_outputs = list_connected_outputs (self, &total_w, &total_h);
|
|
|
|
|
|
|
|
|
|
n_monitors = g_list_length (connected_outputs);
|
|
|
|
|
|
|
|
|
|
g_list_free (connected_outputs);
|
|
|
|
|
|
|
|
|
|
available_w = viewport.width - 2 * MARGIN - (n_monitors - 1) * SPACE;
|
|
|
|
|
available_h = viewport.height - 2 * MARGIN - (n_monitors - 1) * SPACE;
|
|
|
|
|
|
|
|
|
|
return MIN ((double)available_w / total_w, (double)available_h / total_h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct Edge
|
|
|
|
|
{
|
|
|
|
|
GnomeRROutputInfo *output;
|
|
|
|
|
int x1, y1;
|
|
|
|
|
int x2, y2;
|
|
|
|
|
} Edge;
|
|
|
|
|
|
|
|
|
|
typedef struct Snap
|
|
|
|
|
{
|
|
|
|
|
Edge *snapper; /* Edge that should be snapped */
|
|
|
|
|
Edge *snappee;
|
|
|
|
|
int dy, dx;
|
|
|
|
|
} Snap;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_edge (GnomeRROutputInfo *output, int x1, int y1, int x2, int y2, GArray *edges)
|
|
|
|
|
{
|
|
|
|
|
Edge e;
|
|
|
|
|
|
|
|
|
|
e.x1 = x1;
|
|
|
|
|
e.x2 = x2;
|
|
|
|
|
e.y1 = y1;
|
|
|
|
|
e.y2 = y2;
|
|
|
|
|
e.output = output;
|
|
|
|
|
|
|
|
|
|
g_array_append_val (edges, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
list_edges_for_output (GnomeRROutputInfo *output, GArray *edges)
|
|
|
|
|
{
|
|
|
|
|
int x, y, w, h;
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &x, &y, &w, &h);
|
|
|
|
|
|
2013-07-17 17:15:25 +01:00
|
|
|
|
if (!gnome_rr_output_info_is_active (output))
|
|
|
|
|
{
|
|
|
|
|
h = gnome_rr_output_info_get_preferred_height (output);
|
|
|
|
|
w = gnome_rr_output_info_get_preferred_width (output);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
apply_rotation_to_geometry (output, &w, &h);
|
|
|
|
|
|
|
|
|
|
/* Top, Bottom, Left, Right */
|
|
|
|
|
add_edge (output, x, y, x + w, y, edges);
|
|
|
|
|
add_edge (output, x, y + h, x + w, y + h, edges);
|
|
|
|
|
add_edge (output, x, y, x, y + h, edges);
|
|
|
|
|
add_edge (output, x + w, y, x + w, y + h, edges);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
list_edges (GnomeRRConfig *config, GArray *edges)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
GnomeRROutputInfo **outputs = gnome_rr_config_get_outputs (config);
|
|
|
|
|
|
|
|
|
|
for (i = 0; outputs[i]; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (gnome_rr_output_info_is_connected (outputs[i]))
|
|
|
|
|
list_edges_for_output (outputs[i], edges);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
overlap (int s1, int e1, int s2, int e2)
|
|
|
|
|
{
|
|
|
|
|
return (!(e1 < s2 || s1 >= e2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
horizontal_overlap (Edge *snapper, Edge *snappee)
|
|
|
|
|
{
|
|
|
|
|
if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
vertical_overlap (Edge *snapper, Edge *snappee)
|
|
|
|
|
{
|
|
|
|
|
if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_snap (GArray *snaps, Snap snap)
|
|
|
|
|
{
|
|
|
|
|
if (ABS (snap.dx) <= 200 || ABS (snap.dy) <= 200)
|
|
|
|
|
g_array_append_val (snaps, snap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps)
|
|
|
|
|
{
|
|
|
|
|
Snap snap;
|
|
|
|
|
|
|
|
|
|
snap.snapper = snapper;
|
|
|
|
|
snap.snappee = snappee;
|
|
|
|
|
|
|
|
|
|
if (horizontal_overlap (snapper, snappee))
|
|
|
|
|
{
|
|
|
|
|
snap.dx = 0;
|
|
|
|
|
snap.dy = snappee->y1 - snapper->y1;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
}
|
|
|
|
|
else if (vertical_overlap (snapper, snappee))
|
|
|
|
|
{
|
|
|
|
|
snap.dy = 0;
|
|
|
|
|
snap.dx = snappee->x1 - snapper->x1;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Corner snaps */
|
|
|
|
|
/* 1->1 */
|
|
|
|
|
snap.dx = snappee->x1 - snapper->x1;
|
|
|
|
|
snap.dy = snappee->y1 - snapper->y1;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
|
|
|
|
|
/* 1->2 */
|
|
|
|
|
snap.dx = snappee->x2 - snapper->x1;
|
|
|
|
|
snap.dy = snappee->y2 - snapper->y1;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
|
|
|
|
|
/* 2->2 */
|
|
|
|
|
snap.dx = snappee->x2 - snapper->x2;
|
|
|
|
|
snap.dy = snappee->y2 - snapper->y2;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
|
|
|
|
|
/* 2->1 */
|
|
|
|
|
snap.dx = snappee->x1 - snapper->x2;
|
|
|
|
|
snap.dy = snappee->y1 - snapper->y2;
|
|
|
|
|
|
|
|
|
|
add_snap (snaps, snap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
list_snaps (GnomeRROutputInfo *output, GArray *edges, GArray *snaps)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < edges->len; ++i)
|
|
|
|
|
{
|
|
|
|
|
Edge *output_edge = &(g_array_index (edges, Edge, i));
|
|
|
|
|
|
|
|
|
|
if (output_edge->output == output)
|
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < edges->len; ++j)
|
|
|
|
|
{
|
|
|
|
|
Edge *edge = &(g_array_index (edges, Edge, j));
|
|
|
|
|
|
|
|
|
|
if (edge->output != output)
|
|
|
|
|
add_edge_snaps (output_edge, edge, snaps);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static void
|
|
|
|
|
print_edge (Edge *edge)
|
|
|
|
|
{
|
|
|
|
|
g_debug ("(%d %d %d %d)", edge->x1, edge->y1, edge->x2, edge->y2);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
corner_on_edge (int x, int y, Edge *e)
|
|
|
|
|
{
|
|
|
|
|
if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
edges_align (Edge *e1, Edge *e2)
|
|
|
|
|
{
|
|
|
|
|
if (corner_on_edge (e1->x1, e1->y1, e2))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (corner_on_edge (e2->x1, e2->y1, e1))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
output_is_aligned (GnomeRROutputInfo *output, GArray *edges)
|
|
|
|
|
{
|
|
|
|
|
gboolean result = FALSE;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < edges->len; ++i)
|
|
|
|
|
{
|
|
|
|
|
Edge *output_edge = &(g_array_index (edges, Edge, i));
|
|
|
|
|
|
|
|
|
|
if (output_edge->output == output)
|
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < edges->len; ++j)
|
|
|
|
|
{
|
|
|
|
|
Edge *edge = &(g_array_index (edges, Edge, j));
|
|
|
|
|
|
|
|
|
|
/* We are aligned if an output edge matches
|
|
|
|
|
* an edge of another output
|
|
|
|
|
*/
|
|
|
|
|
if (edge->output != output_edge->output)
|
|
|
|
|
{
|
|
|
|
|
if (edges_align (output_edge, edge))
|
|
|
|
|
{
|
|
|
|
|
result = TRUE;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
done:
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_output_rect (GnomeRROutputInfo *output, GdkRectangle *rect)
|
|
|
|
|
{
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &rect->x, &rect->y, &rect->width, &rect->height);
|
|
|
|
|
|
2013-07-17 17:15:25 +01:00
|
|
|
|
if (!gnome_rr_output_info_is_active (output))
|
|
|
|
|
{
|
|
|
|
|
rect->width = gnome_rr_output_info_get_preferred_width (output);
|
|
|
|
|
rect->height = gnome_rr_output_info_get_preferred_height (output);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
apply_rotation_to_geometry (output, &rect->width, &rect->height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
output_overlaps (GnomeRROutputInfo *output, GnomeRRConfig *config)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
GdkRectangle output_rect;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
|
|
|
|
|
|
|
|
|
g_assert (output != NULL);
|
|
|
|
|
|
|
|
|
|
get_output_rect (output, &output_rect);
|
|
|
|
|
|
|
|
|
|
outputs = gnome_rr_config_get_outputs (config);
|
|
|
|
|
for (i = 0; outputs[i]; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (outputs[i] != output && gnome_rr_output_info_is_connected (outputs[i]))
|
|
|
|
|
{
|
|
|
|
|
GdkRectangle other_rect;
|
|
|
|
|
|
|
|
|
|
get_output_rect (outputs[i], &other_rect);
|
|
|
|
|
if (gdk_rectangle_intersect (&output_rect, &other_rect, NULL))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gnome_rr_config_is_aligned (GnomeRRConfig *config, GArray *edges)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
gboolean result = TRUE;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
|
|
|
|
|
|
|
|
|
outputs = gnome_rr_config_get_outputs (config);
|
|
|
|
|
for (i = 0; outputs[i]; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (gnome_rr_output_info_is_connected (outputs[i]))
|
|
|
|
|
{
|
|
|
|
|
if (!output_is_aligned (outputs[i], edges))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (output_overlaps (outputs[i], config))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
is_corner_snap (const Snap *s)
|
|
|
|
|
{
|
|
|
|
|
return s->dx != 0 && s->dy != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
compare_snaps (gconstpointer v1, gconstpointer v2)
|
|
|
|
|
{
|
|
|
|
|
const Snap *s1 = v1;
|
|
|
|
|
const Snap *s2 = v2;
|
|
|
|
|
int sv1 = MAX (ABS (s1->dx), ABS (s1->dy));
|
|
|
|
|
int sv2 = MAX (ABS (s2->dx), ABS (s2->dy));
|
|
|
|
|
int d;
|
|
|
|
|
|
|
|
|
|
d = sv1 - sv2;
|
|
|
|
|
|
|
|
|
|
/* This snapping algorithm is good enough for rock'n'roll, but
|
|
|
|
|
* this is probably a better:
|
|
|
|
|
*
|
|
|
|
|
* First do a horizontal/vertical snap, then
|
|
|
|
|
* with the new coordinates from that snap,
|
|
|
|
|
* do a corner snap.
|
|
|
|
|
*
|
|
|
|
|
* Right now, it's confusing that corner snapping
|
|
|
|
|
* depends on the distance in an axis that you can't actually see.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
if (d == 0)
|
|
|
|
|
{
|
|
|
|
|
if (is_corner_snap (s1) && !is_corner_snap (s2))
|
|
|
|
|
return -1;
|
|
|
|
|
else if (is_corner_snap (s2) && !is_corner_snap (s1))
|
|
|
|
|
return 1;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sets a mouse cursor for a widget's window. As a hack, you can pass
|
|
|
|
|
* GDK_BLANK_CURSOR to mean "set the cursor to NULL" (i.e. reset the widget's
|
|
|
|
|
* window's cursor to its default).
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
set_cursor (GtkWidget *widget, GdkCursorType type)
|
|
|
|
|
{
|
|
|
|
|
GdkCursor *cursor;
|
|
|
|
|
GdkWindow *window;
|
|
|
|
|
|
|
|
|
|
if (type == GDK_BLANK_CURSOR)
|
|
|
|
|
cursor = NULL;
|
|
|
|
|
else
|
|
|
|
|
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), type);
|
|
|
|
|
|
|
|
|
|
window = gtk_widget_get_window (widget);
|
|
|
|
|
|
|
|
|
|
if (window)
|
|
|
|
|
gdk_window_set_cursor (window, cursor);
|
|
|
|
|
|
|
|
|
|
if (cursor)
|
|
|
|
|
g_object_unref (cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-17 11:05:11 +01:00
|
|
|
|
static void
|
|
|
|
|
grab_weak_ref_notify (gpointer area,
|
|
|
|
|
GObject *object)
|
|
|
|
|
{
|
|
|
|
|
foo_scroll_area_end_grab (area, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
static void
|
|
|
|
|
on_output_event (FooScrollArea *area,
|
|
|
|
|
FooScrollAreaEvent *event,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GnomeRROutputInfo *output = data;
|
|
|
|
|
CcDisplayPanel *self = g_object_get_data (G_OBJECT (area), "panel");
|
|
|
|
|
|
|
|
|
|
if (event->type == FOO_DRAG_HOVER)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (event->type == FOO_DROP)
|
|
|
|
|
{
|
|
|
|
|
/* Activate new primary? */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the mouse is inside the outputs, set the cursor to "you can move me". See
|
|
|
|
|
* on_canvas_event() for where we reset the cursor to the default if it
|
|
|
|
|
* exits the outputs' area.
|
|
|
|
|
*/
|
|
|
|
|
if (!gnome_rr_config_get_clone (self->priv->current_configuration) && get_n_connected (self) > 1)
|
|
|
|
|
set_cursor (GTK_WIDGET (area), GDK_FLEUR);
|
|
|
|
|
|
|
|
|
|
if (event->type == FOO_BUTTON_PRESS)
|
|
|
|
|
{
|
|
|
|
|
GrabInfo *info;
|
|
|
|
|
|
|
|
|
|
self->priv->current_output = output;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!gnome_rr_config_get_clone (self->priv->current_configuration) && get_n_connected (self) > 1)
|
|
|
|
|
{
|
|
|
|
|
int output_x, output_y;
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &output_x, &output_y, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_begin_grab (area, on_output_event, data);
|
2013-07-17 11:05:11 +01:00
|
|
|
|
g_object_weak_ref (data, grab_weak_ref_notify, area);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
info = g_new0 (GrabInfo, 1);
|
|
|
|
|
info->grab_x = event->x;
|
|
|
|
|
info->grab_y = event->y;
|
|
|
|
|
info->output_x = output_x;
|
|
|
|
|
info->output_y = output_y;
|
|
|
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (output), "grab-info", info);
|
|
|
|
|
}
|
|
|
|
|
foo_scroll_area_invalidate (area);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (foo_scroll_area_is_grabbed (area))
|
|
|
|
|
{
|
|
|
|
|
GrabInfo *info = g_object_get_data (G_OBJECT (output), "grab-info");
|
2013-08-20 14:48:04 +01:00
|
|
|
|
double scale = compute_scale (self, area);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
int old_x, old_y;
|
|
|
|
|
int width, height;
|
|
|
|
|
int new_x, new_y;
|
|
|
|
|
int i;
|
|
|
|
|
GArray *edges, *snaps, *new_edges;
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &old_x, &old_y, &width, &height);
|
|
|
|
|
new_x = info->output_x + (event->x - info->grab_x) / scale;
|
|
|
|
|
new_y = info->output_y + (event->y - info->grab_y) / scale;
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_set_geometry (output, new_x, new_y, width, height);
|
|
|
|
|
|
|
|
|
|
edges = g_array_new (TRUE, TRUE, sizeof (Edge));
|
|
|
|
|
snaps = g_array_new (TRUE, TRUE, sizeof (Snap));
|
|
|
|
|
new_edges = g_array_new (TRUE, TRUE, sizeof (Edge));
|
|
|
|
|
|
|
|
|
|
list_edges (self->priv->current_configuration, edges);
|
|
|
|
|
list_snaps (output, edges, snaps);
|
|
|
|
|
|
|
|
|
|
g_array_sort (snaps, compare_snaps);
|
|
|
|
|
|
2013-07-17 11:38:13 +01:00
|
|
|
|
gnome_rr_output_info_set_geometry (output, old_x, old_y, width, height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < snaps->len; ++i)
|
|
|
|
|
{
|
|
|
|
|
Snap *snap = &(g_array_index (snaps, Snap, i));
|
|
|
|
|
GArray *new_edges = g_array_new (TRUE, TRUE, sizeof (Edge));
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_set_geometry (output, new_x + snap->dx, new_y + snap->dy, width, height);
|
|
|
|
|
|
|
|
|
|
g_array_set_size (new_edges, 0);
|
|
|
|
|
list_edges (self->priv->current_configuration, new_edges);
|
|
|
|
|
|
|
|
|
|
if (gnome_rr_config_is_aligned (self->priv->current_configuration, new_edges))
|
|
|
|
|
{
|
|
|
|
|
g_array_free (new_edges, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gnome_rr_output_info_set_geometry (output, info->output_x, info->output_y, width, height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_array_free (new_edges, TRUE);
|
|
|
|
|
g_array_free (snaps, TRUE);
|
|
|
|
|
g_array_free (edges, TRUE);
|
|
|
|
|
|
|
|
|
|
if (event->type == FOO_BUTTON_RELEASE)
|
|
|
|
|
{
|
|
|
|
|
foo_scroll_area_end_grab (area, event);
|
|
|
|
|
|
|
|
|
|
g_free (g_object_get_data (G_OBJECT (output), "grab-info"));
|
|
|
|
|
g_object_set_data (G_OBJECT (output), "grab-info", NULL);
|
2013-07-17 11:05:11 +01:00
|
|
|
|
g_object_weak_unref (data, grab_weak_ref_notify, area);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
g_debug ("new position: %d %d %d %d", output->x, output->y, output->width, output->height);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_invalidate (area);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
on_canvas_event (FooScrollArea *area,
|
|
|
|
|
FooScrollAreaEvent *event,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
/* If the mouse exits the outputs, reset the cursor to the default. See
|
|
|
|
|
* on_output_event() for where we set the cursor to the movement cursor if
|
|
|
|
|
* it is over one of the outputs.
|
|
|
|
|
*/
|
|
|
|
|
set_cursor (GTK_WIDGET (area), GDK_BLANK_CURSOR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
paint_background (FooScrollArea *area,
|
|
|
|
|
cairo_t *cr)
|
|
|
|
|
{
|
|
|
|
|
GdkRectangle viewport;
|
|
|
|
|
GtkWidget *widget;
|
|
|
|
|
GtkStyleContext *context;
|
|
|
|
|
GdkRGBA fg, bg;
|
|
|
|
|
|
|
|
|
|
widget = GTK_WIDGET (area);
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_get_viewport (area, &viewport);
|
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
|
|
|
gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fg);
|
|
|
|
|
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg);
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgba (cr,
|
|
|
|
|
(fg.red + bg.red) / 2,
|
|
|
|
|
(fg.green + bg.green) / 2,
|
|
|
|
|
(fg.blue + bg.blue) / 2,
|
|
|
|
|
(fg.alpha + bg.alpha) / 2);
|
|
|
|
|
|
|
|
|
|
cairo_rectangle (cr,
|
|
|
|
|
viewport.x, viewport.y,
|
|
|
|
|
viewport.width, viewport.height);
|
|
|
|
|
|
|
|
|
|
cairo_fill_preserve (cr);
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL);
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgba (cr,
|
|
|
|
|
0.7 * bg.red,
|
|
|
|
|
0.7 * bg.green,
|
|
|
|
|
0.7 * bg.blue,
|
|
|
|
|
0.7 * bg.alpha);
|
|
|
|
|
|
|
|
|
|
cairo_stroke (cr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
on_area_paint (FooScrollArea *area,
|
|
|
|
|
cairo_t *cr,
|
|
|
|
|
gpointer data)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanel *self = data;
|
|
|
|
|
GList *connected_outputs = NULL;
|
|
|
|
|
GList *list;
|
|
|
|
|
|
|
|
|
|
paint_background (area, cr);
|
|
|
|
|
|
|
|
|
|
if (!self->priv->current_configuration)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
connected_outputs = list_connected_outputs (self, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
for (list = connected_outputs; list != NULL; list = list->next)
|
|
|
|
|
{
|
|
|
|
|
int w, h;
|
|
|
|
|
double scale = compute_scale (self, area);
|
2013-08-27 11:52:20 +01:00
|
|
|
|
gint x, y;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int output_x, output_y;
|
|
|
|
|
int total_w, total_h;
|
|
|
|
|
GList *connected_outputs;
|
|
|
|
|
GnomeRROutputInfo *output = list->data;
|
|
|
|
|
GdkRectangle viewport;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cairo_save (cr);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
connected_outputs = list_connected_outputs (self, &total_w, &total_h);
|
|
|
|
|
g_list_free (connected_outputs);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
foo_scroll_area_get_viewport (area, &viewport);
|
|
|
|
|
get_geometry (output, &w, &h);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
viewport.height -= 2 * MARGIN;
|
|
|
|
|
viewport.width -= 2 * MARGIN;
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_get_geometry (output, &output_x, &output_y, NULL, NULL);
|
|
|
|
|
x = output_x * scale + MARGIN + (viewport.width - total_w * scale) / 2.0;
|
|
|
|
|
y = output_y * scale + MARGIN + (viewport.height - total_h * scale) / 2.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
|
|
|
|
cairo_rectangle (cr, x, y, w * scale + 0.5, h * scale + 0.5);
|
|
|
|
|
foo_scroll_area_add_input_from_fill (area, cr, on_output_event, output);
|
|
|
|
|
cairo_fill (cr);
|
|
|
|
|
|
|
|
|
|
cairo_translate (cr, x, y);
|
2013-08-27 11:52:20 +01:00
|
|
|
|
paint_output (self, cr, self->priv->current_configuration, list->data,
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cc_display_panel_get_output_id (list->data),
|
|
|
|
|
w * scale, h * scale);
|
|
|
|
|
|
|
|
|
|
cairo_restore (cr);
|
|
|
|
|
|
|
|
|
|
if (gnome_rr_config_get_clone (self->priv->current_configuration))
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
compute_virtual_size_for_configuration (GnomeRRConfig *config, int *ret_width, int *ret_height)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int i;
|
|
|
|
|
int width, height;
|
|
|
|
|
int output_x, output_y, output_width, output_height;
|
|
|
|
|
GnomeRROutputInfo **outputs;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = height = 0;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
outputs = gnome_rr_config_get_outputs (config);
|
|
|
|
|
for (i = 0; outputs[i] != NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
if (gnome_rr_output_info_is_active (outputs[i]))
|
|
|
|
|
{
|
|
|
|
|
gnome_rr_output_info_get_geometry (outputs[i], &output_x, &output_y, &output_width, &output_height);
|
|
|
|
|
width = MAX (width, output_x + output_width);
|
|
|
|
|
height = MAX (height, output_y + output_height);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
*ret_width = width;
|
|
|
|
|
*ret_height = height;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
check_required_virtual_size (CcDisplayPanel *self)
|
|
|
|
|
{
|
|
|
|
|
int req_width, req_height;
|
|
|
|
|
int min_width, max_width;
|
|
|
|
|
int min_height, max_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
compute_virtual_size_for_configuration (self->priv->current_configuration, &req_width, &req_height);
|
|
|
|
|
|
|
|
|
|
gnome_rr_screen_get_ranges (self->priv->screen, &min_width, &max_width, &min_height, &max_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
#if 0
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_debug ("X Server supports:");
|
|
|
|
|
g_debug ("min_width = %d, max_width = %d", min_width, max_width);
|
|
|
|
|
g_debug ("min_height = %d, max_height = %d", min_height, max_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_debug ("Requesting size of %dx%d", req_width, req_height);
|
|
|
|
|
#endif
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!(min_width <= req_width && req_width <= max_width
|
|
|
|
|
&& min_height <= req_height && req_height <= max_height))
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: present a useful dialog, maybe even before the user tries to Apply */
|
|
|
|
|
#if 0
|
|
|
|
|
g_debug ("Your X server needs a larger Virtual size!");
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#endif
|
2013-08-20 14:48:04 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
apply_current_configuration (CcDisplayPanel *self)
|
|
|
|
|
{
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
|
|
gnome_rr_config_sanitize (self->priv->current_configuration);
|
|
|
|
|
gnome_rr_config_ensure_primary (self->priv->current_configuration);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
check_required_virtual_size (self);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_config_apply_persistent (self->priv->current_configuration,
|
|
|
|
|
self->priv->screen, &error);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* re-read the configuration */
|
|
|
|
|
on_screen_changed (self);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
|
g_warning ("Error applying configuration: %s", error->message);
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
show_arrange_displays_dialog (GtkButton *button,
|
|
|
|
|
CcDisplayPanel *panel)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *dialog, *content_area, *area, *vbox, *label;
|
|
|
|
|
|
|
|
|
|
dialog = gtk_dialog_new_with_buttons (_("Arrange Combined Displays"),
|
|
|
|
|
GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel)))),
|
|
|
|
|
GTK_DIALOG_MODAL,
|
|
|
|
|
_("_Cancel"), GTK_RESPONSE_REJECT,
|
|
|
|
|
_("_Apply"), GTK_RESPONSE_ACCEPT,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
|
|
|
|
|
|
area = (GtkWidget *) foo_scroll_area_new ();
|
|
|
|
|
g_object_set_data (G_OBJECT (area), "panel", panel);
|
|
|
|
|
|
|
|
|
|
foo_scroll_area_set_min_size (FOO_SCROLL_AREA (area), 520, 290);
|
|
|
|
|
gtk_widget_set_margin_right (area, 12);
|
|
|
|
|
gtk_widget_set_margin_left (area, 12);
|
|
|
|
|
gtk_widget_set_size_request (area, 520, 290);
|
|
|
|
|
g_signal_connect (area, "paint",
|
|
|
|
|
G_CALLBACK (on_area_paint), panel);
|
|
|
|
|
g_signal_connect (area, "viewport_changed",
|
|
|
|
|
G_CALLBACK (on_viewport_changed), panel);
|
|
|
|
|
|
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (vbox), area);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("Drag displays to rearrange them"));
|
|
|
|
|
gtk_widget_set_margin_top (label, 12);
|
|
|
|
|
gtk_widget_set_margin_bottom (label, 12);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (vbox), label);
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
|
|
|
|
|
|
|
|
|
|
gtk_widget_show_all (vbox);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (content_area), vbox);
|
|
|
|
|
|
|
|
|
|
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
|
|
|
|
|
apply_current_configuration (panel);
|
|
|
|
|
else
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* re-read the previous configuration */
|
|
|
|
|
on_screen_changed (panel);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static const gchar *
|
|
|
|
|
make_aspect_string (gint width,
|
|
|
|
|
gint height)
|
|
|
|
|
{
|
|
|
|
|
int ratio;
|
|
|
|
|
const gchar *aspect = NULL;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* We use a number of Unicode characters below:
|
|
|
|
|
* ∶ is U+2236 RATIO
|
|
|
|
|
* is U+2009 THIN SPACE,
|
|
|
|
|
* × is U+00D7 MULTIPLICATION SIGN
|
|
|
|
|
*/
|
|
|
|
|
if (width && height) {
|
|
|
|
|
if (width > height)
|
|
|
|
|
ratio = width * 10 / height;
|
|
|
|
|
else
|
|
|
|
|
ratio = height * 10 / width;
|
|
|
|
|
|
|
|
|
|
switch (ratio) {
|
|
|
|
|
case 13:
|
|
|
|
|
aspect = "4∶3";
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
aspect = "16∶10";
|
|
|
|
|
break;
|
|
|
|
|
case 17:
|
|
|
|
|
aspect = "16∶9";
|
|
|
|
|
break;
|
|
|
|
|
case 23:
|
|
|
|
|
aspect = "21∶9";
|
|
|
|
|
break;
|
|
|
|
|
case 12:
|
|
|
|
|
aspect = "5∶4";
|
|
|
|
|
break;
|
|
|
|
|
/* This catches 1.5625 as well (1600x1024) when maybe it shouldn't. */
|
|
|
|
|
case 15:
|
|
|
|
|
aspect = "3∶2";
|
|
|
|
|
break;
|
|
|
|
|
case 18:
|
|
|
|
|
aspect = "9∶5";
|
|
|
|
|
break;
|
|
|
|
|
case 10:
|
|
|
|
|
aspect = "1∶1";
|
|
|
|
|
break;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
2013-08-20 14:48:04 +01:00
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
return aspect;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static char *
|
|
|
|
|
make_resolution_string (gint width,
|
|
|
|
|
gint height)
|
|
|
|
|
{
|
|
|
|
|
const char *aspect = make_aspect_string (width, height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (aspect != NULL)
|
|
|
|
|
return g_strdup_printf ("%d × %d (%s)", width, height, aspect);
|
|
|
|
|
else
|
|
|
|
|
return g_strdup_printf ("%d × %d", width, height);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static GtkWidget *
|
|
|
|
|
list_box_item (const gchar *title,
|
|
|
|
|
const gchar *subtitle)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *item, *label, *row;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
row = gtk_list_box_row_new ();
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
item = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (item), 12);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
label = gtk_label_new (title);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), label);
|
|
|
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
|
|
|
|
gtk_widget_set_size_request (label, 230, -1);
|
|
|
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
label = gtk_label_new (subtitle);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (item), label);
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
|
|
|
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
|
|
|
|
gtk_widget_set_size_request (label, 230, -1);
|
|
|
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_container_add (GTK_CONTAINER (row), item);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
return row;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
cc_display_panel_list_box_update_header (GtkListBoxRow *row,
|
|
|
|
|
GtkListBoxRow *before,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *current;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (before == NULL)
|
|
|
|
|
return;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
current = gtk_list_box_row_get_header (row);
|
|
|
|
|
if (current == NULL)
|
|
|
|
|
{
|
|
|
|
|
current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
|
gtk_widget_show (current);
|
|
|
|
|
gtk_list_box_row_set_header (row, current);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
setup_resolution_combo_box (CcDisplayPanel *panel,
|
|
|
|
|
GnomeRRMode **modes,
|
|
|
|
|
GnomeRRMode *current_mode)
|
|
|
|
|
{
|
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GtkTreeModel *res_model;
|
|
|
|
|
GHashTable *resolutions;
|
|
|
|
|
gint i;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
res_model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->res_combo));
|
|
|
|
|
gtk_list_store_clear (GTK_LIST_STORE (res_model));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
resolutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; modes[i] != NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
gchar *res;
|
|
|
|
|
gboolean present;
|
|
|
|
|
gint output_width, output_height, mode_width, mode_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!current_mode)
|
|
|
|
|
current_mode = modes[i];
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
mode_width = gnome_rr_mode_get_width (modes[i]);
|
|
|
|
|
mode_height = gnome_rr_mode_get_height (modes[i]);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output_width = gnome_rr_output_info_get_preferred_width (priv->current_output);
|
|
|
|
|
output_height = gnome_rr_output_info_get_preferred_height (priv->current_output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!should_show_resolution (output_width, output_height, mode_width,
|
|
|
|
|
mode_height))
|
|
|
|
|
continue;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
res = make_resolution_string (gnome_rr_mode_get_width (modes[i]),
|
|
|
|
|
gnome_rr_mode_get_height (modes[i]));
|
|
|
|
|
present = GPOINTER_TO_INT (g_hash_table_lookup (resolutions, res));
|
|
|
|
|
if (!present)
|
|
|
|
|
{
|
|
|
|
|
GtkTreeIter iter;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_hash_table_insert (resolutions, g_strdup (res),
|
|
|
|
|
GINT_TO_POINTER (TRUE));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_list_store_insert_with_values (GTK_LIST_STORE (res_model), &iter,
|
|
|
|
|
-1, 0, res, 1, modes[i], -1);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* select the current mode in the combo box */
|
|
|
|
|
if (gnome_rr_mode_get_width (modes[i]) == gnome_rr_mode_get_width (current_mode)
|
|
|
|
|
&& gnome_rr_mode_get_height (modes[i]) == gnome_rr_mode_get_height (current_mode))
|
|
|
|
|
{
|
|
|
|
|
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->res_combo),
|
|
|
|
|
&iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_free (res);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* ensure a resolution is selected by default */
|
|
|
|
|
if (gtk_combo_box_get_active (GTK_COMBO_BOX (priv->res_combo)) == -1)
|
|
|
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX (priv->res_combo), 0);
|
|
|
|
|
|
|
|
|
|
g_hash_table_destroy (resolutions);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
|
2011-08-25 18:32:15 +01:00
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
setup_listbox_row_activated (GtkListBox *list_box,
|
|
|
|
|
GtkListBoxRow *row,
|
|
|
|
|
CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GnomeRRMode **modes;
|
|
|
|
|
gint index;
|
|
|
|
|
GnomeRROutput *output;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!row)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
return;
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
index = gtk_list_box_row_get_index (row);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output = gnome_rr_screen_get_output_by_name (priv->screen,
|
|
|
|
|
gnome_rr_output_info_get_name (priv->current_output));
|
|
|
|
|
|
|
|
|
|
if (index == DISPLAY_MODE_MIRROR)
|
|
|
|
|
{
|
|
|
|
|
modes = gnome_rr_screen_list_clone_modes (priv->screen);
|
|
|
|
|
gnome_rr_config_set_clone (priv->current_configuration, TRUE);
|
|
|
|
|
}
|
|
|
|
|
else
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_set_primary (priv->current_output,
|
|
|
|
|
(index == DISPLAY_MODE_PRIMARY));
|
|
|
|
|
gnome_rr_config_set_clone (priv->current_configuration, FALSE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
modes = gnome_rr_output_list_modes (output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
2013-08-20 14:48:04 +01:00
|
|
|
|
|
|
|
|
|
setup_resolution_combo_box (panel, modes,
|
|
|
|
|
gnome_rr_output_get_current_mode (output));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
dialog_toplevel_focus_changed (GtkWindow *window,
|
|
|
|
|
GParamSpec *pspec,
|
|
|
|
|
CcDisplayPanel *self)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (self->priv->labeler == NULL)
|
|
|
|
|
return;
|
|
|
|
|
if (gtk_window_has_toplevel_focus (window))
|
|
|
|
|
cc_rr_labeler_show (self->priv->labeler);
|
|
|
|
|
else
|
|
|
|
|
cc_rr_labeler_hide (self->priv->labeler);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
rotate_left_clicked (GtkButton *button,
|
|
|
|
|
CcDisplayPanel *panel)
|
|
|
|
|
{
|
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GnomeRRRotation rotation;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (priv->current_output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (rotation & GNOME_RR_ROTATION_0)
|
|
|
|
|
{
|
|
|
|
|
rotation = GNOME_RR_ROTATION_90;
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_left_button, FALSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotation = GNOME_RR_ROTATION_0;
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_right_button, TRUE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
2013-08-20 14:48:04 +01:00
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_set_rotation (priv->current_output, rotation);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotate_right_clicked (GtkButton *button,
|
|
|
|
|
CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GnomeRRRotation rotation;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (priv->current_output);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (rotation & GNOME_RR_ROTATION_0)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
rotation = GNOME_RR_ROTATION_270;
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_right_button, FALSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rotation = GNOME_RR_ROTATION_0;
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_left_button, TRUE);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_set_rotation (priv->current_output, rotation);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static const double known_diagonals[] = {
|
|
|
|
|
12.1,
|
|
|
|
|
13.3,
|
|
|
|
|
15.6
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
diagonal_to_str (double d)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
int i;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (known_diagonals); i++)
|
|
|
|
|
{
|
|
|
|
|
double delta;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
delta = fabs(known_diagonals[i] - d);
|
|
|
|
|
if (delta < 0.1)
|
|
|
|
|
return g_strdup_printf ("%0.1lf\"", known_diagonals[i]);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
return g_strdup_printf ("%d\"", (int) (d + 0.5));
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static char *
|
|
|
|
|
make_display_size_string (int width_mm,
|
|
|
|
|
int height_mm)
|
|
|
|
|
{
|
|
|
|
|
char *inches = NULL;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (width_mm > 0 && height_mm > 0)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
double d = sqrt (width_mm * width_mm + height_mm * height_mm);
|
|
|
|
|
|
|
|
|
|
inches = diagonal_to_str (d / 25.4);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
2013-08-20 14:48:04 +01:00
|
|
|
|
|
|
|
|
|
return inches;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
res_combo_changed (GtkComboBox *combo,
|
|
|
|
|
CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GtkTreeModel *res_model;
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
GnomeRRMode *mode;
|
|
|
|
|
gint x, y, width, height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
res_model = gtk_combo_box_get_model (combo);
|
|
|
|
|
|
|
|
|
|
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
|
|
|
|
|
{
|
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (res_model), &iter, 1, &mode, -1);
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_get_geometry (priv->current_output, &x, &y, NULL, NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
width = gnome_rr_mode_get_width (mode);
|
|
|
|
|
height = gnome_rr_mode_get_height (mode);
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_info_set_geometry (priv->current_output, x, y, width, height);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
show_setup_dialog (CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
2013-09-09 16:13:44 +02:00
|
|
|
|
GtkWidget *dialog, *listbox = NULL, *content_area, *item, *box, *frame, *preview;
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GtkWidget *label, *rotate_box, *grid;
|
|
|
|
|
gint i, width_mm, height_mm, old_width, old_height;
|
|
|
|
|
GnomeRROutput *output;
|
|
|
|
|
gchar *str;
|
|
|
|
|
gboolean clone, was_clone, primary, was_primary, active;
|
|
|
|
|
GtkListStore *res_model;
|
|
|
|
|
GtkCellRenderer *renderer;
|
|
|
|
|
GnomeRRRotation rotation;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output = gnome_rr_screen_get_output_by_name (priv->screen,
|
|
|
|
|
gnome_rr_output_info_get_name (priv->current_output));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dialog = gtk_dialog_new_with_buttons (gnome_rr_output_info_get_display_name (priv->current_output),
|
|
|
|
|
GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel)))),
|
|
|
|
|
GTK_DIALOG_MODAL,
|
|
|
|
|
_("_Cancel"), GTK_RESPONSE_REJECT,
|
|
|
|
|
_("_Apply"), GTK_RESPONSE_ACCEPT,
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
|
|
|
|
|
|
|
|
|
|
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
|
|
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
|
|
|
gtk_widget_set_margin_left (box, 12);
|
|
|
|
|
gtk_widget_set_margin_right (box, 12);
|
|
|
|
|
gtk_widget_set_margin_top (box, 6);
|
|
|
|
|
gtk_widget_set_margin_bottom (box, 12);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (content_area), box);
|
|
|
|
|
|
|
|
|
|
/* configuration grid */
|
|
|
|
|
grid = gtk_grid_new ();
|
|
|
|
|
gtk_widget_set_margin_left (grid, 36);
|
|
|
|
|
gtk_widget_set_margin_right (grid, 36);
|
|
|
|
|
gtk_widget_set_margin_bottom (grid, 6);
|
|
|
|
|
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
|
|
|
|
|
gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
|
|
|
|
|
|
|
|
|
|
/* preview */
|
2013-08-27 11:52:20 +01:00
|
|
|
|
preview = display_preview_new (panel, priv->current_output,
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->current_configuration,
|
|
|
|
|
cc_display_panel_get_output_id (priv->current_output),
|
|
|
|
|
DISPLAY_PREVIEW_SETUP_HEIGHT);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), preview, 0, 0, 2, 1);
|
|
|
|
|
|
|
|
|
|
/* rotation */
|
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (priv->current_output);
|
|
|
|
|
rotate_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
|
|
|
gtk_widget_set_margin_bottom (rotate_box, 12);
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (rotate_box),
|
|
|
|
|
GTK_STYLE_CLASS_LINKED);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), rotate_box, 0, 1, 2, 1);
|
|
|
|
|
gtk_widget_set_halign (rotate_box, GTK_ALIGN_CENTER);
|
|
|
|
|
|
|
|
|
|
priv->rotate_left_button = gtk_button_new ();
|
|
|
|
|
if (rotation == GNOME_RR_ROTATION_90)
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_left_button, FALSE);
|
|
|
|
|
g_signal_connect (priv->rotate_left_button, "clicked",
|
|
|
|
|
G_CALLBACK (rotate_left_clicked), panel);
|
|
|
|
|
g_signal_connect_swapped (priv->rotate_left_button, "clicked",
|
|
|
|
|
G_CALLBACK (gtk_widget_queue_draw), preview);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->rotate_left_button),
|
|
|
|
|
gtk_image_new_from_icon_name ("object-rotate-left-symbolic",
|
|
|
|
|
GTK_ICON_SIZE_BUTTON));
|
|
|
|
|
gtk_widget_set_halign (priv->rotate_left_button, GTK_ALIGN_END);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (rotate_box), priv->rotate_left_button);
|
|
|
|
|
|
|
|
|
|
priv->rotate_right_button = gtk_button_new ();
|
|
|
|
|
if (rotation == GNOME_RR_ROTATION_270)
|
|
|
|
|
gtk_widget_set_sensitive (priv->rotate_right_button, FALSE);
|
|
|
|
|
g_signal_connect (priv->rotate_right_button, "clicked",
|
|
|
|
|
G_CALLBACK (rotate_right_clicked), panel);
|
|
|
|
|
g_signal_connect_swapped (priv->rotate_right_button, "clicked",
|
|
|
|
|
G_CALLBACK (gtk_widget_queue_draw), preview);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->rotate_right_button),
|
|
|
|
|
gtk_image_new_from_icon_name ("object-rotate-right-symbolic",
|
|
|
|
|
GTK_ICON_SIZE_BUTTON));
|
|
|
|
|
gtk_widget_set_halign (priv->rotate_right_button, GTK_ALIGN_START);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (rotate_box), priv->rotate_right_button);
|
|
|
|
|
|
|
|
|
|
/* size */
|
|
|
|
|
label = gtk_label_new (_("Size"));
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label),
|
|
|
|
|
GTK_STYLE_CLASS_DIM_LABEL);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_END);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gnome_rr_output_get_physical_size (output, &width_mm, &height_mm);
|
|
|
|
|
str = make_display_size_string (width_mm, height_mm);
|
|
|
|
|
label = gtk_label_new (str);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 1, 2, 1, 1);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
g_free (str);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* aspect ratio */
|
|
|
|
|
label = gtk_label_new (_("Aspect Ratio"));
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label),
|
|
|
|
|
GTK_STYLE_CLASS_DIM_LABEL);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_END);
|
|
|
|
|
label = gtk_label_new (make_aspect_string (gnome_rr_output_info_get_preferred_width (priv->current_output),
|
|
|
|
|
gnome_rr_output_info_get_preferred_height (priv->current_output)));
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 1, 3, 1, 1);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
|
|
|
|
|
/* resolution combo box */
|
|
|
|
|
res_model = gtk_list_store_new (2, G_TYPE_STRING, GNOME_TYPE_RR_MODE);
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
priv->res_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (res_model));
|
|
|
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->res_combo), renderer, TRUE);
|
|
|
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->res_combo), renderer, "text", 0);
|
|
|
|
|
g_signal_connect (priv->res_combo, "changed", G_CALLBACK (res_combo_changed),
|
|
|
|
|
panel);
|
|
|
|
|
g_signal_connect_swapped (priv->res_combo, "changed",
|
|
|
|
|
G_CALLBACK (gtk_widget_queue_draw), preview);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("Resolution"));
|
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label),
|
|
|
|
|
GTK_STYLE_CLASS_DIM_LABEL);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), label, 0, 4, 1, 1);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), priv->res_combo, 1, 4, 1, 1);
|
|
|
|
|
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_END);
|
|
|
|
|
gtk_widget_set_halign (priv->res_combo, GTK_ALIGN_START);
|
|
|
|
|
|
|
|
|
|
was_clone = clone = gnome_rr_config_get_clone (priv->current_configuration);
|
|
|
|
|
was_primary = primary = gnome_rr_output_info_get_primary (priv->current_output);
|
|
|
|
|
active = gnome_rr_output_info_is_active (priv->current_output);
|
|
|
|
|
|
|
|
|
|
if (g_hash_table_size (output_ids) > 1)
|
|
|
|
|
{
|
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), frame);
|
|
|
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
|
|
|
|
|
|
|
|
listbox = gtk_list_box_new ();
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), listbox);
|
|
|
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (listbox),
|
|
|
|
|
cc_display_panel_list_box_update_header,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
g_signal_connect (listbox, "row-selected",
|
|
|
|
|
G_CALLBACK (setup_listbox_row_activated), panel);
|
|
|
|
|
g_signal_connect_swapped (listbox, "row-selected",
|
|
|
|
|
G_CALLBACK (gtk_widget_queue_draw), preview);
|
|
|
|
|
gtk_widget_show (listbox);
|
|
|
|
|
|
|
|
|
|
item = list_box_item (_("Primary"),
|
|
|
|
|
_("Show the top bar and Activities Overview on this display"));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (listbox), item);
|
|
|
|
|
if (primary)
|
|
|
|
|
gtk_list_box_select_row (GTK_LIST_BOX (listbox),
|
|
|
|
|
GTK_LIST_BOX_ROW (item));
|
|
|
|
|
|
|
|
|
|
item = list_box_item (_("Secondary Display"),
|
|
|
|
|
_("Join this display with another to create an extra workspace"));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (listbox), item);
|
|
|
|
|
if (!primary && !clone)
|
|
|
|
|
gtk_list_box_select_row (GTK_LIST_BOX (listbox),
|
|
|
|
|
GTK_LIST_BOX_ROW (item));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
#if 0
|
2013-08-20 14:48:04 +01:00
|
|
|
|
item = list_box_item (_("Presentation"),
|
|
|
|
|
_("Show slideshows and media only"));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (listbox), item);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* translators: "Mirror" describes when both displays show the same view */
|
|
|
|
|
item = list_box_item (_("Mirror"),
|
|
|
|
|
_("Show your existing view on both displays"));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (listbox), item);
|
|
|
|
|
if (clone && active)
|
|
|
|
|
gtk_list_box_select_row (GTK_LIST_BOX (listbox),
|
|
|
|
|
GTK_LIST_BOX_ROW (item));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GnomeRRMode **modes;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
modes = gnome_rr_output_list_modes (output);
|
|
|
|
|
setup_resolution_combo_box (panel, modes,
|
|
|
|
|
gnome_rr_output_get_current_mode (output));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), grid);
|
|
|
|
|
gtk_widget_show_all (box);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (priv->current_output, NULL, NULL,
|
|
|
|
|
&old_width, &old_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
GnomeRROutputInfo **outputs;
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GnomeRRRotation rotation;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (g_hash_table_size (output_ids) > 1)
|
|
|
|
|
{
|
|
|
|
|
gint new_width, new_height;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
outputs = gnome_rr_config_get_outputs (priv->current_configuration);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_get_geometry (priv->current_output, NULL, NULL,
|
|
|
|
|
&new_width, &new_height);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
row = gtk_list_box_get_selected_row (GTK_LIST_BOX (listbox));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
switch (gtk_list_box_row_get_index (row))
|
|
|
|
|
{
|
|
|
|
|
case DISPLAY_MODE_PRIMARY:
|
|
|
|
|
primary = TRUE;
|
|
|
|
|
clone = FALSE;
|
|
|
|
|
break;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
#if 0
|
|
|
|
|
case DISPLAY_MODE_PRESENTATION:
|
|
|
|
|
gnome_rr_config_set_clone (priv->current_configuration, FALSE);
|
|
|
|
|
primary = FALSE;
|
|
|
|
|
clone = FALSE;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
case DISPLAY_MODE_MIRROR:
|
|
|
|
|
clone = TRUE;
|
|
|
|
|
break;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
case DISPLAY_MODE_SECONDARY:
|
|
|
|
|
primary = FALSE;
|
|
|
|
|
clone = FALSE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_set_active (priv->current_output, TRUE);
|
|
|
|
|
gnome_rr_output_info_set_primary (priv->current_output, primary);
|
|
|
|
|
gnome_rr_config_set_clone (priv->current_configuration, clone);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
for (i = 0; outputs[i]; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!gnome_rr_output_info_is_active (outputs[i]))
|
|
|
|
|
continue;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (clone)
|
|
|
|
|
{
|
|
|
|
|
/* set all active outputs to the same size and position when
|
|
|
|
|
* cloning */
|
|
|
|
|
gnome_rr_output_info_set_geometry (outputs[i], 0, 0,
|
|
|
|
|
new_width, new_height);
|
|
|
|
|
}
|
|
|
|
|
else if (outputs[i] != priv->current_output)
|
|
|
|
|
{
|
|
|
|
|
/* ensure no other outputs are primary if this output is now
|
|
|
|
|
* primary, or find another output to set as primary if this
|
|
|
|
|
* output is no longer primary */
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gnome_rr_output_info_set_primary (outputs[i], !primary);
|
|
|
|
|
if (!was_primary)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* if the display was previously in clone mode, ensure the outputs
|
|
|
|
|
* are arranged correctly */
|
|
|
|
|
if ((was_clone && !clone))
|
|
|
|
|
lay_out_outputs_horizontally (panel);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!clone)
|
|
|
|
|
realign_outputs_after_resolution_change (panel,
|
|
|
|
|
priv->current_output,
|
|
|
|
|
old_width, old_height);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
/* check rotation */
|
|
|
|
|
rotation = gnome_rr_output_info_get_rotation (priv->current_output);
|
|
|
|
|
|
|
|
|
|
/* other options such as reflection are not supported */
|
|
|
|
|
rotation &= (GNOME_RR_ROTATION_0 | GNOME_RR_ROTATION_90
|
|
|
|
|
| GNOME_RR_ROTATION_180 | GNOME_RR_ROTATION_270);
|
|
|
|
|
if (rotation == 0)
|
|
|
|
|
rotation = GNOME_RR_ROTATION_0;
|
|
|
|
|
gnome_rr_output_info_set_rotation (priv->current_output, rotation);
|
|
|
|
|
|
|
|
|
|
apply_current_configuration (panel);
|
|
|
|
|
}
|
2011-08-25 19:39:48 +01:00
|
|
|
|
else
|
2013-08-20 14:48:04 +01:00
|
|
|
|
{
|
|
|
|
|
/* changes cancelled, so re-read the current configuration */
|
|
|
|
|
on_screen_changed (panel);
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->rotate_left_button = NULL;
|
|
|
|
|
priv->rotate_right_button = NULL;
|
|
|
|
|
priv->res_combo = NULL;
|
|
|
|
|
gtk_widget_destroy (dialog);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
cc_display_panel_box_row_activated (GtkListBox *listbox,
|
|
|
|
|
GtkWidget *row,
|
|
|
|
|
CcDisplayPanel *panel)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
GnomeRROutputInfo *output_info;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_list_box_select_row (listbox, NULL);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output_info = g_object_get_data (G_OBJECT (row), "gnome-rr-output");
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
if (!output_info)
|
|
|
|
|
return;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->current_output = output_info;
|
|
|
|
|
|
|
|
|
|
show_setup_dialog (panel);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-08-20 14:48:04 +01:00
|
|
|
|
mapped_cb (CcDisplayPanel *panel)
|
2011-08-25 19:39:48 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv = panel->priv;
|
|
|
|
|
CcShell *shell;
|
|
|
|
|
GtkWidget *toplevel;
|
|
|
|
|
|
|
|
|
|
shell = cc_panel_get_shell (CC_PANEL (panel));
|
|
|
|
|
toplevel = cc_shell_get_toplevel (shell);
|
|
|
|
|
if (toplevel)
|
|
|
|
|
priv->focus_id = g_signal_connect (toplevel, "notify::has-toplevel-focus",
|
|
|
|
|
G_CALLBACK (dialog_toplevel_focus_changed), panel);
|
2011-08-25 19:39:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-19 17:07:56 +01:00
|
|
|
|
static void
|
|
|
|
|
cc_display_panel_up_client_changed (UpClient *client,
|
|
|
|
|
CcDisplayPanel *self)
|
|
|
|
|
{
|
|
|
|
|
CcDisplayPanelPrivate *priv = self->priv;
|
|
|
|
|
gboolean lid_is_closed;
|
|
|
|
|
|
|
|
|
|
lid_is_closed = up_client_get_lid_is_closed (client);
|
|
|
|
|
|
|
|
|
|
if (lid_is_closed != priv->lid_is_closed)
|
|
|
|
|
{
|
|
|
|
|
priv->lid_is_closed = lid_is_closed;
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
on_screen_changed (self);
|
2013-07-19 17:07:56 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
static void
|
|
|
|
|
cc_display_panel_init (CcDisplayPanel *self)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
CcDisplayPanelPrivate *priv;
|
|
|
|
|
GtkWidget *frame, *vbox;
|
|
|
|
|
GError *error = NULL;
|
2013-08-27 11:52:20 +01:00
|
|
|
|
GSettings *settings;
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_resources_register (cc_display_get_resource ());
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv = self->priv = DISPLAY_PANEL_PRIVATE (self);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-27 11:52:20 +01:00
|
|
|
|
settings = g_settings_new ("org.gnome.desktop.background");
|
|
|
|
|
priv->background = gnome_bg_new ();
|
|
|
|
|
gnome_bg_load_from_preferences (priv->background, settings);
|
|
|
|
|
g_object_unref (settings);
|
|
|
|
|
|
|
|
|
|
priv->thumbnail_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
|
|
|
|
|
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->screen = gnome_rr_screen_new (gdk_screen_get_default (), &error);
|
|
|
|
|
if (!priv->screen)
|
2011-08-25 18:32:15 +01:00
|
|
|
|
{
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_container_add (GTK_CONTAINER (self),
|
|
|
|
|
gtk_label_new (_("Could not get screen information")));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
g_error_free (error);
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_widget_show_all (GTK_WIDGET (self));
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
output_ids = g_hash_table_new (g_direct_hash, g_direct_equal);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (self), vbox);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
|
|
|
gtk_widget_set_margin_left (vbox, 134);
|
|
|
|
|
gtk_widget_set_margin_right (vbox, 134);
|
|
|
|
|
gtk_widget_set_margin_top (vbox, 22);
|
|
|
|
|
gtk_widget_set_margin_bottom (vbox, 22);
|
|
|
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (vbox), frame);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->displays_listbox = gtk_list_box_new ();
|
|
|
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (priv->displays_listbox),
|
|
|
|
|
cc_display_panel_list_box_update_header, NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
g_signal_connect (priv->displays_listbox, "row-activated",
|
|
|
|
|
G_CALLBACK (cc_display_panel_box_row_activated),
|
|
|
|
|
self);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), priv->displays_listbox);
|
2011-08-25 18:32:15 +01:00
|
|
|
|
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
priv->arrange_button = gtk_button_new_with_mnemonic (_("_Arrange Combined Displays"));
|
|
|
|
|
g_signal_connect (priv->arrange_button, "clicked",
|
|
|
|
|
G_CALLBACK (show_arrange_displays_dialog), self);
|
|
|
|
|
gtk_widget_set_halign (priv->arrange_button, GTK_ALIGN_CENTER);
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
gtk_container_add (GTK_CONTAINER (vbox), priv->arrange_button);
|
|
|
|
|
gtk_widget_show_all (vbox);
|
2010-06-07 16:51:11 +01:00
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
on_screen_changed (self);
|
|
|
|
|
priv->screen_changed_handler_id = g_signal_connect_swapped (priv->screen,
|
|
|
|
|
"changed",
|
|
|
|
|
G_CALLBACK (on_screen_changed),
|
|
|
|
|
self);
|
2011-08-25 19:39:48 +01:00
|
|
|
|
|
2013-07-19 17:07:56 +01:00
|
|
|
|
self->priv->up_client = up_client_new ();
|
|
|
|
|
if (up_client_get_lid_is_present (self->priv->up_client))
|
|
|
|
|
{
|
|
|
|
|
/* Connect to the "changed" signal to track changes to "lid-is-closed"
|
|
|
|
|
* property. Connecting to "notify::lid-is-closed" would be preferable,
|
|
|
|
|
* but currently doesn't work as expected:
|
|
|
|
|
* https://bugs.freedesktop.org/show_bug.cgi?id=43001
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
g_signal_connect (self->priv->up_client, "changed",
|
|
|
|
|
G_CALLBACK (cc_display_panel_up_client_changed), self);
|
|
|
|
|
cc_display_panel_up_client_changed (self->priv->up_client, self);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_clear_object (&self->priv->up_client);
|
|
|
|
|
|
2013-08-20 14:48:04 +01:00
|
|
|
|
g_signal_connect (self, "map", G_CALLBACK (mapped_cb), NULL);
|
2010-06-07 16:51:11 +01:00
|
|
|
|
}
|