~ubuntu-branches/ubuntu/lucid/gnome-control-center/lucid-updates

1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1
/* Monitor Settings. A preference panel for configuring monitors
2
 *
3
 * Copyright (C) 2007, 2008  Red Hat, Inc.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
68 by Michael Vogt
* move packaging into bzr
14
 *
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 *
19
 * Author: Soren Sandmann <sandmann@redhat.com>
20
 */
21
22
#include <config.h>
23
#include <gtk/gtk.h>
24
#include <string.h>
25
#include <stdlib.h>
26
#include "scrollarea.h"
27
#define GNOME_DESKTOP_USE_UNSTABLE_API
28
#include <libgnomeui/gnome-rr.h>
29
#include <libgnomeui/gnome-rr-config.h>
68 by Michael Vogt
* move packaging into bzr
30
#include <libgnomeui/gnome-rr-labeler.h>
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
31
#include <gdk/gdkx.h>
32
#include <X11/Xlib.h>
33
#include <glib/gi18n.h>
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
34
#include <gconf/gconf-client.h>
1.1.24 by Didier Roche
Import upstream version 2.25.3
35
#include <dbus/dbus-glib.h>
36
#include <dbus/dbus-glib-bindings.h>
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
37
38
typedef struct App App;
39
typedef struct GrabInfo GrabInfo;
40
41
struct App
42
{
43
    GnomeRRScreen       *screen;
44
    GnomeRRConfig  *current_configuration;
68 by Michael Vogt
* move packaging into bzr
45
    GnomeRRLabeler *labeler;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
46
    GnomeOutputInfo         *current_output;
68 by Michael Vogt
* move packaging into bzr
47
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
48
    GtkWidget	   *dialog;
1.1.24 by Didier Roche
Import upstream version 2.25.3
49
    GtkWidget      *current_monitor_event_box;
50
    GtkWidget      *current_monitor_label;
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
51
    GtkWidget      *monitor_on_radio;
52
    GtkWidget      *monitor_off_radio;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
53
    GtkListStore   *resolution_store;
54
    GtkWidget	   *resolution_combo;
55
    GtkWidget	   *refresh_combo;
56
    GtkWidget	   *rotation_combo;
57
    GtkWidget	   *panel_checkbox;
58
    GtkWidget	   *clone_checkbox;
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
59
    GtkWidget	   *show_icon_checkbox;
68 by Michael Vogt
* move packaging into bzr
60
1.1.28 by Chris Coulson
Import upstream version 2.27.3
61
    /* We store the event timestamp when the Apply button is clicked */
62
    GtkWidget      *apply_button;
63
    guint32         apply_button_clicked_timestamp;
64
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
65
    GtkWidget      *area;
66
    gboolean	    ignore_gui_changes;
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
67
    GConfClient	   *client;
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
68
69
    /* These are used while we are waiting for the ApplyConfiguration method to be executed over D-bus */
70
    DBusGConnection *connection;
71
    DBusGProxy *proxy;
72
    DBusGProxyCall *proxy_call;
1.1.28 by Chris Coulson
Import upstream version 2.27.3
73
74
    enum {
75
	APPLYING_VERSION_1,
76
	APPLYING_VERSION_2
77
    } apply_configuration_state;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
78
};
79
80
static void rebuild_gui (App *app);
1.1.34 by Chris Coulson
Import upstream version 2.29.4
81
static void on_clone_changed (GtkWidget *box, gpointer data);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
82
static void on_rate_changed (GtkComboBox *box, gpointer data);
1.1.24 by Didier Roche
Import upstream version 2.25.3
83
static gboolean output_overlaps (GnomeOutputInfo *output, GnomeRRConfig *config);
84
static void select_current_output_from_dialog_position (App *app);
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
85
static void monitor_on_off_toggled_cb (GtkToggleButton *toggle, gpointer data);
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
86
static void get_geometry (GnomeOutputInfo *output, int *w, int *h);
1.1.28 by Chris Coulson
Import upstream version 2.27.3
87
static void apply_configuration_returned_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, void *data);
1.1.34 by Chris Coulson
Import upstream version 2.29.4
88
static gboolean get_clone_size (GnomeRRScreen *screen, int *width, int *height);
89
static gboolean output_info_supports_mode (App *app, GnomeOutputInfo *info, int width, int height);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
90
91
static void
1.1.24 by Didier Roche
Import upstream version 2.25.3
92
error_message (App *app, const char *primary_text, const char *secondary_text)
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
93
{
1.1.24 by Didier Roche
Import upstream version 2.25.3
94
    GtkWidget *dialog;
68 by Michael Vogt
* move packaging into bzr
95
1.1.24 by Didier Roche
Import upstream version 2.25.3
96
    dialog = gtk_message_dialog_new ((app && app->dialog) ? GTK_WINDOW (app->dialog) : NULL,
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
97
				     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1.1.24 by Didier Roche
Import upstream version 2.25.3
98
				     GTK_MESSAGE_ERROR,
99
				     GTK_BUTTONS_CLOSE,
100
				     "%s", primary_text);
1.1.28 by Chris Coulson
Import upstream version 2.27.3
101
102
    if (secondary_text)
103
	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary_text);
68 by Michael Vogt
* move packaging into bzr
104
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
105
    gtk_dialog_run (GTK_DIALOG (dialog));
106
    gtk_widget_destroy (dialog);
107
}
108
109
static gboolean
110
do_free (gpointer data)
111
{
112
    g_free (data);
113
    return FALSE;
114
}
115
116
static gchar *
117
idle_free (gchar *s)
118
{
119
    g_idle_add (do_free, s);
120
121
    return s;
122
}
123
124
static void
125
on_screen_changed (GnomeRRScreen *scr,
126
		   gpointer data)
127
{
128
    GnomeRRConfig *current;
129
    App *app = data;
130
131
    current = gnome_rr_config_new_current (app->screen);
132
133
    if (app->current_configuration)
134
	gnome_rr_config_free (app->current_configuration);
68 by Michael Vogt
* move packaging into bzr
135
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
136
    app->current_configuration = current;
1.1.27 by Didier Roche
Import upstream version 2.26.0
137
    app->current_output = NULL;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
138
68 by Michael Vogt
* move packaging into bzr
139
    if (app->labeler) {
140
	gnome_rr_labeler_hide (app->labeler);
141
	g_object_unref (app->labeler);
142
    }
143
144
    app->labeler = gnome_rr_labeler_new (app->current_configuration);
145
1.1.24 by Didier Roche
Import upstream version 2.25.3
146
    select_current_output_from_dialog_position (app);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
147
}
148
149
static void
150
on_viewport_changed (FooScrollArea *scroll_area,
151
		     GdkRectangle  *old_viewport,
152
		     GdkRectangle  *new_viewport)
153
{
154
    foo_scroll_area_set_size (scroll_area,
155
			      new_viewport->width,
156
			      new_viewport->height);
157
158
    foo_scroll_area_invalidate (scroll_area);
159
}
160
161
static void
162
layout_set_font (PangoLayout *layout, const char *font)
163
{
164
    PangoFontDescription *desc =
165
	pango_font_description_from_string (font);
166
167
    if (desc)
168
    {
169
	pango_layout_set_font_description (layout, desc);
170
171
	pango_font_description_free (desc);
172
    }
173
}
174
175
static void
176
clear_combo (GtkWidget *widget)
177
{
178
    GtkComboBox *box = GTK_COMBO_BOX (widget);
179
    GtkTreeModel *model = gtk_combo_box_get_model (box);
180
    GtkListStore *store = GTK_LIST_STORE (model);
181
182
    gtk_list_store_clear (store);
183
}
184
185
typedef struct
186
{
187
    const char *text;
188
    gboolean found;
189
    GtkTreeIter iter;
190
} ForeachInfo;
191
192
static gboolean
193
foreach (GtkTreeModel *model,
194
	 GtkTreePath *path,
195
	 GtkTreeIter *iter,
196
	 gpointer data)
197
{
198
    ForeachInfo *info = data;
199
    char *text = NULL;
200
201
    gtk_tree_model_get (model, iter, 0, &text, -1);
202
203
    g_assert (text != NULL);
68 by Michael Vogt
* move packaging into bzr
204
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
205
    if (strcmp (info->text, text) == 0)
206
    {
207
	info->found = TRUE;
208
	info->iter = *iter;
209
	return TRUE;
210
    }
211
212
    return FALSE;
213
}
214
215
static void
216
add_key (GtkWidget *widget,
217
	 const char *text,
218
	 int width, int height, int rate,
219
	 GnomeRRRotation rotation)
220
{
221
    ForeachInfo info;
222
    GtkComboBox *box = GTK_COMBO_BOX (widget);
223
    GtkTreeModel *model = gtk_combo_box_get_model (box);
224
    GtkListStore *store = GTK_LIST_STORE (model);
225
    gboolean retval;
226
227
    info.text = text;
228
    info.found = FALSE;
68 by Michael Vogt
* move packaging into bzr
229
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
230
    gtk_tree_model_foreach (model, foreach, &info);
231
232
    if (!info.found)
233
    {
234
	GtkTreeIter iter;
1.1.23 by Michael Vogt
Import upstream version 2.25.2
235
	gtk_list_store_insert_with_values (store, &iter, -1,
236
                                           0, text,
237
                                           1, width,
238
                                           2, height,
239
                                           3, rate,
240
                                           4, width * height,
241
                                           5, rotation,
242
                                           -1);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
243
244
	retval = TRUE;
245
    }
246
    else
247
    {
248
	retval = FALSE;
249
    }
250
}
251
252
static gboolean
253
combo_select (GtkWidget *widget, const char *text)
254
{
255
    GtkComboBox *box = GTK_COMBO_BOX (widget);
256
    GtkTreeModel *model = gtk_combo_box_get_model (box);
257
    ForeachInfo info;
258
259
    info.text = text;
260
    info.found = FALSE;
68 by Michael Vogt
* move packaging into bzr
261
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
262
    gtk_tree_model_foreach (model, foreach, &info);
263
264
    if (!info.found)
265
	return FALSE;
266
267
    gtk_combo_box_set_active_iter (box, &info.iter);
268
    return TRUE;
269
}
270
271
static GnomeRRMode **
272
get_current_modes (App *app)
273
{
274
    GnomeRROutput *output;
275
276
    if (app->current_configuration->clone)
277
    {
1.1.23 by Michael Vogt
Import upstream version 2.25.2
278
	return gnome_rr_screen_list_clone_modes (app->screen);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
279
    }
280
    else
281
    {
282
	if (!app->current_output)
283
	    return NULL;
68 by Michael Vogt
* move packaging into bzr
284
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
285
	output = gnome_rr_screen_get_output_by_name (
286
	    app->screen, app->current_output->name);
68 by Michael Vogt
* move packaging into bzr
287
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
288
	if (!output)
289
	    return NULL;
68 by Michael Vogt
* move packaging into bzr
290
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
291
	return gnome_rr_output_list_modes (output);
292
    }
293
}
294
295
static void
296
rebuild_rotation_combo (App *app)
297
{
298
    typedef struct
299
    {
300
	GnomeRRRotation	rotation;
301
	const char *	name;
302
    } RotationInfo;
303
    static const RotationInfo rotations[] = {
304
	{ GNOME_RR_ROTATION_0, N_("Normal") },
305
	{ GNOME_RR_ROTATION_90, N_("Left") },
306
	{ GNOME_RR_ROTATION_270, N_("Right") },
307
	{ GNOME_RR_ROTATION_180, N_("Upside Down") },
308
    };
309
    const char *selection;
310
    GnomeRRRotation current;
311
    int i;
68 by Michael Vogt
* move packaging into bzr
312
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
313
    clear_combo (app->rotation_combo);
314
315
    gtk_widget_set_sensitive (
316
	app->rotation_combo, app->current_output && app->current_output->on);
68 by Michael Vogt
* move packaging into bzr
317
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
318
    if (!app->current_output)
319
	return;
68 by Michael Vogt
* move packaging into bzr
320
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
321
    current = app->current_output->rotation;
322
323
    selection = NULL;
324
    for (i = 0; i < G_N_ELEMENTS (rotations); ++i)
325
    {
326
	const RotationInfo *info = &(rotations[i]);
68 by Michael Vogt
* move packaging into bzr
327
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
328
	app->current_output->rotation = info->rotation;
329
1.1.24 by Didier Roche
Import upstream version 2.25.3
330
	/* NULL-GError --- FIXME: we should say why this rotation is not available! */
331
	if (gnome_rr_config_applicable (app->current_configuration, app->screen, NULL))
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
332
	{
1.1.27 by Didier Roche
Import upstream version 2.26.0
333
 	    add_key (app->rotation_combo, _(info->name), 0, 0, 0, info->rotation);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
334
335
	    if (info->rotation == current)
1.1.27 by Didier Roche
Import upstream version 2.26.0
336
		selection = _(info->name);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
337
	}
338
    }
339
340
    app->current_output->rotation = current;
68 by Michael Vogt
* move packaging into bzr
341
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
342
    if (!(selection && combo_select (app->rotation_combo, selection)))
1.1.27 by Didier Roche
Import upstream version 2.26.0
343
	combo_select (app->rotation_combo, _("Normal"));
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
344
}
345
1.1.30 by Chris Coulson
Import upstream version 2.27.5
346
static char *
347
make_rate_string (int hz)
348
{
349
    return g_strdup_printf (_("%d Hz"), hz);
350
}
351
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
352
static void
353
rebuild_rate_combo (App *app)
354
{
355
    GHashTable *rates;
356
    GnomeRRMode **modes;
357
    int best;
358
    int i;
359
360
    clear_combo (app->refresh_combo);
361
362
    gtk_widget_set_sensitive (
363
	app->refresh_combo, app->current_output && app->current_output->on);
364
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
365
    if (!app->current_output
366
        || !(modes = get_current_modes (app)))
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
367
	return;
68 by Michael Vogt
* move packaging into bzr
368
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
369
    rates = g_hash_table_new_full (
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
370
	g_str_hash, g_str_equal, (GFreeFunc) g_free, NULL);
68 by Michael Vogt
* move packaging into bzr
371
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
372
    best = -1;
373
    for (i = 0; modes[i] != NULL; ++i)
374
    {
375
	GnomeRRMode *mode = modes[i];
376
	int width, height, rate;
377
378
	width = gnome_rr_mode_get_width (mode);
379
	height = gnome_rr_mode_get_height (mode);
380
	rate = gnome_rr_mode_get_freq (mode);
381
382
	if (width == app->current_output->width		&&
383
	    height == app->current_output->height)
384
	{
385
	    add_key (app->refresh_combo,
1.1.30 by Chris Coulson
Import upstream version 2.27.5
386
		     idle_free (make_rate_string (rate)),
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
387
		     0, 0, rate, -1);
388
389
	    if (rate > best)
390
		best = rate;
391
	}
392
    }
393
1.1.30 by Chris Coulson
Import upstream version 2.27.5
394
    if (!combo_select (app->refresh_combo, idle_free (make_rate_string (app->current_output->rate))))
395
	combo_select (app->refresh_combo, idle_free (make_rate_string (best)));
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
396
}
397
398
static int
399
count_active_outputs (App *app)
400
{
401
    int i, count = 0;
68 by Michael Vogt
* move packaging into bzr
402
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
403
    for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
404
    {
405
	GnomeOutputInfo *output = app->current_configuration->outputs[i];
406
	if (output->on)
407
	    count++;
408
    }
68 by Michael Vogt
* move packaging into bzr
409
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
410
    return count;
411
}
412
1.1.23 by Michael Vogt
Import upstream version 2.25.2
413
#if 0
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
414
static int
415
count_all_outputs (GnomeRRConfig *config)
416
{
417
    int i;
418
419
    for (i = 0; config->outputs[i] != NULL; i++)
420
	;
421
422
    return i;
423
}
1.1.23 by Michael Vogt
Import upstream version 2.25.2
424
#endif
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
425
1.1.34 by Chris Coulson
Import upstream version 2.29.4
426
/* Computes whether "Mirror Screens" (clone mode) is supported based on these criteria:
427
 *
428
 * 1. There is an available size for cloning.
429
 *
430
 * 2. There are 2 or more connected outputs that support that size.
431
 */
432
static gboolean
433
mirror_screens_is_supported (App *app)
434
{
435
    int clone_width, clone_height;
436
    gboolean have_clone_size;
437
    gboolean mirror_is_supported;
438
439
    mirror_is_supported = FALSE;
440
441
    have_clone_size = get_clone_size (app->screen, &clone_width, &clone_height);
442
443
    if (have_clone_size) {
444
	int i;
445
	int num_outputs_with_clone_size;
446
447
	num_outputs_with_clone_size = 0;
448
449
	for (i = 0; app->current_configuration->outputs[i] != NULL; i++)
450
	{
451
	    GnomeOutputInfo *output = app->current_configuration->outputs[i];
452
453
	    /* We count the connected outputs that support the clone size.  It
454
	     * doesn't matter if those outputs aren't actually On currently; we
455
	     * will turn them on in on_clone_changed().
456
	     */
457
	    if (output->connected && output_info_supports_mode (app, output, clone_width, clone_height))
458
		num_outputs_with_clone_size++;
459
	}
460
461
	if (num_outputs_with_clone_size >= 2)
462
	    mirror_is_supported = TRUE;
463
    }
464
465
    return mirror_is_supported;
466
}
467
468
static void
469
rebuild_mirror_screens (App *app)
470
{
471
    gboolean mirror_is_active;
472
    gboolean mirror_is_supported;
473
474
    g_signal_handlers_block_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app);
475
476
    mirror_is_active = app->current_configuration && app->current_configuration->clone;
477
478
    /* If mirror_is_active, then it *must* be possible to turn mirroring off */
479
    mirror_is_supported = mirror_is_active || mirror_screens_is_supported (app);
480
    
481
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->clone_checkbox), mirror_is_active);
482
    gtk_widget_set_sensitive (app->clone_checkbox, mirror_is_supported);
483
484
    g_signal_handlers_unblock_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app);
485
}
486
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
487
static void
1.1.24 by Didier Roche
Import upstream version 2.25.3
488
rebuild_current_monitor_label (App *app)
489
{
1.1.31 by Chris Coulson
Import upstream version 2.27.91
490
	char *str, *tmp;
1.1.24 by Didier Roche
Import upstream version 2.25.3
491
	GdkColor color;
492
	gboolean use_color;
493
494
	if (app->current_output)
495
	{
1.1.34 by Chris Coulson
Import upstream version 2.29.4
496
	    if (app->current_configuration->clone)
497
		tmp = g_strdup (_("Mirror Screens"));
498
	    else
499
		tmp = g_strdup_printf (_("Monitor: %s"), app->current_output->display_name);
500
1.1.31 by Chris Coulson
Import upstream version 2.27.91
501
	    str = g_strdup_printf ("<b>%s</b>", tmp);
1.1.24 by Didier Roche
Import upstream version 2.25.3
502
	    gnome_rr_labeler_get_color_for_output (app->labeler, app->current_output, &color);
503
	    use_color = TRUE;
1.1.31 by Chris Coulson
Import upstream version 2.27.91
504
	    g_free (tmp);
1.1.24 by Didier Roche
Import upstream version 2.25.3
505
	}
506
	else
507
	{
1.1.31 by Chris Coulson
Import upstream version 2.27.91
508
	    str = g_strdup_printf ("<b>%s</b>", _("Monitor"));
1.1.24 by Didier Roche
Import upstream version 2.25.3
509
	    use_color = FALSE;
510
	}
511
512
	gtk_label_set_markup (GTK_LABEL (app->current_monitor_label), str);
1.1.31 by Chris Coulson
Import upstream version 2.27.91
513
	g_free (str);
1.1.24 by Didier Roche
Import upstream version 2.25.3
514
515
	if (use_color)
1.1.28 by Chris Coulson
Import upstream version 2.27.3
516
	{
517
	    GdkColor black = { 0, 0, 0, 0 };
518
1.1.24 by Didier Roche
Import upstream version 2.25.3
519
	    gtk_widget_modify_bg (app->current_monitor_event_box, app->current_monitor_event_box->state, &color);
520
1.1.28 by Chris Coulson
Import upstream version 2.27.3
521
	    /* Make the label explicitly black.  We don't want it to follow the
522
	     * theme's colors, since the label is always shown against a light
523
	     * pastel background.  See bgo#556050
524
	     */
525
	    gtk_widget_modify_fg (app->current_monitor_label, GTK_WIDGET_STATE (app->current_monitor_label), &black);
526
	}
527
	else
528
	{
529
	    /* Remove any modifications we did on the label's color */
530
	    GtkRcStyle *reset_rc_style;
531
532
	    reset_rc_style = gtk_rc_style_new ();
533
	    gtk_widget_modify_style (app->current_monitor_label, reset_rc_style); /* takes ownership of, and destroys, the rc style */
534
	}
535
1.1.24 by Didier Roche
Import upstream version 2.25.3
536
	gtk_event_box_set_visible_window (GTK_EVENT_BOX (app->current_monitor_event_box), use_color);
537
}
538
539
static void
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
540
rebuild_on_off_radios (App *app)
541
{
542
    gboolean sensitive;
543
    gboolean on_active;
544
    gboolean off_active;
545
546
    g_signal_handlers_block_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
547
    g_signal_handlers_block_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
548
1.1.34 by Chris Coulson
Import upstream version 2.29.4
549
    sensitive = FALSE;
550
    on_active = FALSE;
551
    off_active = FALSE;
552
553
    if (!app->current_configuration->clone && app->current_output)
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
554
    {
555
	if (count_active_outputs (app) > 1 || !app->current_output->on)
556
	    sensitive = TRUE;
557
	else
558
	    sensitive = FALSE;
559
560
	on_active = app->current_output->on;
561
	off_active = !on_active;
562
    }
563
564
    gtk_widget_set_sensitive (app->monitor_on_radio, sensitive);
565
    gtk_widget_set_sensitive (app->monitor_off_radio, sensitive);
566
567
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->monitor_on_radio), on_active);
568
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->monitor_off_radio), off_active);
569
570
    g_signal_handlers_unblock_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
571
    g_signal_handlers_unblock_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app);
572
}
573
1.1.30 by Chris Coulson
Import upstream version 2.27.5
574
static char *
575
make_resolution_string (int width, int height)
576
{
577
    return g_strdup_printf (_("%d x %d"), width, height);
578
}
579
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
580
static void
1.1.34 by Chris Coulson
Import upstream version 2.29.4
581
find_best_mode (GnomeRRMode **modes, int *out_width, int *out_height)
582
{
583
    int i;
584
585
    *out_width = 0;
586
    *out_height = 0;
587
588
    for (i = 0; modes[i] != NULL; i++)
589
    {
590
	int w, h;
591
592
	w = gnome_rr_mode_get_width (modes[i]);
593
	h = gnome_rr_mode_get_height (modes[i]);
594
595
	if (w * h > *out_width * *out_height)
596
	{
597
	    *out_width = w;
598
	    *out_height = h;
599
	}
600
    }
601
}
602
603
static void
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
604
rebuild_resolution_combo (App *app)
605
{
606
    int i;
607
    GnomeRRMode **modes;
608
    const char *current;
609
610
    clear_combo (app->resolution_combo);
68 by Michael Vogt
* move packaging into bzr
611
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
612
    if (!(modes = get_current_modes (app))
613
	|| !app->current_output
614
	|| !app->current_output->on)
615
    {
616
	gtk_widget_set_sensitive (app->resolution_combo, FALSE);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
617
	return;
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
618
    }
619
620
    g_assert (app->current_output != NULL);
1.1.30 by Chris Coulson
Import upstream version 2.27.5
621
    g_assert (app->current_output->width != 0 && app->current_output->height != 0);
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
622
623
    gtk_widget_set_sensitive (app->resolution_combo, TRUE);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
624
625
    for (i = 0; modes[i] != NULL; ++i)
626
    {
627
	int width, height;
628
629
	width = gnome_rr_mode_get_width (modes[i]);
630
	height = gnome_rr_mode_get_height (modes[i]);
68 by Michael Vogt
* move packaging into bzr
631
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
632
	add_key (app->resolution_combo,
1.1.30 by Chris Coulson
Import upstream version 2.27.5
633
		 idle_free (make_resolution_string (width, height)),
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
634
		 width, height, 0, -1);
635
    }
636
1.1.30 by Chris Coulson
Import upstream version 2.27.5
637
    current = idle_free (make_resolution_string (app->current_output->width, app->current_output->height));
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
638
639
    if (!combo_select (app->resolution_combo, current))
1.1.34 by Chris Coulson
Import upstream version 2.29.4
640
    {
641
	int best_w, best_h;
642
643
	find_best_mode (modes, &best_w, &best_h);
644
	combo_select (app->resolution_combo, idle_free (make_resolution_string (best_w, best_h)));
645
    }
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
646
}
647
648
static void
649
rebuild_gui (App *app)
650
{
651
    gboolean sensitive;
652
653
    /* We would break spectacularly if we recursed, so
654
     * just assert if that happens
655
     */
656
    g_assert (app->ignore_gui_changes == FALSE);
68 by Michael Vogt
* move packaging into bzr
657
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
658
    app->ignore_gui_changes = TRUE;
659
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
660
    sensitive = app->current_output ? TRUE : FALSE;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
661
662
#if 0
68 by Michael Vogt
* move packaging into bzr
663
    g_debug ("rebuild gui, is on: %d", app->current_output->on);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
664
#endif
68 by Michael Vogt
* move packaging into bzr
665
1.1.34 by Chris Coulson
Import upstream version 2.29.4
666
    rebuild_mirror_screens (app);
1.1.24 by Didier Roche
Import upstream version 2.25.3
667
    rebuild_current_monitor_label (app);
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
668
    rebuild_on_off_radios (app);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
669
    rebuild_resolution_combo (app);
670
    rebuild_rate_combo (app);
671
    rebuild_rotation_combo (app);
672
673
#if 0
68 by Michael Vogt
* move packaging into bzr
674
    g_debug ("sensitive: %d, on: %d", sensitive, app->current_output->on);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
675
#endif
676
    gtk_widget_set_sensitive (app->panel_checkbox, sensitive);
677
678
    app->ignore_gui_changes = FALSE;
679
}
680
681
static gboolean
682
get_mode (GtkWidget *widget, int *width, int *height, int *freq, GnomeRRRotation *rot)
683
{
684
    GtkTreeIter iter;
685
    GtkTreeModel *model;
686
    GtkComboBox *box = GTK_COMBO_BOX (widget);
687
    int dummy;
688
689
    if (!gtk_combo_box_get_active_iter (box, &iter))
690
	return FALSE;
691
692
    if (!width)
693
	width = &dummy;
694
695
    if (!height)
696
	height = &dummy;
697
698
    if (!freq)
699
	freq = &dummy;
700
701
    if (!rot)
702
	rot = (GnomeRRRotation *)&dummy;
68 by Michael Vogt
* move packaging into bzr
703
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
704
    model = gtk_combo_box_get_model (box);
705
    gtk_tree_model_get (model, &iter,
706
			1, width,
707
			2, height,
708
			3, freq,
709
			5, rot,
710
			-1);
711
712
    return TRUE;
713
714
}
715
716
static void
717
on_rotation_changed (GtkComboBox *box, gpointer data)
718
{
719
    App *app = data;
720
    GnomeRRRotation rotation;
721
722
    if (!app->current_output)
723
	return;
724
725
    if (get_mode (app->rotation_combo, NULL, NULL, NULL, &rotation))
726
	app->current_output->rotation = rotation;
727
728
    foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
729
}
730
731
static void
732
on_rate_changed (GtkComboBox *box, gpointer data)
733
{
734
    App *app = data;
735
    int rate;
736
737
    if (!app->current_output)
738
	return;
739
740
    if (get_mode (app->refresh_combo, NULL, NULL, &rate, NULL))
741
	app->current_output->rate = rate;
742
743
    foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
744
}
745
746
static void
1.1.30 by Chris Coulson
Import upstream version 2.27.5
747
select_resolution_for_current_output (App *app)
748
{
749
    GnomeRRMode **modes;
750
    int width, height;
751
752
    if (app->current_output->pref_width != 0 && app->current_output->pref_height != 0)
753
    {
754
	app->current_output->width = app->current_output->pref_width;
755
	app->current_output->height = app->current_output->pref_height;
756
	return;
757
    }
758
759
    modes = get_current_modes (app);
760
    if (!modes)
761
	return;
762
763
    find_best_mode (modes, &width, &height);
764
765
    app->current_output->width = width;
766
    app->current_output->height = height;
767
}
768
769
static void
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
770
monitor_on_off_toggled_cb (GtkToggleButton *toggle, gpointer data)
771
{
772
    App *app = data;
773
    gboolean is_on;
774
775
    if (!app->current_output)
776
	return;
777
778
    if (!gtk_toggle_button_get_active (toggle))
779
	return;
780
781
    if (GTK_WIDGET (toggle) == app->monitor_on_radio)
782
	is_on = TRUE;
783
    else if (GTK_WIDGET (toggle) == app->monitor_off_radio)
784
	is_on = FALSE;
785
    else
786
    {
787
	g_assert_not_reached ();
788
	return;
789
    }
790
791
    app->current_output->on = is_on;
792
1.1.30 by Chris Coulson
Import upstream version 2.27.5
793
    if (is_on)
794
	select_resolution_for_current_output (app); /* The refresh rate will be picked in rebuild_rate_combo() */
795
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
796
    rebuild_gui (app);
797
    foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
798
}
799
800
static void
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
801
realign_outputs_after_resolution_change (App *app, GnomeOutputInfo *output_that_changed, int old_width, int old_height)
802
{
803
    /* We find the outputs that were below or to the right of the output that
804
     * changed, and realign them; we also do that for outputs that shared the
805
     * right/bottom edges with the output that changed.  The outputs that are
806
     * above or to the left of that output don't need to change.
807
     */
808
809
    int i;
810
    int old_right_edge, old_bottom_edge;
811
    int dx, dy;
812
813
    g_assert (app->current_configuration != NULL);
814
1.1.30 by Chris Coulson
Import upstream version 2.27.5
815
    if (output_that_changed->width == old_width && output_that_changed->height == old_height)
816
	return;
817
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
818
    old_right_edge = output_that_changed->x + old_width;
819
    old_bottom_edge = output_that_changed->y + old_height;
820
821
    dx = output_that_changed->width - old_width;
822
    dy = output_that_changed->height - old_height;
823
824
    for (i = 0; app->current_configuration->outputs[i] != NULL; i++) {
825
	GnomeOutputInfo *output;
826
	int output_width, output_height;
827
828
	output = app->current_configuration->outputs[i];
829
830
	if (output == output_that_changed || !output->connected)
831
	    continue;
832
833
	get_geometry (output, &output_width, &output_height);
834
835
	if (output->x >= old_right_edge)
836
	    output->x += dx;
837
	else if (output->x + output_width == old_right_edge)
838
	    output->x = output_that_changed->x + output_that_changed->width - output_width;
839
840
	if (output->y >= old_bottom_edge)
841
	    output->y += dy;
842
	else if (output->y + output_height == old_bottom_edge)
843
	    output->y = output_that_changed->y + output_that_changed->height - output_height;
844
    }
845
}
846
847
static void
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
848
on_resolution_changed (GtkComboBox *box, gpointer data)
849
{
850
    App *app = data;
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
851
    int old_width, old_height;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
852
    int width;
853
    int height;
854
855
    if (!app->current_output)
856
	return;
857
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
858
    old_width = app->current_output->width;
859
    old_height = app->current_output->height;
860
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
861
    if (get_mode (app->resolution_combo, &width, &height, NULL, NULL))
862
    {
863
	app->current_output->width = width;
864
	app->current_output->height = height;
865
866
	if (width == 0 || height == 0)
867
	    app->current_output->on = FALSE;
868
	else
869
	    app->current_output->on = TRUE;
870
    }
68 by Michael Vogt
* move packaging into bzr
871
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
872
    realign_outputs_after_resolution_change (app, app->current_output, old_width, old_height);
68 by Michael Vogt
* move packaging into bzr
873
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
874
    rebuild_rate_combo (app);
875
    rebuild_rotation_combo (app);
68 by Michael Vogt
* move packaging into bzr
876
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
877
    foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
878
}
879
880
static void
1.1.24 by Didier Roche
Import upstream version 2.25.3
881
lay_out_outputs_horizontally (App *app)
882
{
883
    int i;
884
    int x;
885
886
    /* Lay out all the monitors horizontally when "mirror screens" is turned
887
     * off, to avoid having all of them overlapped initially.  We put the
888
     * outputs turned off on the right-hand side.
889
     */
890
891
    x = 0;
892
893
    /* First pass, all "on" outputs */
894
895
    for (i = 0; app->current_configuration->outputs[i]; ++i)
896
    {
897
	GnomeOutputInfo *output;
898
899
	output = app->current_configuration->outputs[i];
1.1.28 by Chris Coulson
Import upstream version 2.27.3
900
	if (output->connected && output->on) {
1.1.24 by Didier Roche
Import upstream version 2.25.3
901
	    output->x = x;
1.1.28 by Chris Coulson
Import upstream version 2.27.3
902
	    output->y = 0;
903
	    x += output->width;
904
	}
1.1.24 by Didier Roche
Import upstream version 2.25.3
905
    }
906
907
    /* Second pass, all the black screens */
908
909
    for (i = 0; app->current_configuration->outputs[i]; ++i)
910
    {
911
	GnomeOutputInfo *output;
912
913
	output = app->current_configuration->outputs[i];
1.1.28 by Chris Coulson
Import upstream version 2.27.3
914
	if (!(output->connected && output->on)) {
1.1.24 by Didier Roche
Import upstream version 2.25.3
915
	    output->x = x;
1.1.28 by Chris Coulson
Import upstream version 2.27.3
916
	    output->y = 0;
917
	    x += output->width;
918
	}
1.1.24 by Didier Roche
Import upstream version 2.25.3
919
    }
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
920
1.1.24 by Didier Roche
Import upstream version 2.25.3
921
}
922
1.1.34 by Chris Coulson
Import upstream version 2.29.4
923
/* FIXME: this function is copied from gnome-settings-daemon/plugins/xrandr/gsd-xrandr-manager.c.
924
 * Do we need to put this function in gnome-desktop for public use?
925
 */
926
static gboolean
927
get_clone_size (GnomeRRScreen *screen, int *width, int *height)
928
{
929
        GnomeRRMode **modes = gnome_rr_screen_list_clone_modes (screen);
930
        int best_w, best_h;
931
        int i;
932
933
        best_w = 0;
934
        best_h = 0;
935
936
        for (i = 0; modes[i] != NULL; ++i) {
937
                GnomeRRMode *mode = modes[i];
938
                int w, h;
939
940
                w = gnome_rr_mode_get_width (mode);
941
                h = gnome_rr_mode_get_height (mode);
942
943
                if (w * h > best_w * best_h) {
944
                        best_w = w;
945
                        best_h = h;
946
                }
947
        }
948
949
        if (best_w > 0 && best_h > 0) {
950
                if (width)
951
                        *width = best_w;
952
                if (height)
953
                        *height = best_h;
954
955
                return TRUE;
956
        }
957
958
        return FALSE;
959
}
960
961
static gboolean
962
output_info_supports_mode (App *app, GnomeOutputInfo *info, int width, int height)
963
{
964
    GnomeRROutput *output;
965
    GnomeRRMode **modes;
966
    int i;
967
968
    if (!info->connected)
969
	return FALSE;
970
971
    output = gnome_rr_screen_get_output_by_name (app->screen, info->name);
972
    if (!output)
973
	return FALSE;
974
975
    modes = gnome_rr_output_list_modes (output);
976
977
    for (i = 0; modes[i]; i++) {
978
	if (gnome_rr_mode_get_width (modes[i]) == width
979
	    && gnome_rr_mode_get_height (modes[i]) == height)
980
	    return TRUE;
981
    }
982
983
    return FALSE;
984
}
985
1.1.24 by Didier Roche
Import upstream version 2.25.3
986
static void
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
987
on_clone_changed (GtkWidget *box, gpointer data)
988
{
989
    App *app = data;
990
991
    app->current_configuration->clone =
992
	gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (app->clone_checkbox));
993
994
    if (app->current_configuration->clone)
995
    {
996
	int i;
1.1.34 by Chris Coulson
Import upstream version 2.29.4
997
	int width, height;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
998
999
	for (i = 0; app->current_configuration->outputs[i]; ++i)
1000
	{
1001
	    if (app->current_configuration->outputs[i]->connected)
1002
	    {
1003
		app->current_output = app->current_configuration->outputs[i];
1004
		break;
1005
	    }
1006
	}
1.1.34 by Chris Coulson
Import upstream version 2.29.4
1007
1008
	/* Turn on all the connected screens that support the best clone mode.
1009
	 * The user may hit "Mirror Screens", but he shouldn't have to turn on
1010
	 * all the required outputs as well.
1011
	 */
1012
1013
	get_clone_size (app->screen, &width, &height);
1014
1015
	for (i = 0; app->current_configuration->outputs[i]; i++) {
1016
	    if (output_info_supports_mode (app, app->current_configuration->outputs[i], width, height)) {
1017
		app->current_configuration->outputs[i]->on = TRUE;
1018
		app->current_configuration->outputs[i]->width = width;
1019
		app->current_configuration->outputs[i]->height = height;
1020
	    }
1021
	}
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1022
    }
1.1.24 by Didier Roche
Import upstream version 2.25.3
1023
    else
1024
    {
1025
	if (output_overlaps (app->current_output, app->current_configuration))
1026
	    lay_out_outputs_horizontally (app);
1027
    }
68 by Michael Vogt
* move packaging into bzr
1028
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1029
    rebuild_gui (app);
1030
}
1031
1032
static void
1033
get_geometry (GnomeOutputInfo *output, int *w, int *h)
1034
{
1035
    if (output->on)
1036
    {
1037
	*h = output->height;
1038
	*w = output->width;
1039
    }
1040
    else
1041
    {
1042
	*h = output->pref_height;
1043
	*w = output->pref_width;
1044
    }
1.1.31 by Chris Coulson
Import upstream version 2.27.91
1045
   if ((output->rotation & GNOME_RR_ROTATION_90) || (output->rotation & GNOME_RR_ROTATION_270))
1046
   {
1047
        int tmp;
1048
        tmp = *h;
1049
        *h = *w;
1050
        *w = tmp;
1051
   }
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1052
}
1053
1054
#define SPACE 15
1055
#define MARGIN  15
1056
1057
static GList *
1058
list_connected_outputs (App *app, int *total_w, int *total_h)
1059
{
1060
    int i, dummy;
1061
    GList *result = NULL;
1062
1063
    if (!total_w)
1064
	total_w = &dummy;
1065
    if (!total_h)
1066
	total_h = &dummy;
1067
1068
    *total_w = 0;
1069
    *total_h = 0;
1070
    for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
1071
    {
1072
	GnomeOutputInfo *output = app->current_configuration->outputs[i];
1073
1074
	if (output->connected)
1075
	{
1076
	    int w, h;
68 by Michael Vogt
* move packaging into bzr
1077
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1078
	    result = g_list_prepend (result, output);
1079
1080
	    get_geometry (output, &w, &h);
1081
1082
	    *total_w += w;
1083
	    *total_h += h;
1084
	}
1085
    }
1086
1087
    return g_list_reverse (result);
1088
}
1089
1090
static int
1091
get_n_connected (App *app)
1092
{
1093
    GList *connected_outputs = list_connected_outputs (app, NULL, NULL);
1094
    int n = g_list_length (connected_outputs);
1095
1096
    g_list_free (connected_outputs);
1097
1098
    return n;
1099
}
1100
1101
static double
1102
compute_scale (App *app)
1103
{
1104
    int available_w, available_h;
1105
    int total_w, total_h;
1106
    int n_monitors;
1107
    GdkRectangle viewport;
1108
    GList *connected_outputs;
68 by Michael Vogt
* move packaging into bzr
1109
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1110
    foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport);
1111
1112
    connected_outputs = list_connected_outputs (app, &total_w, &total_h);
68 by Michael Vogt
* move packaging into bzr
1113
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1114
    n_monitors = g_list_length (connected_outputs);
1115
1116
    g_list_free (connected_outputs);
1117
1118
    available_w = viewport.width - 2 * MARGIN - (n_monitors - 1) * SPACE;
1119
    available_h = viewport.height - 2 * MARGIN - (n_monitors - 1) * SPACE;
1120
1121
    return MIN ((double)available_w / total_w, (double)available_h / total_h);
1122
}
1123
1124
typedef struct Edge
1125
{
1126
    GnomeOutputInfo *output;
1127
    int x1, y1;
1128
    int x2, y2;
1129
} Edge;
1130
1131
typedef struct Snap
1132
{
1133
    Edge *snapper;		/* Edge that should be snapped */
1134
    Edge *snappee;
68 by Michael Vogt
* move packaging into bzr
1135
    int dy, dx;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1136
} Snap;
1137
1138
static void
1139
add_edge (GnomeOutputInfo *output, int x1, int y1, int x2, int y2, GArray *edges)
1140
{
1141
    Edge e;
68 by Michael Vogt
* move packaging into bzr
1142
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1143
    e.x1 = x1;
1144
    e.x2 = x2;
1145
    e.y1 = y1;
1146
    e.y2 = y2;
1147
    e.output = output;
1148
1149
    g_array_append_val (edges, e);
1150
}
1151
1152
static void
1153
list_edges_for_output (GnomeOutputInfo *output, GArray *edges)
1154
{
1155
    int x, y, w, h;
68 by Michael Vogt
* move packaging into bzr
1156
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1157
    x = output->x;
1158
    y = output->y;
1159
    get_geometry (output, &w, &h);
68 by Michael Vogt
* move packaging into bzr
1160
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1161
    /* Top, Bottom, Left, Right */
1162
    add_edge (output, x, y, x + w, y, edges);
1163
    add_edge (output, x, y + h, x + w, y + h, edges);
1164
    add_edge (output, x, y, x, y + h, edges);
1165
    add_edge (output, x + w, y, x + w, y + h, edges);
1166
}
1167
1168
static void
1169
list_edges (GnomeRRConfig *config, GArray *edges)
1170
{
1171
    int i;
1172
1173
    for (i = 0; config->outputs[i]; ++i)
1174
    {
1175
	GnomeOutputInfo *output = config->outputs[i];
1176
1177
	if (output->connected)
1178
	    list_edges_for_output (output, edges);
1179
    }
1180
}
1181
1182
static gboolean
1183
overlap (int s1, int e1, int s2, int e2)
1184
{
1185
    return (!(e1 < s2 || s1 >= e2));
1186
}
1187
1188
static gboolean
1189
horizontal_overlap (Edge *snapper, Edge *snappee)
1190
{
1191
    if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2)
1192
	return FALSE;
1193
1194
    return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2);
1195
}
1196
1197
static gboolean
1198
vertical_overlap (Edge *snapper, Edge *snappee)
1199
{
1200
    if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2)
1201
	return FALSE;
68 by Michael Vogt
* move packaging into bzr
1202
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1203
    return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2);
1204
}
1205
1206
static void
1207
add_snap (GArray *snaps, Snap snap)
1208
{
1209
    if (ABS (snap.dx) <= 200 || ABS (snap.dy) <= 200)
1210
	g_array_append_val (snaps, snap);
1211
}
1212
1213
static void
1214
add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps)
1215
{
1216
    Snap snap;
1217
1218
    snap.snapper = snapper;
1219
    snap.snappee = snappee;
68 by Michael Vogt
* move packaging into bzr
1220
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1221
    if (horizontal_overlap (snapper, snappee))
1222
    {
1223
	snap.dx = 0;
1224
	snap.dy = snappee->y1 - snapper->y1;
1225
1226
	add_snap (snaps, snap);
1227
    }
1228
    else if (vertical_overlap (snapper, snappee))
1229
    {
1230
	snap.dy = 0;
1231
	snap.dx = snappee->x1 - snapper->x1;
1232
1233
	add_snap (snaps, snap);
1234
    }
1235
1236
    /* Corner snaps */
1237
    /* 1->1 */
1238
    snap.dx = snappee->x1 - snapper->x1;
1239
    snap.dy = snappee->y1 - snapper->y1;
68 by Michael Vogt
* move packaging into bzr
1240
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1241
    add_snap (snaps, snap);
68 by Michael Vogt
* move packaging into bzr
1242
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1243
    /* 1->2 */
1244
    snap.dx = snappee->x2 - snapper->x1;
1245
    snap.dy = snappee->y2 - snapper->y1;
1246
1247
    add_snap (snaps, snap);
1248
1249
    /* 2->2 */
1250
    snap.dx = snappee->x2 - snapper->x2;
1251
    snap.dy = snappee->y2 - snapper->y2;
1252
1253
    add_snap (snaps, snap);
1254
1255
    /* 2->1 */
1256
    snap.dx = snappee->x1 - snapper->x2;
1257
    snap.dy = snappee->y1 - snapper->y2;
1258
1259
    add_snap (snaps, snap);
1260
}
1261
1262
static void
1263
list_snaps (GnomeOutputInfo *output, GArray *edges, GArray *snaps)
1264
{
1265
    int i;
1266
1267
    for (i = 0; i < edges->len; ++i)
1268
    {
1269
	Edge *output_edge = &(g_array_index (edges, Edge, i));
1270
1271
	if (output_edge->output == output)
1272
	{
1273
	    int j;
68 by Michael Vogt
* move packaging into bzr
1274
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1275
	    for (j = 0; j < edges->len; ++j)
68 by Michael Vogt
* move packaging into bzr
1276
	    {
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1277
		Edge *edge = &(g_array_index (edges, Edge, j));
1278
1279
		if (edge->output != output)
1280
		    add_edge_snaps (output_edge, edge, snaps);
1281
	    }
1282
	}
1283
    }
1284
}
1285
1286
#if 0
1287
static void
1288
print_edge (Edge *edge)
1289
{
68 by Michael Vogt
* move packaging into bzr
1290
    g_debug ("(%d %d %d %d)", edge->x1, edge->y1, edge->x2, edge->y2);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1291
}
1292
#endif
1293
1294
static gboolean
1295
corner_on_edge (int x, int y, Edge *e)
1296
{
1297
    if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2)
1298
	return TRUE;
68 by Michael Vogt
* move packaging into bzr
1299
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1300
    if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2)
1301
	return TRUE;
1302
1303
    return FALSE;
1304
}
1305
1306
static gboolean
1307
edges_align (Edge *e1, Edge *e2)
1308
{
1309
    if (corner_on_edge (e1->x1, e1->y1, e2))
1310
	return TRUE;
1311
1312
    if (corner_on_edge (e2->x1, e2->y1, e1))
1313
	return TRUE;
1314
1315
    return FALSE;
1316
}
1317
1318
static gboolean
1319
output_is_aligned (GnomeOutputInfo *output, GArray *edges)
1320
{
1321
    gboolean result = FALSE;
1322
    int i;
1323
1324
    for (i = 0; i < edges->len; ++i)
1325
    {
1326
	Edge *output_edge = &(g_array_index (edges, Edge, i));
1327
1328
	if (output_edge->output == output)
1329
	{
1330
	    int j;
1331
1332
	    for (j = 0; j < edges->len; ++j)
1333
	    {
1334
		Edge *edge = &(g_array_index (edges, Edge, j));
1335
1336
		/* We are aligned if an output edge matches
1337
		 * an edge of another output
1338
		 */
1339
		if (edge->output != output_edge->output)
1340
		{
1341
		    if (edges_align (output_edge, edge))
1342
		    {
1343
			result = TRUE;
1344
			goto done;
1345
		    }
1346
		}
1347
	    }
1348
	}
1349
    }
1350
done:
1351
1352
    return result;
1353
}
1354
1355
static void
1356
get_output_rect (GnomeOutputInfo *output, GdkRectangle *rect)
1357
{
1358
    int w, h;
1359
1360
    get_geometry (output, &w, &h);
1361
1362
    rect->width = w;
1363
    rect->height = h;
1364
    rect->x = output->x;
1365
    rect->y = output->y;
1366
}
1367
1368
static gboolean
1369
output_overlaps (GnomeOutputInfo *output, GnomeRRConfig *config)
1370
{
1371
    int i;
1372
    GdkRectangle output_rect;
1373
1374
    get_output_rect (output, &output_rect);
1375
1376
    for (i = 0; config->outputs[i]; ++i)
1377
    {
1378
	GnomeOutputInfo *other = config->outputs[i];
1379
1380
	if (other != output && other->connected)
1381
	{
1382
	    GdkRectangle other_rect;
68 by Michael Vogt
* move packaging into bzr
1383
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1384
	    get_output_rect (other, &other_rect);
1385
	    if (gdk_rectangle_intersect (&output_rect, &other_rect, NULL))
1386
		return TRUE;
1387
	}
1388
    }
1389
1390
    return FALSE;
1391
}
1392
1393
static gboolean
1394
gnome_rr_config_is_aligned (GnomeRRConfig *config, GArray *edges)
1395
{
1396
    int i;
1397
    gboolean result = TRUE;
1398
1399
    for (i = 0; config->outputs[i]; ++i)
1400
    {
1401
	GnomeOutputInfo *output = config->outputs[i];
1402
1403
	if (output->connected)
1404
	{
1405
	    if (!output_is_aligned (output, edges))
1406
		return FALSE;
1407
1408
	    if (output_overlaps (output, config))
1409
		return FALSE;
1410
	}
1411
    }
1412
1413
    return result;
1414
}
1415
1416
struct GrabInfo
1417
{
1418
    int grab_x;
1419
    int grab_y;
1420
    int output_x;
1421
    int output_y;
1422
};
1423
1424
static gboolean
1425
is_corner_snap (const Snap *s)
1426
{
1427
    return s->dx != 0 && s->dy != 0;
1428
}
1429
1430
static int
1431
compare_snaps (gconstpointer v1, gconstpointer v2)
1432
{
1433
    const Snap *s1 = v1;
1434
    const Snap *s2 = v2;
1435
    int sv1 = MAX (ABS (s1->dx), ABS (s1->dy));
1436
    int sv2 = MAX (ABS (s2->dx), ABS (s2->dy));
1437
    int d;
1438
1439
    d = sv1 - sv2;
1440
1441
    /* This snapping algorithm is good enough for rock'n'roll, but
1442
     * this is probably a better:
1443
     *
1444
     *    First do a horizontal/vertical snap, then
1445
     *    with the new coordinates from that snap,
1446
     *    do a corner snap.
1447
     *
1448
     * Right now, it's confusing that corner snapping
1449
     * depends on the distance in an axis that you can't actually see.
1450
     *
1451
     */
1452
    if (d == 0)
1453
    {
1454
	if (is_corner_snap (s1) && !is_corner_snap (s2))
1455
	    return -1;
1456
	else if (is_corner_snap (s2) && !is_corner_snap (s1))
1457
	    return 1;
1458
	else
1459
	    return 0;
1460
    }
1461
    else
1462
    {
1463
	return d;
1464
    }
1465
}
1466
1.1.37 by Chris Coulson
Import upstream version 2.29.91
1467
/* Sets a mouse cursor for a widget's window.  As a hack, you can pass
1468
 * GDK_BLANK_CURSOR to mean "set the cursor to NULL" (i.e. reset the widget's
1469
 * window's cursor to its default).
1470
 */
1471
static void
1472
set_cursor (GtkWidget *widget, GdkCursorType type)
1473
{
1474
	GdkCursor *cursor;
1475
	GdkWindow *window;
1476
1477
	if (type == GDK_BLANK_CURSOR)
1478
	    cursor = NULL;
1479
	else
1480
	    cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), type);
1481
1482
	window = gtk_widget_get_window (widget);
1483
1484
	if (window)
1485
	    gdk_window_set_cursor (window, cursor);
1486
1487
	if (cursor)
1488
	    gdk_cursor_unref (cursor);
1489
}
1490
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1491
static void
1.1.39 by Didier Roche
Import upstream version 2.30.0
1492
set_monitors_tooltip (App *app, gboolean is_dragging)
1493
{
1494
    const char *text;
1495
1496
    if (is_dragging)
1497
	text = NULL;
1498
    else
1499
	text = _("Select a monitor to change its properties; drag it to rearrange its placement.");
1500
1501
    gtk_widget_set_tooltip_text (app->area, text);
1502
}
1503
1504
static void
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1505
on_output_event (FooScrollArea *area,
1506
		 FooScrollAreaEvent *event,
1507
		 gpointer data)
1508
{
1509
    GnomeOutputInfo *output = data;
1510
    App *app = g_object_get_data (G_OBJECT (area), "app");
1511
1.1.37 by Chris Coulson
Import upstream version 2.29.91
1512
    /* If the mouse is inside the outputs, set the cursor to "you can move me".  See
1513
     * on_canvas_event() for where we reset the cursor to the default if it
1514
     * exits the outputs' area.
1515
     */
1516
    if (!app->current_configuration->clone && get_n_connected (app) > 1)
1517
	set_cursor (GTK_WIDGET (area), GDK_FLEUR);
1518
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1519
    if (event->type == FOO_BUTTON_PRESS)
1520
    {
1521
	GrabInfo *info;
68 by Michael Vogt
* move packaging into bzr
1522
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1523
	app->current_output = output;
1524
1525
	rebuild_gui (app);
1.1.39 by Didier Roche
Import upstream version 2.30.0
1526
	set_monitors_tooltip (app, TRUE);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1527
1528
	if (!app->current_configuration->clone && get_n_connected (app) > 1)
1529
	{
1530
	    foo_scroll_area_begin_grab (area, on_output_event, data);
68 by Michael Vogt
* move packaging into bzr
1531
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1532
	    info = g_new0 (GrabInfo, 1);
1533
	    info->grab_x = event->x;
1534
	    info->grab_y = event->y;
1535
	    info->output_x = output->x;
1536
	    info->output_y = output->y;
68 by Michael Vogt
* move packaging into bzr
1537
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1538
	    output->user_data = info;
1539
	}
68 by Michael Vogt
* move packaging into bzr
1540
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1541
	foo_scroll_area_invalidate (area);
1542
    }
1543
    else
1544
    {
1545
	if (foo_scroll_area_is_grabbed (area))
1546
	{
1547
	    GrabInfo *info = output->user_data;
1548
	    double scale = compute_scale (app);
1549
	    int old_x, old_y;
1550
	    int new_x, new_y;
1551
	    int i;
1552
	    GArray *edges, *snaps, *new_edges;
1553
1554
	    old_x = output->x;
1555
	    old_y = output->y;
1556
	    new_x = info->output_x + (event->x - info->grab_x) / scale;
1557
	    new_y = info->output_y + (event->y - info->grab_y) / scale;
68 by Michael Vogt
* move packaging into bzr
1558
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1559
	    output->x = new_x;
1560
	    output->y = new_y;
1561
1562
	    edges = g_array_new (TRUE, TRUE, sizeof (Edge));
1563
	    snaps = g_array_new (TRUE, TRUE, sizeof (Snap));
1564
	    new_edges = g_array_new (TRUE, TRUE, sizeof (Edge));
68 by Michael Vogt
* move packaging into bzr
1565
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1566
	    list_edges (app->current_configuration, edges);
1567
	    list_snaps (output, edges, snaps);
1568
1569
	    g_array_sort (snaps, compare_snaps);
1570
1571
	    output->x = info->output_x;
1572
	    output->y = info->output_y;
68 by Michael Vogt
* move packaging into bzr
1573
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1574
	    for (i = 0; i < snaps->len; ++i)
1575
	    {
1576
		Snap *snap = &(g_array_index (snaps, Snap, i));
1577
		GArray *new_edges = g_array_new (TRUE, TRUE, sizeof (Edge));
1578
1579
		output->x = new_x + snap->dx;
1580
		output->y = new_y + snap->dy;
1581
1582
		g_array_set_size (new_edges, 0);
1583
		list_edges (app->current_configuration, new_edges);
68 by Michael Vogt
* move packaging into bzr
1584
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1585
		if (gnome_rr_config_is_aligned (app->current_configuration, new_edges))
1586
		{
1587
		    g_array_free (new_edges, TRUE);
1588
		    break;
1589
		}
1590
		else
1591
		{
1592
		    output->x = info->output_x;
1593
		    output->y = info->output_y;
1594
		}
1595
	    }
1596
1597
	    g_array_free (new_edges, TRUE);
1598
	    g_array_free (snaps, TRUE);
1599
	    g_array_free (edges, TRUE);
68 by Michael Vogt
* move packaging into bzr
1600
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1601
	    if (event->type == FOO_BUTTON_RELEASE)
1602
	    {
1603
		foo_scroll_area_end_grab (area);
1.1.39 by Didier Roche
Import upstream version 2.30.0
1604
		set_monitors_tooltip (app, FALSE);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1605
1606
		g_free (output->user_data);
1607
		output->user_data = NULL;
1608
1609
#if 0
68 by Michael Vogt
* move packaging into bzr
1610
		g_debug ("new position: %d %d %d %d", output->x, output->y, output->width, output->height);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1611
#endif
1612
	    }
1613
1614
	    foo_scroll_area_invalidate (area);
1615
	}
1616
    }
1617
}
1618
1619
static void
1620
on_canvas_event (FooScrollArea *area,
1621
		 FooScrollAreaEvent *event,
1622
		 gpointer data)
1623
{
1.1.37 by Chris Coulson
Import upstream version 2.29.91
1624
    /* If the mouse exits the outputs, reset the cursor to the default.  See
1625
     * on_output_event() for where we set the cursor to the movement cursor if
1626
     * it is over one of the outputs.
1627
     */
1628
    set_cursor (GTK_WIDGET (area), GDK_BLANK_CURSOR);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1629
}
1630
1631
static PangoLayout *
1632
get_display_name (App *app,
1633
		  GnomeOutputInfo *output)
1634
{
1635
    const char *text;
68 by Michael Vogt
* move packaging into bzr
1636
1.1.21 by Michael Vogt
Import upstream version 2.24.0
1637
    if (app->current_configuration->clone) {
1638
	/* Translators:  this is the feature where what you see on your laptop's
1639
	 * screen is the same as your external monitor.  Here, "Mirror" is being
1640
	 * used as an adjective, not as a verb.  For example, the Spanish
1641
	 * translation could be "Pantallas en Espejo", *not* "Espejar Pantallas".
1642
	 */
1643
	text = _("Mirror Screens");
1644
    } else
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1645
	text = output->display_name;
1646
1647
    return gtk_widget_create_pango_layout (
1648
	GTK_WIDGET (app->area), text);
1649
}
1650
1651
static void
1652
paint_background (FooScrollArea *area,
1653
		  cairo_t       *cr)
1654
{
1655
    GdkRectangle viewport;
1.1.21 by Michael Vogt
Import upstream version 2.24.0
1656
    GtkWidget *widget;
1657
1658
    widget = GTK_WIDGET (area);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1659
1660
    foo_scroll_area_get_viewport (area, &viewport);
68 by Michael Vogt
* move packaging into bzr
1661
1.1.21 by Michael Vogt
Import upstream version 2.24.0
1662
    cairo_set_source_rgb (cr,
1663
                          widget->style->base[GTK_STATE_SELECTED].red / 65535.0,
1664
                          widget->style->base[GTK_STATE_SELECTED].green / 65535.0,
1665
                          widget->style->base[GTK_STATE_SELECTED].blue / 65535.0);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1666
1667
    cairo_rectangle (cr,
1668
		     viewport.x, viewport.y,
1669
		     viewport.width, viewport.height);
1670
1671
    cairo_fill_preserve (cr);
1672
1673
    foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL);
1674
1.1.21 by Michael Vogt
Import upstream version 2.24.0
1675
    cairo_set_source_rgb (cr,
1676
                          widget->style->dark[GTK_STATE_SELECTED].red / 65535.0,
1677
                          widget->style->dark[GTK_STATE_SELECTED].green / 65535.0,
1678
                          widget->style->dark[GTK_STATE_SELECTED].blue / 65535.0);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1679
1680
    cairo_stroke (cr);
1681
}
1682
1683
static void
1684
paint_output (App *app, cairo_t *cr, int i)
1685
{
1686
    int w, h;
1687
    double scale = compute_scale (app);
1688
    double x, y;
1689
    int total_w, total_h;
68 by Michael Vogt
* move packaging into bzr
1690
    GList *connected_outputs = list_connected_outputs (app, &total_w, &total_h);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1691
    GnomeOutputInfo *output = g_list_nth (connected_outputs, i)->data;
1692
    PangoLayout *layout = get_display_name (app, output);
1.1.31 by Chris Coulson
Import upstream version 2.27.91
1693
    PangoRectangle ink_extent, log_extent;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1694
    GdkRectangle viewport;
68 by Michael Vogt
* move packaging into bzr
1695
    GdkColor output_color;
1696
    double r, g, b;
1.1.31 by Chris Coulson
Import upstream version 2.27.91
1697
    double available_w;
1698
    double factor;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1699
1700
    cairo_save (cr);
68 by Michael Vogt
* move packaging into bzr
1701
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1702
    foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area), &viewport);
68 by Michael Vogt
* move packaging into bzr
1703
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1704
    get_geometry (output, &w, &h);
68 by Michael Vogt
* move packaging into bzr
1705
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1706
#if 0
68 by Michael Vogt
* move packaging into bzr
1707
    g_debug ("%s (%p) geometry %d %d %d", output->name, output,
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1708
	     w, h, output->rate);
1709
#endif
1710
1711
    viewport.height -= 2 * MARGIN;
1712
    viewport.width -= 2 * MARGIN;
68 by Michael Vogt
* move packaging into bzr
1713
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1714
    x = output->x * scale + MARGIN + (viewport.width - total_w * scale) / 2.0;
1715
    y = output->y * scale + MARGIN + (viewport.height - total_h * scale) / 2.0;
1716
1717
#if 0
68 by Michael Vogt
* move packaging into bzr
1718
    g_debug ("scaled: %f %f", x, y);
1719
1720
    g_debug ("scale: %f", scale);
1721
1722
    g_debug ("%f %f %f %f", x, y, w * scale + 0.5, h * scale + 0.5);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1723
#endif
1724
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1725
    cairo_save (cr);
1726
1727
    cairo_translate (cr,
1728
		     x + (w * scale + 0.5) / 2,
1729
		     y + (h * scale + 0.5) / 2);
68 by Michael Vogt
* move packaging into bzr
1730
1.1.31 by Chris Coulson
Import upstream version 2.27.91
1731
    /* rotation is already applied in get_geometry */
68 by Michael Vogt
* move packaging into bzr
1732
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1733
    if (output->rotation & GNOME_RR_REFLECT_X)
1734
	cairo_scale (cr, -1, 1);
68 by Michael Vogt
* move packaging into bzr
1735
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1736
    if (output->rotation & GNOME_RR_REFLECT_Y)
1737
	cairo_scale (cr, 1, -1);
68 by Michael Vogt
* move packaging into bzr
1738
1739
    cairo_translate (cr,
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1740
		     - x - (w * scale + 0.5) / 2,
1741
		     - y - (h * scale + 0.5) / 2);
68 by Michael Vogt
* move packaging into bzr
1742
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1743
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1744
    cairo_rectangle (cr, x, y, w * scale + 0.5, h * scale + 0.5);
1745
    cairo_clip_preserve (cr);
68 by Michael Vogt
* move packaging into bzr
1746
1747
    gnome_rr_labeler_get_color_for_output (app->labeler, output, &output_color);
1748
    r = output_color.red / 65535.0;
1749
    g = output_color.green / 65535.0;
1750
    b = output_color.blue / 65535.0;
1751
1752
    if (!output->on)
1753
    {
1754
	/* If the output is turned off, just darken the selected color */
1755
	r *= 0.2;
1756
	g *= 0.2;
1757
	b *= 0.2;
1758
    }
1759
1760
    cairo_set_source_rgba (cr, r, g, b, 1.0);
1761
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1762
    foo_scroll_area_add_input_from_fill (FOO_SCROLL_AREA (app->area),
1763
					 cr, on_output_event, output);
1764
    cairo_fill (cr);
68 by Michael Vogt
* move packaging into bzr
1765
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1766
    if (output == app->current_output)
1767
    {
1768
	cairo_rectangle (cr, x + 2, y + 2, w * scale + 0.5 - 4, h * scale + 0.5 - 4);
68 by Michael Vogt
* move packaging into bzr
1769
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1770
	cairo_set_line_width (cr, 4);
1771
	cairo_set_source_rgba (cr, 0.33, 0.43, 0.57, 1.0);
1772
	cairo_stroke (cr);
1773
    }
68 by Michael Vogt
* move packaging into bzr
1774
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1775
    cairo_rectangle (cr, x + 0.5, y + 0.5, w * scale + 0.5 - 1, h * scale + 0.5 - 1);
68 by Michael Vogt
* move packaging into bzr
1776
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1777
    cairo_set_line_width (cr, 1);
1778
    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
1779
1780
    cairo_stroke (cr);
1781
    cairo_set_line_width (cr, 2);
1782
1783
    layout_set_font (layout, "Sans Bold 12");
1.1.31 by Chris Coulson
Import upstream version 2.27.91
1784
    pango_layout_get_pixel_extents (layout, &ink_extent, &log_extent);
1785
1786
    available_w = w * scale + 0.5 - 6; /* Same as the inner rectangle's width, minus 1 pixel of padding on each side */
1787
    if (available_w < ink_extent.width)
1788
	factor = available_w / ink_extent.width;
1789
    else
1790
	factor = 1.0;
1791
1792
    cairo_move_to (cr,
1793
		   x + ((w * scale + 0.5) - factor * log_extent.width) / 2,
1794
		   y + ((h * scale + 0.5) - factor * log_extent.height) / 2);
1795
1796
    cairo_scale (cr, factor, factor);
68 by Michael Vogt
* move packaging into bzr
1797
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1798
    if (output->on)
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
1799
	cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1800
    else
1801
	cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
68 by Michael Vogt
* move packaging into bzr
1802
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1803
    pango_cairo_show_layout (cr, layout);
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
1804
1805
    cairo_restore (cr);
68 by Michael Vogt
* move packaging into bzr
1806
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1807
    g_object_unref (layout);
1808
}
1809
1810
static void
1811
on_area_paint (FooScrollArea *area,
1812
	       cairo_t	     *cr,
1813
	       GdkRectangle  *extent,
1814
	       GdkRegion     *region,
1815
	       gpointer	      data)
1816
{
1817
    App *app = data;
1818
    double scale;
1819
    GList *connected_outputs = NULL;
1820
    GList *list;
1821
1822
    paint_background (area, cr);
68 by Michael Vogt
* move packaging into bzr
1823
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1824
    if (!app->current_configuration)
1825
	return;
68 by Michael Vogt
* move packaging into bzr
1826
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1827
    scale = compute_scale (app);
1828
    connected_outputs = list_connected_outputs (app, NULL, NULL);
68 by Michael Vogt
* move packaging into bzr
1829
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1830
#if 0
68 by Michael Vogt
* move packaging into bzr
1831
    g_debug ("scale: %f", scale);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1832
#endif
68 by Michael Vogt
* move packaging into bzr
1833
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1834
    for (list = connected_outputs; list != NULL; list = list->next)
1835
    {
1836
	paint_output (app, cr, g_list_position (connected_outputs, list));
1837
1838
	if (app->current_configuration->clone)
1839
	    break;
1840
    }
1841
}
1842
1843
static void
1844
make_text_combo (GtkWidget *widget, int sort_column)
1845
{
1846
    GtkComboBox *box = GTK_COMBO_BOX (widget);
1847
    GtkListStore *store = gtk_list_store_new (
1848
	6,
1849
	G_TYPE_STRING,		/* Text */
68 by Michael Vogt
* move packaging into bzr
1850
	G_TYPE_INT,		/* Width */
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1851
	G_TYPE_INT,		/* Height */
1852
	G_TYPE_INT,		/* Frequency */
1853
	G_TYPE_INT,		/* Width * Height */
1854
	G_TYPE_INT);		/* Rotation */
68 by Michael Vogt
* move packaging into bzr
1855
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1856
    GtkCellRenderer *cell;
1857
1858
    gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget));
68 by Michael Vogt
* move packaging into bzr
1859
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1860
    gtk_combo_box_set_model (box, GTK_TREE_MODEL (store));
1861
1862
    cell = gtk_cell_renderer_text_new ();
1863
    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, TRUE);
1864
    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
1865
				    "text", 0,
1866
				    NULL);
1867
1868
    if (sort_column != -1)
1869
    {
1870
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1871
					      sort_column,
1872
					      GTK_SORT_DESCENDING);
1873
    }
1874
}
1875
1876
static void
1877
compute_virtual_size_for_configuration (GnomeRRConfig *config, int *ret_width, int *ret_height)
1878
{
1879
    int i;
1880
    int width, height;
1881
1882
    width = height = 0;
1883
1884
    for (i = 0; config->outputs[i] != NULL; i++)
1885
    {
1886
	GnomeOutputInfo *output;
1887
1888
	output = config->outputs[i];
1889
1890
	if (output->on)
1891
	{
1892
	    width = MAX (width, output->x + output->width);
1893
	    height = MAX (height, output->y + output->height);
1894
	}
1895
    }
1896
1897
    *ret_width = width;
1898
    *ret_height = height;
1899
}
1900
1901
static void
1902
check_required_virtual_size (App *app)
1903
{
1904
    int req_width, req_height;
1905
    int min_width, max_width;
1906
    int min_height, max_height;
1907
1908
    compute_virtual_size_for_configuration (app->current_configuration, &req_width, &req_height);
1909
1910
    gnome_rr_screen_get_ranges (app->screen, &min_width, &max_width, &min_height, &max_height);
1911
1912
#if 0
68 by Michael Vogt
* move packaging into bzr
1913
    g_debug ("X Server supports:");
1914
    g_debug ("min_width = %d, max_width = %d", min_width, max_width);
1915
    g_debug ("min_height = %d, max_height = %d", min_height, max_height);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1916
68 by Michael Vogt
* move packaging into bzr
1917
    g_debug ("Requesting size of %dx%d", req_width, req_height);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1918
#endif
1919
1920
    if (!(min_width <= req_width && req_width <= max_width
1921
	  && min_height <= req_height && req_height <= max_height))
1922
    {
1923
	/* FIXME: present a useful dialog, maybe even before the user tries to Apply */
1924
#if 0
68 by Michael Vogt
* move packaging into bzr
1925
	g_debug ("Your X server needs a larger Virtual size!");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
1926
#endif
1927
    }
1928
}
1929
1.1.28 by Chris Coulson
Import upstream version 2.27.3
1930
static void
1931
begin_version2_apply_configuration (App *app, GdkWindow *parent_window, guint32 timestamp)
1932
{
1933
    XID parent_window_xid;
1934
1935
    parent_window_xid = GDK_WINDOW_XID (parent_window);
1936
1937
    app->proxy = dbus_g_proxy_new_for_name (app->connection,
1938
					    "org.gnome.SettingsDaemon",
1939
					    "/org/gnome/SettingsDaemon/XRANDR",
1940
					    "org.gnome.SettingsDaemon.XRANDR_2");
1941
    g_assert (app->proxy != NULL); /* that call does not fail unless we pass bogus names */
1942
1943
    app->apply_configuration_state = APPLYING_VERSION_2;
1944
    app->proxy_call = dbus_g_proxy_begin_call (app->proxy, "ApplyConfiguration",
1945
					       apply_configuration_returned_cb, app,
1946
					       NULL,
1947
					       G_TYPE_INT64, (gint64) parent_window_xid,
1948
					       G_TYPE_INT64, (gint64) timestamp,
1949
					       G_TYPE_INVALID,
1950
					       G_TYPE_INVALID);
1951
    /* FIXME: we don't check for app->proxy_call == NULL, which could happen if
1952
     * the connection was disconnected.  This is left as an exercise for the
1953
     * reader.
1954
     */
1955
}
1956
1957
static void
1958
begin_version1_apply_configuration (App *app)
1959
{
1960
    app->proxy = dbus_g_proxy_new_for_name (app->connection,
1961
					    "org.gnome.SettingsDaemon",
1962
					    "/org/gnome/SettingsDaemon/XRANDR",
1963
					    "org.gnome.SettingsDaemon.XRANDR");
1964
    g_assert (app->proxy != NULL); /* that call does not fail unless we pass bogus names */
1965
1966
    app->apply_configuration_state = APPLYING_VERSION_1;
1967
    app->proxy_call = dbus_g_proxy_begin_call (app->proxy, "ApplyConfiguration",
1968
					       apply_configuration_returned_cb, app,
1969
					       NULL,
1970
					       G_TYPE_INVALID,
1971
					       G_TYPE_INVALID);
1972
    /* FIXME: we don't check for app->proxy_call == NULL, which could happen if
1973
     * the connection was disconnected.  This is left as an exercise for the
1974
     * reader.
1975
     */
1976
}
1977
1978
static void
1979
ensure_current_configuration_is_saved (void)
1980
{
1981
        GnomeRRScreen *rr_screen;
1982
        GnomeRRConfig *rr_config;
1983
1984
        /* Normally, gnome_rr_config_save() creates a backup file based on the
1985
         * old monitors.xml.  However, if *that* file didn't exist, there is
1986
         * nothing from which to create a backup.  So, here we'll save the
1987
         * current/unchanged configuration and then let our caller call
1988
         * gnome_rr_config_save() again with the new/changed configuration, so
1989
         * that there *will* be a backup file in the end.
1990
         */
1991
1992
        rr_screen = gnome_rr_screen_new (gdk_screen_get_default (), NULL, NULL, NULL); /* NULL-GError */
1993
        if (!rr_screen)
1994
                return;
1995
1996
        rr_config = gnome_rr_config_new_current (rr_screen);
1997
        gnome_rr_config_save (rr_config, NULL); /* NULL-GError */
1998
1999
        gnome_rr_config_free (rr_config);
2000
        gnome_rr_screen_destroy (rr_screen);
2001
}
2002
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
2003
/* Callback for dbus_g_proxy_begin_call() */
2004
static void
2005
apply_configuration_returned_cb (DBusGProxy       *proxy,
2006
				 DBusGProxyCall   *call_id,
2007
				 void             *data)
2008
{
2009
    App *app = data;
2010
    gboolean success;
2011
    GError *error;
2012
2013
    g_assert (call_id == app->proxy_call);
2014
2015
    error = NULL;
2016
    success = dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID);
2017
2018
    if (!success) {
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2019
	if (app->apply_configuration_state == APPLYING_VERSION_2
2020
	    && g_error_matches (error, DBUS_GERROR, DBUS_GERROR_UNKNOWN_METHOD)) {
2021
	    g_error_free (error);
2022
2023
	    g_object_unref (app->proxy);
2024
	    app->proxy = NULL;
2025
2026
	    begin_version1_apply_configuration (app);
2027
	    return;
2028
	} else {
2029
	    /* We don't pop up an error message; gnome-settings-daemon already does that
2030
	     * in case the selected RANDR configuration could not be applied.
2031
	     */
2032
	    g_error_free (error);
2033
	}
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
2034
    }
2035
2036
    g_object_unref (app->proxy);
2037
    app->proxy = NULL;
2038
2039
    dbus_g_connection_unref (app->connection);
2040
    app->connection = NULL;
2041
    app->proxy_call = NULL;
2042
2043
    gtk_widget_set_sensitive (app->dialog, TRUE);
2044
}
2045
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2046
static void
2047
apply (App *app)
2048
{
1.1.24 by Didier Roche
Import upstream version 2.25.3
2049
    GError *error = NULL;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2050
2051
    gnome_rr_config_sanitize (app->current_configuration);
2052
2053
    check_required_virtual_size (app);
2054
2055
    foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
68 by Michael Vogt
* move packaging into bzr
2056
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2057
    ensure_current_configuration_is_saved ();
2058
1.1.24 by Didier Roche
Import upstream version 2.25.3
2059
    if (!gnome_rr_config_save (app->current_configuration, &error))
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2060
    {
1.1.24 by Didier Roche
Import upstream version 2.25.3
2061
	error_message (app, _("Could not save the monitor configuration"), error->message);
2062
	g_error_free (error);
2063
	return;
2064
    }
2065
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
2066
    g_assert (app->connection == NULL);
2067
    g_assert (app->proxy == NULL);
2068
    g_assert (app->proxy_call == NULL);
2069
2070
    app->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
2071
    if (app->connection == NULL) {
1.1.24 by Didier Roche
Import upstream version 2.25.3
2072
	error_message (app, _("Could not get session bus while applying display configuration"), error->message);
2073
	g_error_free (error);
2074
	return;
2075
    }
2076
1.1.26 by Sebastien Bacher
Import upstream version 2.25.92
2077
    gtk_widget_set_sensitive (app->dialog, FALSE);
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2078
2079
    begin_version2_apply_configuration (app, gtk_widget_get_window (app->dialog), app->apply_button_clicked_timestamp);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2080
}
2081
1.1.23 by Michael Vogt
Import upstream version 2.25.2
2082
#if 0
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2083
/* Returns whether the graphics driver doesn't advertise RANDR 1.2 features, and just 1.0 */
2084
static gboolean
2085
driver_is_randr_10 (GnomeRRConfig *config)
2086
{
2087
    /* In the Xorg code, see xserver/randr/rrinfo.c:RRScanOldConfig().  It gets
2088
     * called when the graphics driver doesn't support RANDR 1.2 yet, just 1.0.
2089
     * In that case, the X server's base code (which supports RANDR 1.2) will
2090
     * simulate having a single output called "default".  For drivers that *do*
2091
     * support RANDR 1.2, the separate outputs will be named differently, we
2092
     * hope.
2093
     *
2094
     * This heuristic is courtesy of Dirk Mueller <dmueller@suse.de>
2095
     *
2096
     * FIXME: however, we don't even check for XRRQueryVersion() returning 1.2, neither
2097
     * here nor in gnome-desktop/libgnomedesktop*.c.  Do we need to check for that,
2098
     * or is gnome_rr_screen_new()'s return value sufficient?
2099
     */
2100
2101
    return (count_all_outputs (config) == 1 && strcmp (config->outputs[0]->name, "default") == 0);
2102
}
1.1.23 by Michael Vogt
Import upstream version 2.25.2
2103
#endif
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2104
2105
static void
2106
on_detect_displays (GtkWidget *widget, gpointer data)
2107
{
2108
    App *app = data;
1.1.24 by Didier Roche
Import upstream version 2.25.3
2109
    GError *error;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2110
1.1.24 by Didier Roche
Import upstream version 2.25.3
2111
    error = NULL;
2112
    if (!gnome_rr_screen_refresh (app->screen, &error)) {
2113
	if (error) {
2114
	    error_message (app, _("Could not detect displays"), error->message);
2115
	    g_error_free (error);
2116
	}
2117
    }
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2118
}
2119
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2120
#define SHOW_ICON_KEY "/apps/gnome_settings_daemon/xrandr/show_notification_icon"
2121
2122
2123
static void
2124
on_show_icon_toggled (GtkWidget *widget, gpointer data)
2125
{
2126
    GtkToggleButton *tb = GTK_TOGGLE_BUTTON (widget);
2127
    App *app = data;
68 by Michael Vogt
* move packaging into bzr
2128
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2129
    gconf_client_set_bool (app->client, SHOW_ICON_KEY,
2130
			   gtk_toggle_button_get_active (tb), NULL);
2131
}
2132
1.1.24 by Didier Roche
Import upstream version 2.25.3
2133
static GnomeOutputInfo *
2134
get_nearest_output (GnomeRRConfig *configuration, int x, int y)
2135
{
2136
    int i;
2137
    int nearest_index;
2138
    int nearest_dist;
2139
2140
    nearest_index = -1;
2141
    nearest_dist = G_MAXINT;
2142
2143
    for (i = 0; configuration->outputs[i] != NULL; i++)
2144
    {
2145
	GnomeOutputInfo *output;
2146
	int dist_x, dist_y;
2147
2148
	output = configuration->outputs[i];
2149
2150
	if (!(output->connected && output->on))
2151
	    continue;
2152
2153
	if (x < output->x)
2154
	    dist_x = output->x - x;
2155
	else if (x >= output->x + output->width)
2156
	    dist_x = x - (output->x + output->width) + 1;
2157
	else
2158
	    dist_x = 0;
2159
2160
	if (y < output->y)
2161
	    dist_y = output->y - y;
2162
	else if (y >= output->y + output->height)
2163
	    dist_y = y - (output->y + output->height) + 1;
2164
	else
2165
	    dist_y = 0;
2166
2167
	if (MIN (dist_x, dist_y) < nearest_dist)
2168
	{
2169
	    nearest_dist = MIN (dist_x, dist_y);
2170
	    nearest_index = i;
2171
	}
2172
    }
2173
2174
    if (nearest_index != -1)
2175
	return configuration->outputs[nearest_index];
2176
    else
2177
	return NULL;
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
2178
1.1.24 by Didier Roche
Import upstream version 2.25.3
2179
}
2180
2181
/* Gets the output that contains the largest intersection with the window.
2182
 * Logic stolen from gdk_screen_get_monitor_at_window().
2183
 */
2184
static GnomeOutputInfo *
2185
get_output_for_window (GnomeRRConfig *configuration, GdkWindow *window)
2186
{
2187
    GdkRectangle win_rect;
2188
    int i;
2189
    int largest_area;
2190
    int largest_index;
2191
2192
    gdk_window_get_geometry (window, &win_rect.x, &win_rect.y, &win_rect.width, &win_rect.height, NULL);
2193
    gdk_window_get_origin (window, &win_rect.x, &win_rect.y);
2194
2195
    largest_area = 0;
2196
    largest_index = -1;
2197
2198
    for (i = 0; configuration->outputs[i] != NULL; i++)
2199
    {
2200
	GnomeOutputInfo *output;
2201
	GdkRectangle output_rect, intersection;
2202
2203
	output = configuration->outputs[i];
2204
2205
	output_rect.x	   = output->x;
2206
	output_rect.y	   = output->y;
2207
	output_rect.width  = output->width;
2208
	output_rect.height = output->height;
2209
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
2210
	if (output->connected && gdk_rectangle_intersect (&win_rect, &output_rect, &intersection))
1.1.24 by Didier Roche
Import upstream version 2.25.3
2211
	{
2212
	    int area;
2213
2214
	    area = intersection.width * intersection.height;
2215
	    if (area > largest_area)
2216
	    {
2217
		largest_area = area;
2218
		largest_index = i;
2219
	    }
2220
	}
2221
    }
2222
2223
    if (largest_index != -1)
2224
	return configuration->outputs[largest_index];
2225
    else
2226
	return get_nearest_output (configuration,
2227
				   win_rect.x + win_rect.width / 2,
2228
				   win_rect.y + win_rect.height / 2);
2229
}
2230
2231
/* We select the current output, i.e. select the one being edited, based on
2232
 * which output is showing the configuration dialog.
2233
 */
2234
static void
2235
select_current_output_from_dialog_position (App *app)
2236
{
2237
    if (GTK_WIDGET_REALIZED (app->dialog))
1.1.27 by Didier Roche
Import upstream version 2.26.0
2238
	app->current_output = get_output_for_window (app->current_configuration, app->dialog->window);
2239
    else
2240
	app->current_output = NULL;
1.1.24 by Didier Roche
Import upstream version 2.25.3
2241
2242
    rebuild_gui (app);
2243
}
2244
2245
/* This is a GtkWidget::map-event handler.  We wait for the display-properties
2246
 * dialog to be mapped, and then we select the output which corresponds to the
2247
 * monitor on which the dialog is being shown.
2248
 */
2249
static gboolean
2250
dialog_map_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
2251
{
2252
    App *app = data;
2253
2254
    select_current_output_from_dialog_position (app);
2255
    return FALSE;
2256
}
2257
2258
static void
2259
hide_help_button (App *app)
2260
{
2261
    GtkWidget *action_area;
2262
    GList *children;
2263
    GList *l;
2264
2265
    action_area = gtk_dialog_get_action_area (GTK_DIALOG (app->dialog));
2266
    children = gtk_container_get_children (GTK_CONTAINER (action_area));
2267
2268
    for (l = children; l; l = l->next)
2269
    {
2270
	GtkWidget *child;
2271
	int response;
2272
2273
	child = GTK_WIDGET (l->data);
2274
2275
	response = gtk_dialog_get_response_for_widget (GTK_DIALOG (app->dialog), child);
2276
	if (response == GTK_RESPONSE_HELP)
2277
	{
2278
	    gtk_widget_hide (child);
2279
	    return;
2280
	}
2281
    }
2282
}
2283
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2284
static void
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2285
apply_button_clicked_cb (GtkButton *button, gpointer data)
2286
{
2287
    App *app = data;
2288
2289
    /* We simply store the timestamp at which the Apply button was clicked.
2290
     * We'll just wait for the dialog to return from gtk_dialog_run(), and
2291
     * *then* use the timestamp when applying the RANDR configuration.
2292
     */
2293
2294
    app->apply_button_clicked_timestamp = gtk_get_current_event_time ();
2295
}
2296
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2297
static GtkWidget*
2298
_gtk_builder_get_widget (GtkBuilder *builder, const gchar *name)
2299
{
2300
    return GTK_WIDGET (gtk_builder_get_object (builder, name));
2301
}
2302
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2303
static void
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2304
run_application (App *app)
2305
{
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2306
#ifndef UIDIR
2307
#define UIDIR "."
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2308
#endif
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2309
#define UI_FILE UIDIR "/display-capplet.ui"
2310
    GtkBuilder *builder;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2311
    GtkWidget *align;
1.1.24 by Didier Roche
Import upstream version 2.25.3
2312
    GError *error;
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2313
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2314
    error = NULL;
2315
    builder = gtk_builder_new ();
2316
2317
    if (gtk_builder_add_from_file (builder, UI_FILE, &error) == 0)
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2318
    {
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2319
	g_warning ("Could not parse UI definition: %s", error->message);
2320
	g_error_free (error);
2321
	g_object_unref (builder);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2322
	return;
2323
    }
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2324
68 by Michael Vogt
* move packaging into bzr
2325
    app->screen = gnome_rr_screen_new (gdk_screen_get_default (),
1.1.24 by Didier Roche
Import upstream version 2.25.3
2326
				       on_screen_changed, app, &error);
68 by Michael Vogt
* move packaging into bzr
2327
    if (!app->screen)
2328
    {
1.1.24 by Didier Roche
Import upstream version 2.25.3
2329
	error_message (NULL, _("Could not get screen information"), error->message);
2330
	g_error_free (error);
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2331
	g_object_unref (builder);
68 by Michael Vogt
* move packaging into bzr
2332
	return;
2333
    }
2334
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2335
    app->client = gconf_client_get_default ();
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2336
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2337
    app->dialog = _gtk_builder_get_widget (builder, "dialog");
1.1.24 by Didier Roche
Import upstream version 2.25.3
2338
    g_signal_connect_after (app->dialog, "map-event",
2339
			    G_CALLBACK (dialog_map_event_cb), app);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2340
2341
    gtk_window_set_default_icon_name ("gnome-display-properties");
2342
    gtk_window_set_icon_name (GTK_WINDOW (app->dialog),
2343
			      "gnome-display-properties");
2344
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2345
    app->current_monitor_event_box = _gtk_builder_get_widget (builder,
2346
    						   "current_monitor_event_box");
2347
    app->current_monitor_label = _gtk_builder_get_widget (builder,
2348
    						       "current_monitor_label");
1.1.24 by Didier Roche
Import upstream version 2.25.3
2349
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2350
    app->monitor_on_radio = _gtk_builder_get_widget (builder,
2351
    						     "monitor_on_radio");
2352
    app->monitor_off_radio = _gtk_builder_get_widget (builder,
2353
    						      "monitor_off_radio");
1.1.25 by Sebastien Bacher
Import upstream version 2.25.90
2354
    g_signal_connect (app->monitor_on_radio, "toggled",
2355
		      G_CALLBACK (monitor_on_off_toggled_cb), app);
2356
    g_signal_connect (app->monitor_off_radio, "toggled",
2357
		      G_CALLBACK (monitor_on_off_toggled_cb), app);
2358
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2359
    app->resolution_combo = _gtk_builder_get_widget (builder,
2360
    						     "resolution_combo");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2361
    g_signal_connect (app->resolution_combo, "changed",
2362
		      G_CALLBACK (on_resolution_changed), app);
2363
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2364
    app->refresh_combo = _gtk_builder_get_widget (builder, "refresh_combo");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2365
    g_signal_connect (app->refresh_combo, "changed",
2366
		      G_CALLBACK (on_rate_changed), app);
2367
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2368
    app->rotation_combo = _gtk_builder_get_widget (builder, "rotation_combo");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2369
    g_signal_connect (app->rotation_combo, "changed",
2370
		      G_CALLBACK (on_rotation_changed), app);
2371
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2372
    app->clone_checkbox = _gtk_builder_get_widget (builder, "clone_checkbox");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2373
    g_signal_connect (app->clone_checkbox, "toggled",
2374
		      G_CALLBACK (on_clone_changed), app);
2375
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2376
    g_signal_connect (_gtk_builder_get_widget (builder, "detect_displays_button"),
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2377
		      "clicked", G_CALLBACK (on_detect_displays), app);
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2378
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2379
    app->show_icon_checkbox = _gtk_builder_get_widget (builder,
2380
						      "show_notification_icon");
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2381
2382
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app->show_icon_checkbox),
2383
				  gconf_client_get_bool (app->client, SHOW_ICON_KEY, NULL));
68 by Michael Vogt
* move packaging into bzr
2384
1.1.19 by Sebastien Bacher
Import upstream version 2.23.6
2385
    g_signal_connect (app->show_icon_checkbox, "toggled", G_CALLBACK (on_show_icon_toggled), app);
68 by Michael Vogt
* move packaging into bzr
2386
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2387
    app->panel_checkbox = _gtk_builder_get_widget (builder, "panel_checkbox");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2388
2389
    make_text_combo (app->resolution_combo, 4);
2390
    make_text_combo (app->refresh_combo, 3);
2391
    make_text_combo (app->rotation_combo, -1);
68 by Michael Vogt
* move packaging into bzr
2392
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2393
    g_assert (app->panel_checkbox);
68 by Michael Vogt
* move packaging into bzr
2394
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2395
    /* Scroll Area */
2396
    app->area = (GtkWidget *)foo_scroll_area_new ();
2397
2398
    g_object_set_data (G_OBJECT (app->area), "app", app);
68 by Michael Vogt
* move packaging into bzr
2399
1.1.39 by Didier Roche
Import upstream version 2.30.0
2400
    set_monitors_tooltip (app, FALSE);
1.1.37 by Chris Coulson
Import upstream version 2.29.91
2401
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2402
    /* FIXME: this should be computed dynamically */
2403
    foo_scroll_area_set_min_size (FOO_SCROLL_AREA (app->area), -1, 200);
2404
    gtk_widget_show (app->area);
2405
    g_signal_connect (app->area, "paint",
2406
		      G_CALLBACK (on_area_paint), app);
2407
    g_signal_connect (app->area, "viewport_changed",
2408
		      G_CALLBACK (on_viewport_changed), app);
2409
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2410
    align = _gtk_builder_get_widget (builder, "align");
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2411
2412
    gtk_container_add (GTK_CONTAINER (align), app->area);
2413
1.1.24 by Didier Roche
Import upstream version 2.25.3
2414
    /* Until we have help to show, we'll just hide the Help button */
2415
    hide_help_button (app);
2416
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2417
    app->apply_button = _gtk_builder_get_widget (builder, "apply_button");
1.1.28 by Chris Coulson
Import upstream version 2.27.3
2418
    g_signal_connect (app->apply_button, "clicked",
2419
		      G_CALLBACK (apply_button_clicked_cb), app);
2420
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2421
    on_screen_changed (app->screen, app);
2422
1.1.29 by Chris Coulson
Import upstream version 2.27.4.1
2423
    g_object_unref (builder);
68 by Michael Vogt
* move packaging into bzr
2424
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2425
restart:
2426
    switch (gtk_dialog_run (GTK_DIALOG (app->dialog)))
2427
    {
2428
    default:
2429
	/* Fall Through */
2430
    case GTK_RESPONSE_DELETE_EVENT:
2431
    case GTK_RESPONSE_CLOSE:
68 by Michael Vogt
* move packaging into bzr
2432
#if 0
2433
	g_debug ("Close");
2434
#endif
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2435
	break;
2436
2437
    case GTK_RESPONSE_HELP:
68 by Michael Vogt
* move packaging into bzr
2438
#if 0
2439
	g_debug ("Help");
2440
#endif
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2441
	goto restart;
2442
	break;
2443
2444
    case GTK_RESPONSE_APPLY:
2445
	apply (app);
2446
	goto restart;
2447
	break;
2448
    }
2449
2450
    gtk_widget_destroy (app->dialog);
68 by Michael Vogt
* move packaging into bzr
2451
    gnome_rr_screen_destroy (app->screen);
2452
    g_object_unref (app->client);
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2453
}
2454
2455
int
2456
main (int argc, char **argv)
2457
{
2458
    App *app;
68 by Michael Vogt
* move packaging into bzr
2459
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2460
    bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
2461
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2462
    textdomain (GETTEXT_PACKAGE);
68 by Michael Vogt
* move packaging into bzr
2463
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2464
    gtk_init (&argc, &argv);
2465
2466
    app = g_new0 (App, 1);
2467
2468
    run_application (app);
68 by Michael Vogt
* move packaging into bzr
2469
2470
    g_free (app);
2471
1.1.18 by Sebastien Bacher
Import upstream version 2.23.5
2472
    return 0;
2473
}