gedit-utils.c 32.8 KB
Newer Older
1 2 3 4 5
/*
 * gedit-utils.c
 * This file is part of gedit
 *
 * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
6 7
 * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi
 * Copyright (C) 2003-2005 Paolo Maggi
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21
 */
22

23 24
#include "gedit-utils.h"

25
#include <string.h>
Paolo Borelli's avatar
Paolo Borelli committed
26 27
#include <glib/gi18n.h>

28 29 30 31 32 33
/* For the workspace/viewport stuff */
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#endif

34 35
#include "gedit-debug.h"

Paolo Borelli's avatar
Paolo Borelli committed
36
static void
Garrett Regier's avatar
Garrett Regier committed
37 38 39
widget_get_origin (GtkWidget *widget,
		   gint      *x,
		   gint      *y)
Paolo Borelli's avatar
Paolo Borelli committed
40 41 42 43 44 45 46 47

{
	GdkWindow *window;

	window = gtk_widget_get_window (widget);
	gdk_window_get_origin (window, x, y);
}

Paolo Borelli's avatar
Paolo Borelli committed
48 49 50 51 52 53 54
void
gedit_utils_menu_position_under_widget (GtkMenu  *menu,
					gint     *x,
					gint     *y,
					gboolean *push_in,
					gpointer  user_data)
{
Paolo Borelli's avatar
Paolo Borelli committed
55
	GtkWidget *widget;
Paolo Borelli's avatar
Paolo Borelli committed
56
	GtkRequisition requisition;
57
	GtkAllocation allocation;
Paolo Borelli's avatar
Paolo Borelli committed
58

Paolo Borelli's avatar
Paolo Borelli committed
59 60 61
	widget = GTK_WIDGET (user_data);
	widget_get_origin (widget, x, y);

62 63
	gtk_widget_get_preferred_size (GTK_WIDGET (menu), &requisition,
	                               NULL);
64

65 66
	gtk_widget_get_allocation (widget, &allocation);

Paolo Borelli's avatar
Paolo Borelli committed
67
	if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
Paolo Borelli's avatar
Paolo Borelli committed
68
	{
69
		*x += allocation.x + allocation.width - requisition.width;
Paolo Borelli's avatar
Paolo Borelli committed
70 71 72
	}
	else
	{
73
		*x += allocation.x;
74
	}
75

76
	*y += allocation.y + allocation.height;
77

Paolo Borelli's avatar
Paolo Borelli committed
78
	*push_in = TRUE;
79 80
}

81 82 83
gboolean
gedit_utils_menu_position_under_tree_view (GtkTreeView  *tree_view,
					   GdkRectangle *rect)
84 85
{
	GtkTreeSelection *selection;
86 87 88 89
	GtkTreeModel *model;
	gint count_rows;
	GList *rows;
	gint widget_x, widget_y;
90

91 92
	model = gtk_tree_view_get_model (tree_view);
	g_return_val_if_fail (model != NULL, FALSE);
93

94 95
	selection = gtk_tree_view_get_selection (tree_view);
	g_return_val_if_fail (selection != NULL, FALSE);
96

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
	count_rows = gtk_tree_selection_count_selected_rows (selection);
	if (count_rows != 1)
		return FALSE;

	rows = gtk_tree_selection_get_selected_rows (selection, &model);
	gtk_tree_view_get_cell_area (tree_view, (GtkTreePath *)(rows->data),
				     gtk_tree_view_get_column (tree_view, 0),
				     rect);

	gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, rect->x, rect->y, &widget_x, &widget_y);
	rect->x = widget_x;
	rect->y = widget_y;

	g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
	return TRUE;
112 113
}

114
/**
115 116 117 118 119 120
 * gedit_utils_set_atk_name_description:
 * @widget: The Gtk widget for which name/description to be set
 * @name: Atk name string
 * @description: Atk description string
 *
 * This function sets up name and description
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
121 122 123
 * for a specified gtk widget.
 */
void
124
gedit_utils_set_atk_name_description (GtkWidget   *widget,
125 126
				      const gchar *name,
				      const gchar *description)
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
127 128 129 130 131 132 133 134
{
	AtkObject *aobj;

	aobj = gtk_widget_get_accessible (widget);

	if (!(GTK_IS_ACCESSIBLE (aobj)))
		return;

Garrett Regier's avatar
Garrett Regier committed
135
	if (name)
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
136 137
		atk_object_set_name (aobj, name);

Garrett Regier's avatar
Garrett Regier committed
138
	if (description)
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
139 140
		atk_object_set_description (aobj, description);
}
141 142

/**
143 144 145 146 147 148
 * gedit_set_atk_relation:
 * @obj1: specified widget.
 * @obj2: specified widget.
 * @rel_type: the type of relation to set up.
 *
 * This function establishes atk relation
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
149 150 151
 * between 2 specified widgets.
 */
void
152 153
gedit_utils_set_atk_relation (GtkWidget       *obj1,
			      GtkWidget       *obj2,
Garrett Regier's avatar
Garrett Regier committed
154
			      AtkRelationType  rel_type)
Hema Seetharamaiah's avatar
Hema Seetharamaiah committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
{
	AtkObject *atk_obj1, *atk_obj2;
	AtkRelationSet *relation_set;
	AtkObject *targets[1];
	AtkRelation *relation;

	atk_obj1 = gtk_widget_get_accessible (obj1);
	atk_obj2 = gtk_widget_get_accessible (obj2);

	if (!(GTK_IS_ACCESSIBLE (atk_obj1)) || !(GTK_IS_ACCESSIBLE (atk_obj2)))
		return;

	relation_set = atk_object_ref_relation_set (atk_obj1);
	targets[0] = atk_obj2;

	relation = atk_relation_new (targets, 1, rel_type);
	atk_relation_set_add (relation_set, relation);

	g_object_unref (G_OBJECT (relation));
}
Paolo Maggi's avatar
Paolo Maggi committed
175

176
void
Paolo Borelli's avatar
Paolo Borelli committed
177
gedit_warning (GtkWindow *parent, const gchar *format, ...)
178
{
179 180 181 182
	va_list         args;
	gchar          *str;
	GtkWidget      *dialog;
	GtkWindowGroup *wg = NULL;
183

184 185
	g_return_if_fail (format != NULL);

186
	if (parent != NULL)
187
		wg = gtk_window_get_group (parent);
188

189 190 191 192
	va_start (args, format);
	str = g_strdup_vprintf (format, args);
	va_end (args);

Paolo Borelli's avatar
Paolo Borelli committed
193
	dialog = gtk_message_dialog_new_with_markup (
194 195 196 197
			parent,
			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
		   	GTK_MESSAGE_ERROR,
		   	GTK_BUTTONS_OK,
198
			"%s", str);
Paolo Maggi's avatar
Paolo Maggi committed
199

200
	g_free (str);
201

202 203
	if (wg != NULL)
		gtk_window_group_add_window (wg, GTK_WINDOW (dialog));
204

205 206 207 208
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);

209 210 211 212
	g_signal_connect (G_OBJECT (dialog),
			  "response",
			  G_CALLBACK (gtk_widget_destroy),
			  NULL);
213

214
	gtk_widget_show (dialog);
215
}
216

217 218 219 220 221
/**
 * gedit_utils_escape_underscores:
 * @text: some text.
 * @length: the length.
 *
222
 * Doubles underscore to avoid spurious menu accels.
223 224 225
 *
 * Returns: the text escaped.
 * Deprecated: 3.18
226
 */
227
gchar *
Garrett Regier's avatar
Garrett Regier committed
228
gedit_utils_escape_underscores (const gchar *text,
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
				gssize       length)
{
	GString *str;
	const gchar *p;
	const gchar *end;

	g_return_val_if_fail (text != NULL, NULL);

	if (length < 0)
		length = strlen (text);

	str = g_string_sized_new (length);

	p = text;
	end = text + length;

	while (p != end)
	{
		const gchar *next;
		next = g_utf8_next_char (p);

		switch (*p)
		{
			case '_':
				g_string_append (str, "__");
				break;
			default:
				g_string_append_len (str, p, next - p);
				break;
		}

		p = next;
	}

	return g_string_free (str, FALSE);
}

Paolo Borelli's avatar
Paolo Borelli committed
266 267
/* the following functions are taken from eel */

268 269 270
static gchar *
gedit_utils_str_truncate (const gchar *string,
			  guint        truncate_length,
Garrett Regier's avatar
Garrett Regier committed
271
			  gboolean     middle)
272
{
Paolo Maggi's avatar
Paolo Maggi committed
273 274 275 276 277 278 279
	GString     *truncated;
	guint        length;
	guint        n_chars;
	guint        num_left_chars;
	guint        right_offset;
	guint        delimiter_length;
	const gchar *delimiter = "\342\200\246";
280

Paolo Maggi's avatar
Paolo Maggi committed
281
	g_return_val_if_fail (string != NULL, NULL);
282

Paolo Maggi's avatar
Paolo Maggi committed
283 284 285
	length = strlen (string);

	g_return_val_if_fail (g_utf8_validate (string, length, NULL), NULL);
286 287 288 289 290

	/* It doesnt make sense to truncate strings to less than
	 * the size of the delimiter plus 2 characters (one on each
	 * side)
	 */
Paolo Maggi's avatar
Paolo Maggi committed
291
	delimiter_length = g_utf8_strlen (delimiter, -1);
Garrett Regier's avatar
Garrett Regier committed
292 293
	if (truncate_length < (delimiter_length + 2))
	{
294 295 296
		return g_strdup (string);
	}

Paolo Maggi's avatar
Paolo Maggi committed
297
	n_chars = g_utf8_strlen (string, length);
298 299

	/* Make sure the string is not already small enough. */
Garrett Regier's avatar
Garrett Regier committed
300 301
	if (n_chars <= truncate_length)
	{
302 303 304 305
		return g_strdup (string);
	}

	/* Find the 'middle' where the truncation will occur. */
306 307 308 309
	if (middle)
	{
		num_left_chars = (truncate_length - delimiter_length) / 2;
		right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
310

311 312 313 314 315 316 317 318 319 320 321 322
		truncated = g_string_new_len (string,
					      g_utf8_offset_to_pointer (string, num_left_chars) - string);
		g_string_append (truncated, delimiter);
		g_string_append (truncated, g_utf8_offset_to_pointer (string, right_offset));
	}
	else
	{
		num_left_chars = truncate_length - delimiter_length;
		truncated = g_string_new_len (string,
					      g_utf8_offset_to_pointer (string, num_left_chars) - string);
		g_string_append (truncated, delimiter);
	}
323

Paolo Maggi's avatar
Paolo Maggi committed
324
	return g_string_free (truncated, FALSE);
325 326
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340
gchar *
gedit_utils_str_middle_truncate (const gchar *string,
				 guint        truncate_length)
{
	return gedit_utils_str_truncate (string, truncate_length, TRUE);
}

gchar *
gedit_utils_str_end_truncate (const gchar *string,
			      guint        truncate_length)
{
	return gedit_utils_str_truncate (string, truncate_length, FALSE);
}

Paolo Borelli's avatar
Paolo Borelli committed
341 342 343 344 345 346 347
gchar *
gedit_utils_make_valid_utf8 (const char *name)
{
	GString *string;
	const char *remainder, *invalid;
	int remaining_bytes, valid_bytes;

348 349
	g_return_val_if_fail (name != NULL, NULL);

Paolo Borelli's avatar
Paolo Borelli committed
350 351
	string = NULL;
	remainder = name;
352
	remaining_bytes = strlen (name);
Paolo Borelli's avatar
Paolo Borelli committed
353 354

	while (remaining_bytes != 0) {
Garrett Regier's avatar
Garrett Regier committed
355 356
		if (g_utf8_validate (remainder, remaining_bytes, &invalid))
		{
Paolo Borelli's avatar
Paolo Borelli committed
357 358 359 360
			break;
		}
		valid_bytes = invalid - remainder;

Garrett Regier's avatar
Garrett Regier committed
361 362
		if (string == NULL)
		{
Paolo Borelli's avatar
Paolo Borelli committed
363 364 365
			string = g_string_sized_new (remaining_bytes);
		}
		g_string_append_len (string, remainder, valid_bytes);
366 367
		/* append U+FFFD REPLACEMENT CHARACTER */
		g_string_append (string, "\357\277\275");
Paolo Borelli's avatar
Paolo Borelli committed
368 369 370 371 372

		remaining_bytes -= valid_bytes + 1;
		remainder = invalid + 1;
	}

Garrett Regier's avatar
Garrett Regier committed
373 374
	if (string == NULL)
	{
Paolo Borelli's avatar
Paolo Borelli committed
375 376 377 378
		return g_strdup (name);
	}

	g_string_append (string, remainder);
379

Paolo Borelli's avatar
Paolo Borelli committed
380 381 382 383 384
	g_assert (g_utf8_validate (string->str, -1, NULL));

	return g_string_free (string, FALSE);
}

385 386
static gchar *
uri_get_dirname (const gchar *uri)
Paolo Borelli's avatar
Paolo Borelli committed
387 388
{
	gchar *res;
Paolo Borelli's avatar
Paolo Borelli committed
389 390
	gchar *str;

391 392 393
	g_return_val_if_fail (uri != NULL, NULL);

	/* CHECK: does it work with uri chaining? - Paolo */
Paolo Borelli's avatar
Paolo Borelli committed
394
	str = g_path_get_dirname (uri);
395
	g_return_val_if_fail (str != NULL, g_strdup ("."));
Paolo Borelli's avatar
Paolo Borelli committed
396

Paolo Borelli's avatar
Paolo Borelli committed
397 398 399
	if ((strlen (str) == 1) && (*str == '.'))
	{
		g_free (str);
400

Paolo Borelli's avatar
Paolo Borelli committed
401 402 403
		return NULL;
	}

Paolo Borelli's avatar
Paolo Borelli committed
404
	res = gedit_utils_replace_home_dir_with_tilde (str);
Paolo Borelli's avatar
Paolo Borelli committed
405

Paolo Borelli's avatar
Paolo Borelli committed
406
	g_free (str);
407

Paolo Borelli's avatar
Paolo Borelli committed
408 409
	return res;
}
Paolo Maggi's avatar
Paolo Maggi committed
410

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
/**
 * gedit_utils_uri_get_dirname:
 * @uri: the URI.
 *
 * Note: this function replace home dir with ~.
 *
 * Returns: the directory name.
 * Deprecated: 3.18
 */
gchar *
gedit_utils_uri_get_dirname (const gchar *uri)
{
	return uri_get_dirname (uri);
}

426
/**
427
 * gedit_utils_location_get_dirname_for_display:
428
 * @location: the location
429 430 431 432 433 434
 *
 * Returns a string suitable to be displayed in the UI indicating
 * the name of the directory where the file is located.
 * For remote files it may also contain the hostname etc.
 * For local files it tries to replace the home dir with ~.
 *
435
 * Returns: (transfer full): a string to display the dirname
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
 */
gchar *
gedit_utils_location_get_dirname_for_display (GFile *location)
{
	gchar *uri;
	gchar *res;
	GMount *mount;

	g_return_val_if_fail (location != NULL, NULL);

	/* we use the parse name, that is either the local path
	 * or an uri but which is utf8 safe */
	uri = g_file_get_parse_name (location);

	/* FIXME: this is sync... is it a problem? */
	mount = g_file_find_enclosing_mount (location, NULL, NULL);
	if (mount != NULL)
	{
		gchar *mount_name;
455 456
		gchar *path = NULL;
		gchar *dirname;
457 458 459 460

		mount_name = g_mount_get_name (mount);
		g_object_unref (mount);

461 462 463 464 465
		/* obtain the "path" part of the uri */
		gedit_utils_decode_uri (uri,
					NULL, NULL,
					NULL, NULL,
					&path);
466

467 468
		if (path == NULL)
		{
469
			dirname = uri_get_dirname (uri);
470 471
		}
		else
472
		{
473
			dirname = uri_get_dirname (path);
474
		}
475

476
		if (dirname == NULL || strcmp (dirname, ".") == 0)
477 478 479
		{
			res = mount_name;
		}
480 481 482 483 484
		else
		{
			res = g_strdup_printf ("%s %s", mount_name, dirname);
			g_free (mount_name);
		}
485

486 487
		g_free (path);
		g_free (dirname);
488 489 490 491
	}
	else
	{
		/* fallback for local files or uris without mounts */
492
		res = uri_get_dirname (uri);
493 494 495 496 497 498 499
	}

	g_free (uri);

	return res;
}

Paolo Maggi's avatar
Paolo Maggi committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
gchar *
gedit_utils_replace_home_dir_with_tilde (const gchar *uri)
{
	gchar *tmp;
	gchar *home;

	g_return_val_if_fail (uri != NULL, NULL);

	/* Note that g_get_home_dir returns a const string */
	tmp = (gchar *)g_get_home_dir ();

	if (tmp == NULL)
		return g_strdup (uri);

	home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
	if (home == NULL)
		return g_strdup (uri);

	if (strcmp (uri, home) == 0)
	{
		g_free (home);
521

522
		return g_strdup ("~/");
Paolo Maggi's avatar
Paolo Maggi committed
523 524 525 526 527 528 529 530 531 532 533 534 535
	}

	tmp = home;
	home = g_strdup_printf ("%s/", tmp);
	g_free (tmp);

	if (g_str_has_prefix (uri, home))
	{
		gchar *res;

		res = g_strdup_printf ("~/%s", uri + strlen (home));

		g_free (home);
536 537

		return res;
Paolo Maggi's avatar
Paolo Maggi committed
538 539 540
	}

	g_free (home);
541

Paolo Maggi's avatar
Paolo Maggi committed
542 543 544
	return g_strdup (uri);
}

545 546 547
/* the following two functions are courtesy of galeon */

/**
548
 * gedit_utils_get_current_workspace:
549
 * @screen: a #GdkScreen
550 551 552 553 554 555 556 557 558
 *
 * Get the currently visible workspace for the #GdkScreen.
 *
 * If the X11 window property isn't found, 0 (the first workspace)
 * is returned.
 */
guint
gedit_utils_get_current_workspace (GdkScreen *screen)
{
559
#ifdef GDK_WINDOWING_X11
560 561 562 563 564 565 566 567 568
	GdkWindow *root_win;
	GdkDisplay *display;
	guint ret = 0;

	g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);

	root_win = gdk_screen_get_root_window (screen);
	display = gdk_screen_get_display (screen);

569 570 571 572 573 574 575 576 577
	if (GDK_IS_X11_DISPLAY (display))
	{
		Atom type;
		gint format;
		gulong nitems;
		gulong bytes_after;
		guint *current_desktop;
		gint err, result;

578
		gdk_x11_display_error_trap_push (display);
579 580 581 582
		result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
					     gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP"),
					     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
					     &bytes_after, (gpointer) &current_desktop);
583
		err = gdk_x11_display_error_trap_pop (display);
584 585 586 587 588 589 590 591 592

		if (err != Success || result != Success)
			return ret;

		if (type == XA_CARDINAL && format == 32 && nitems > 0)
			ret = current_desktop[0];

		XFree (current_desktop);
	}
593 594

	return ret;
595 596 597 598 599
#else
	/* FIXME: on mac etc proably there are native APIs
	 * to get the current workspace etc */
	return 0;
#endif
600 601 602
}

/**
603
 * gedit_utils_get_window_workspace:
604
 * @gtkwindow: a #GtkWindow.
605
 *
606
 * Get the workspace the window is on.
607 608 609
 *
 * This function gets the workspace that the #GtkWindow is visible on,
 * it returns GEDIT_ALL_WORKSPACES if the window is sticky, or if
610 611 612
 * the window manager doesn't support this function.
 *
 * Returns: the workspace the window is on.
613 614 615 616
 */
guint
gedit_utils_get_window_workspace (GtkWindow *gtkwindow)
{
617
#ifdef GDK_WINDOWING_X11
618 619 620 621 622 623 624
	GdkWindow *window;
	GdkDisplay *display;
	Atom type;
	gint format;
	gulong nitems;
	gulong bytes_after;
	guint *workspace;
625
	gint err, result;
626 627 628
	guint ret = GEDIT_ALL_WORKSPACES;

	g_return_val_if_fail (GTK_IS_WINDOW (gtkwindow), 0);
629
	g_return_val_if_fail (gtk_widget_get_realized (GTK_WIDGET (gtkwindow)), 0);
630

Paolo Borelli's avatar
Paolo Borelli committed
631
	window = gtk_widget_get_window (GTK_WIDGET (gtkwindow));
632
	display = gdk_window_get_display (window);
633

634 635
	if (GDK_IS_X11_DISPLAY (display))
	{
636
		gdk_x11_display_error_trap_push (display);
637 638 639 640
		result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
					     gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
					     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
					     &bytes_after, (gpointer) &workspace);
641
		err = gdk_x11_display_error_trap_pop (display);
642 643 644

		if (err != Success || result != Success)
			return ret;
645

646 647
		if (type == XA_CARDINAL && format == 32 && nitems > 0)
			ret = workspace[0];
648

649 650
		XFree (workspace);
	}
Ignacio Casal Quinteiro's avatar
Ignacio Casal Quinteiro committed
651 652

	return ret;
653 654 655 656 657
#else
	/* FIXME: on mac etc proably there are native APIs
	 * to get the current workspace etc */
	return 0;
#endif
658
}
659

660
/**
661
 * gedit_utils_get_current_viewport:
662 663 664
 * @screen: a #GdkScreen
 * @x: (out): x-axis point.
 * @y: (out): y-axis point.
665 666 667 668 669 670 671 672 673 674
 *
 * Get the currently visible viewport origin for the #GdkScreen.
 *
 * If the X11 window property isn't found, (0, 0) is returned.
 */
void
gedit_utils_get_current_viewport (GdkScreen    *screen,
				  gint         *x,
				  gint         *y)
{
675
#ifdef GDK_WINDOWING_X11
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
	GdkWindow *root_win;
	GdkDisplay *display;
	Atom type;
	gint format;
	gulong nitems;
	gulong bytes_after;
	gulong *coordinates;
	gint err, result;

	g_return_if_fail (GDK_IS_SCREEN (screen));
	g_return_if_fail (x != NULL && y != NULL);

	/* Default values for the viewport origin */
	*x = 0;
	*y = 0;

	root_win = gdk_screen_get_root_window (screen);
	display = gdk_screen_get_display (screen);

695 696
	if (GDK_IS_X11_DISPLAY (display))
	{
697
		gdk_x11_display_error_trap_push (display);
698 699 700 701
		result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
					     gdk_x11_get_xatom_by_name_for_display (display, "_NET_DESKTOP_VIEWPORT"),
					     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
					     &bytes_after, (void*) &coordinates);
702
		err = gdk_x11_display_error_trap_pop (display);
703

704 705
		if (err != Success || result != Success)
			return;
706

707 708 709 710 711 712 713 714
		if (type != XA_CARDINAL || format != 32 || nitems < 2)
		{
			XFree (coordinates);
			return;
		}

		*x = coordinates[0];
		*y = coordinates[1];
715 716
		XFree (coordinates);
	}
717 718 719 720 721 722
#else
	/* FIXME: on mac etc proably there are native APIs
	 * to get the current workspace etc */
	*x = 0;
	*y = 0;
#endif
723 724
}

Paolo Borelli's avatar
Paolo Borelli committed
725 726 727 728 729 730 731 732 733 734 735 736 737
static gboolean
is_valid_scheme_character (gchar c)
{
	return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
}

static gboolean
has_valid_scheme (const gchar *uri)
{
	const gchar *p;

	p = uri;

Garrett Regier's avatar
Garrett Regier committed
738 739
	if (!is_valid_scheme_character (*p))
	{
Paolo Borelli's avatar
Paolo Borelli committed
740 741 742
		return FALSE;
	}

Garrett Regier's avatar
Garrett Regier committed
743 744
	do
	{
Paolo Borelli's avatar
Paolo Borelli committed
745 746 747 748 749 750 751
		p++;
	} while (is_valid_scheme_character (*p));

	return *p == ':';
}

gboolean
752
gedit_utils_is_valid_location (GFile *location)
Paolo Borelli's avatar
Paolo Borelli committed
753 754
{
	const guchar *p;
755 756
	gchar *uri;
	gboolean is_valid;
757

758
	if (location == NULL)
Paolo Borelli's avatar
Paolo Borelli committed
759 760
		return FALSE;

761 762
	uri = g_file_get_uri (location);

Paolo Borelli's avatar
Paolo Borelli committed
763
	if (!has_valid_scheme (uri))
764 765
	{
		g_free (uri);
Paolo Borelli's avatar
Paolo Borelli committed
766
		return FALSE;
767 768 769
	}

	is_valid = TRUE;
Paolo Borelli's avatar
Paolo Borelli committed
770 771

	/* We expect to have a fully valid set of characters */
772
	for (p = (const guchar *)uri; *p; p++) {
Paolo Borelli's avatar
Paolo Borelli committed
773 774 775 776
		if (*p == '%')
		{
			++p;
			if (!g_ascii_isxdigit (*p))
777 778 779 780
			{
				is_valid = FALSE;
				break;
			}
Paolo Borelli's avatar
Paolo Borelli committed
781

782
			++p;
Paolo Borelli's avatar
Paolo Borelli committed
783
			if (!g_ascii_isxdigit (*p))
784 785 786 787
			{
				is_valid = FALSE;
				break;
			}
Paolo Borelli's avatar
Paolo Borelli committed
788 789 790 791
		}
		else
		{
			if (*p <= 32 || *p >= 128)
792 793 794 795
			{
				is_valid = FALSE;
				break;
			}
Paolo Borelli's avatar
Paolo Borelli committed
796 797 798
		}
	}

799 800 801
	g_free (uri);

	return is_valid;
Paolo Borelli's avatar
Paolo Borelli committed
802 803
}

804 805
static GtkWidget *handle_builder_error (const gchar *message, ...) G_GNUC_PRINTF (1, 2);

806
static GtkWidget *
807
handle_builder_error (const gchar *message, ...)
808 809 810 811 812 813 814 815
{
	GtkWidget *label;
	gchar *msg;
	gchar *msg_plain;
	va_list args;

	va_start (args, message);
	msg_plain = g_strdup_vprintf (message, args);
816 817 818 819
	va_end (args);

	label = gtk_label_new (NULL);
	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
820 821

	msg = g_strconcat ("<span size=\"large\" weight=\"bold\">",
822 823 824
			   msg_plain, "</span>\n\n",
			   _("Please check your installation."),
			   NULL);
825

826
	gtk_label_set_markup (GTK_LABEL (label), msg);
827 828 829 830

	g_free (msg_plain);
	g_free (msg);

Paolo Borelli's avatar
Paolo Borelli committed
831 832 833 834
	gtk_widget_set_margin_start (label, 6);
	gtk_widget_set_margin_end (label, 6);
	gtk_widget_set_margin_top (label, 6);
	gtk_widget_set_margin_bottom (label, 6);
835 836 837

	return label;
}
Paolo Borelli's avatar
Paolo Borelli committed
838

839 840 841 842 843 844 845 846
/* TODO: just add a translation_doamin arg to get_ui_objects method */
static gboolean
get_ui_objects_with_translation_domain (const gchar  *filename,
                                        const gchar  *translation_domain,
                                        gchar       **root_objects,
                                        GtkWidget   **error_widget,
                                        const gchar  *object_name,
                                        va_list       args)
Paolo Borelli's avatar
Paolo Borelli committed
847
{
848
	GtkBuilder *builder;
Paolo Borelli's avatar
Paolo Borelli committed
849
	const gchar *name;
850
	GError *error = NULL;
851
	gchar *filename_markup;
Paolo Borelli's avatar
Paolo Borelli committed
852 853 854 855
	gboolean ret = TRUE;

	g_return_val_if_fail (filename != NULL, FALSE);
	g_return_val_if_fail (error_widget != NULL, FALSE);
856
	g_return_val_if_fail (object_name != NULL, FALSE);
Paolo Borelli's avatar
Paolo Borelli committed
857

858
	filename_markup = g_markup_printf_escaped ("<i>%s</i>", filename);
Paolo Borelli's avatar
Paolo Borelli committed
859 860
	*error_widget = NULL;

861
	builder = gtk_builder_new ();
862

863 864 865 866 867
	if (translation_domain != NULL)
	{
		gtk_builder_set_translation_domain (builder, translation_domain);
	}

868
	if (root_objects != NULL)
869
	{
870 871 872
		gtk_builder_add_objects_from_file (builder,
						   filename,
						   root_objects,
873
						   &error);
874
	}
875
	else
876
	{
877 878 879
		gtk_builder_add_from_file (builder,
					   filename,
					   &error);
880
	}
881

882
	if (error != NULL)
Paolo Borelli's avatar
Paolo Borelli committed
883
	{
884
		*error_widget = handle_builder_error (_("Unable to open UI file %s. Error: %s"),
885 886 887
						      filename_markup,
						      error->message);
		g_error_free (error);
888
		g_free (filename_markup);
889
		g_object_unref (builder);
890

Paolo Borelli's avatar
Paolo Borelli committed
891 892
		return FALSE;
	}
893

894
	for (name = object_name; name; name = va_arg (args, const gchar *))
Paolo Borelli's avatar
Paolo Borelli committed
895
	{
896
		GObject **gobj;
897

898 899
		gobj = va_arg (args, GObject **);
		*gobj = gtk_builder_get_object (builder, name);
900

901
		if (!*gobj)
Paolo Borelli's avatar
Paolo Borelli committed
902
		{
903
			*error_widget = handle_builder_error (_("Unable to find the object “%s” inside file %s."),
904
							      name,
905
							      filename_markup),
Paolo Borelli's avatar
Paolo Borelli committed
906 907 908
			ret = FALSE;
			break;
		}
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923

		/* we return a new ref for the root objects,
		 * the others are already reffed by their parent root object */
		if (root_objects != NULL)
		{
			gint i;

			for (i = 0; root_objects[i] != NULL; ++i)
			{
				if ((strcmp (name, root_objects[i]) == 0))
				{
					g_object_ref (*gobj);
				}
			}
		}
Paolo Borelli's avatar
Paolo Borelli committed
924 925
	}

926 927
	g_free (filename_markup);
	g_object_unref (builder);
Paolo Borelli's avatar
Paolo Borelli committed
928 929 930

	return ret;
}
931

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
/**
 * gedit_utils_get_ui_objects:
 * @filename: the path to the gtk builder file
 * @root_objects: a %NULL terminated list of root objects to load or NULL to
 *                load all objects
 * @error_widget: a pointer were a #GtkLabel
 * @object_name: the name of the first object
 * @...: a pointer were the first object is returned, followed by more
 *       name / object pairs and terminated by %NULL.
 *
 * This function gets the requested objects from a GtkBuilder ui file. In case
 * of error it returns %FALSE and sets error_widget to a GtkLabel containing
 * the error message to display.
 *
 * Returns: %FALSE if an error occurs, %TRUE on success.
947
 * Deprecated: 3.18
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
 */
gboolean
gedit_utils_get_ui_objects (const gchar  *filename,
			    gchar       **root_objects,
			    GtkWidget   **error_widget,
			    const gchar  *object_name,
			    ...)
{
	gboolean ret;
	va_list args;

	va_start (args, object_name);
	ret = get_ui_objects_with_translation_domain (filename,
	                                              NULL,
	                                              root_objects,
	                                              error_widget,
	                                              object_name,
	                                              args);
	va_end (args);

	return ret;
}

/**
 * gedit_utils_get_ui_objects_with_translation_domain:
 * @filename: the path to the gtk builder file
 * @translation_domain: the specific translation domain
 * @root_objects: a %NULL terminated list of root objects to load or NULL to
 *                load all objects
 * @error_widget: a pointer were a #GtkLabel
 * @object_name: the name of the first object
 * @...: a pointer were the first object is returned, followed by more
 *       name / object pairs and terminated by %NULL.
 *
 * This function gets the requested objects from a GtkBuilder ui file. In case
 * of error it returns %FALSE and sets error_widget to a GtkLabel containing
 * the error message to display.
 *
 * Returns: %FALSE if an error occurs, %TRUE on success.
987
 * Deprecated: 3.18
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
 */
gboolean
gedit_utils_get_ui_objects_with_translation_domain (const gchar  *filename,
                                                    const gchar  *translation_domain,
                                                    gchar       **root_objects,
                                                    GtkWidget   **error_widget,
                                                    const gchar  *object_name,
                                                    ...)
{
	gboolean ret;
	va_list args;

	va_start (args, object_name);
	ret = get_ui_objects_with_translation_domain (filename,
	                                              translation_domain,
	                                              root_objects,
	                                              error_widget,
	                                              object_name,
	                                              args);
	va_end (args);

	return ret;
}

1012 1013
static gchar *
make_canonical_uri_from_shell_arg (const gchar *str)
1014
{
1015
	GFile *gfile;
1016 1017 1018 1019
	gchar *uri;

	g_return_val_if_fail (str != NULL, NULL);
	g_return_val_if_fail (*str != '\0', NULL);
1020 1021

	/* Note for the future:
1022
	 * FIXME: is still still relevant?
1023
	 *
1024 1025
	 * <federico> paolo: and flame whoever tells
	 * you that file:///gnome/test_files/hëllò
1026 1027
	 * doesn't work --- that's not a valid URI
	 *
1028 1029 1030
	 * <paolo> federico: well, another solution that
	 * does not requires patch to _from_shell_args
	 * is to check that the string returned by it
1031
	 * contains only ASCII chars
1032
	 * <federico> paolo: hmmmm, isn't there
1033 1034 1035 1036
	 * gnome_vfs_is_uri_valid() or something?
	 * <paolo>: I will use gedit_utils_is_valid_uri ()
	 *
	 */
1037 1038 1039

	gfile = g_file_new_for_commandline_arg (str);

1040 1041 1042 1043
	if (gedit_utils_is_valid_location (gfile))
	{
		uri = g_file_get_uri (gfile);
		g_object_unref (gfile);
1044
		return uri;
1045
	}
1046

1047
	g_object_unref (gfile);
1048 1049 1050
	return NULL;
}

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
/**
 * gedit_utils_make_canonical_uri_from_shell_arg:
 * @str: shell arg.
 *
 * Returns: canonical URI, or %NULL if @str is not a valid URI and/or filename.
 * Deprecated: 3.18
 */
gchar *
gedit_utils_make_canonical_uri_from_shell_arg (const gchar *str)
{
	return make_canonical_uri_from_shell_arg (str);
}

1064 1065
/**
 * gedit_utils_basename_for_display:
1066
 * @location: location for which the basename should be displayed
1067
 *
1068
 * Returns: (transfer full): the basename of a file suitable for display to users.
1069 1070
 */
gchar *
1071
gedit_utils_basename_for_display (GFile *location)
1072 1073 1074
{
	gchar *name;
	gchar *hn;
1075
	gchar *uri;
1076

1077
	g_return_val_if_fail (G_IS_FILE (location), NULL);
1078

1079
	uri = g_file_get_uri (location);
1080

1081
	/* First, try to query the display name, but only on local files */
1082
	if (g_file_has_uri_scheme (location, "file"))
1083
	{
1084
		GFileInfo *info;
1085

1086
		info = g_file_query_info (location,
1087 1088 1089
					  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
					  G_FILE_QUERY_INFO_NONE,
					  NULL,
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
					  NULL);

		if (info)
		{
			/* Simply get the display name to use as the basename */
			name = g_strdup (g_file_info_get_display_name (info));
			g_object_unref (info);
		}
		else
		{
			/* This is a local file, and therefore we will use
			 * g_filename_display_basename on the local path */
			gchar *local_path;
1103

1104
			local_path = g_file_get_path (location);
1105 1106 1107
			name = g_filename_display_basename (local_path);
			g_free (local_path);
		}
1108
	}
1109 1110
	else if (g_file_has_parent (location, NULL) ||
	          !gedit_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL))
1111 1112 1113 1114
	{
		/* For remote files with a parent (so not just http://foo.com)
		   or remote file for which the decoding of the host name fails,
		   use the _parse_name and take basename of that */
1115 1116
		gchar *parse_name;
		gchar *base;
1117

1118
		parse_name = g_file_get_parse_name (location);
1119 1120
		base = g_filename_display_basename (parse_name);
		name = g_uri_unescape_string (base, NULL);
1121 1122

		g_free (base);
1123
		g_free (parse_name);
1124 1125 1126 1127 1128
	}
	else
	{
		/* display '/ on <host>' using the decoded host */
		gchar *hn_utf8;
1129 1130

		if  (hn != NULL)
Garrett Regier's avatar
Garrett Regier committed
1131
		{
1132
			hn_utf8 = gedit_utils_make_valid_utf8 (hn);
Garrett Regier's avatar
Garrett Regier committed
1133
		}
1134
		else
Garrett Regier's avatar
Garrett Regier committed
1135
		{
1136 1137
			/* we should never get here */
			hn_utf8 = g_strdup ("?");
Garrett Regier's avatar
Garrett Regier committed
1138
		}
1139

1140 1141
		/* Translators: '/ on <remote-share>' */
		name = g_strdup_printf (_("/ on %s"), hn_utf8);
1142

1143 1144 1145
		g_free (hn_utf8);
		g_free (hn);
	}
Paolo Borelli's avatar
Paolo Borelli committed
1146

1147
	g_free (uri);
Paolo Borelli's avatar
Paolo Borelli committed
1148

1149
	return name;
1150
}
1151

1152 1153 1154 1155 1156
/**
 * gedit_utils_drop_get_uris:
 * @selection_data: the #GtkSelectionData from drag_data_received
 *
 * Create a list of valid uri's from a uri-list drop.
1157 1158
 *
 * Returns: (transfer full): a string array which will hold the uris or
1159
 *           %NULL if there were no valid uris. g_strfreev should be used when
1160
 *           the string array is no longer used
1161 1162 1163 1164 1165 1166 1167 1168 1169
 */
gchar **
gedit_utils_drop_get_uris (GtkSelectionData *selection_data)
{
	gchar **uris;
	gint i;
	gint p = 0;
	gchar **uri_list;

1170
	uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data));
1171 1172 1173 1174 1175
	uri_list = g_new0(gchar *, g_strv_length (uris) + 1);

	for (i = 0; uris[i] != NULL; i++)
	{
		gchar *uri;
1176

1177
		uri = make_canonical_uri_from_shell_arg (uris[i]);
1178

1179 1180 1181 1182 1183 1184 1185 1186
		/* Silently ignore malformed URI/filename */
		if (uri != NULL)
			uri_list[p++] = uri;
	}

	if (*uri_list == NULL)
	{
		g_free(uri_list);
1187
		g_strfreev (uris);
1188 1189 1190
		return NULL;
	}

1191
	g_strfreev (uris);
1192 1193
	return uri_list;
}
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204

static void
null_ptr (gchar **ptr)
{
	if (ptr)
		*ptr = NULL;
}

/**
 * gedit_utils_decode_uri:
 * @uri: the uri to decode
1205
 * @scheme: (out) (allow-none): return value pointer for the uri's
1206
 * scheme (e.g. http, sftp, ...), or %NULL
1207 1208 1209 1210
 * @user: (out) (allow-none): return value pointer for the uri user info, or %NULL
 * @port: (out) (allow-none): return value pointer for the uri port, or %NULL
 * @host: (out) (allow-none): return value pointer for the uri host, or %NULL
 * @path: (out) (allow-none): return value pointer for the uri path, or %NULL
1211 1212 1213
 *
 * Parse and break an uri apart in its individual components like the uri
 * scheme, user info, port, host and path. The return value pointer can be
1214
 * %NULL to ignore certain parts of the uri. If the function returns %TRUE, then
1215
 * all return value pointers should be freed using g_free
1216
 *
1217
 * Return value: %TRUE if the uri could be properly decoded, %FALSE otherwise.
1218 1219
 */
gboolean
1220 1221 1222 1223 1224
gedit_utils_decode_uri (const gchar  *uri,
			gchar       **scheme,
			gchar       **user,
			gchar       **host,
			gchar       **port,
1225
			gchar       **path)
1226
{
1227
	/* Largely copied from glib/gio/gdummyfile.c:_g_decode_uri. This
1228 1229 1230 1231 1232 1233 1234 1235 1236
	 * functionality should be in glib/gio, but for now we implement it
	 * ourselves (see bug #546182) */

	const char *p, *in, *hier_part_start, *hier_part_end;
	char *out;
	char c;

	/* From RFC 3986 Decodes:
	 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
1237
	 */
1238 1239

	p = uri;
1240

1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
	null_ptr (scheme);
	null_ptr (user);
	null_ptr (port);
	null_ptr (host);
	null_ptr (path);

	/* Decode scheme:
	 * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
	 */

	if (!g_ascii_isalpha (*p))
		return FALSE;

	while (1)
	{
		c = *p++;

		if (c == ':')
			break;

		if (!(g_ascii_isalnum(c) ||
		      c == '+' ||
		      c == '-' ||
		      c == '.'))
Garrett Regier's avatar
Garrett Regier committed
1265
		{
1266
			return FALSE;
Garrett Regier's avatar
Garrett Regier committed
1267
		}
1268
	}
1269

1270 1271 1272 1273
	if (scheme)
	{
		*scheme = g_malloc (p - uri);
		out = *scheme;
1274

1275
		for (in = uri; in < p - 1; in++)
Garrett Regier's avatar
Garrett Regier committed
1276
		{
1277
			*out++ = g_ascii_tolower (*in);
Garrett Regier's avatar
Garrett Regier committed
1278
		}
1279

1280 1281
		*out = '\0';
	}
1282

1283 1284
	hier_part_start = p;
	hier_part_end = p + strlen (p);
1285

1286 1287 1288 1289 1290 1291
	if (hier_part_start[0] == '/' && hier_part_start[1] == '/')
	{
		const char *authority_start, *authority_end;
		const char *userinfo_start, *userinfo_end;
		const char *host_start, *host_end;
		const char *port_start;
1292

1293 1294 1295
		authority_start = hier_part_start + 2;
		/* authority is always followed by / or nothing */
		authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
1296

1297 1298 1299 1300 1301 1302 1303 1304
		if (authority_end == NULL)
			authority_end = hier_part_end;

		/* 3.2:
		 * authority = [ userinfo "@" ] host [ ":" port ]
		 */

		userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
1305

1306 1307 1308
		if (userinfo_end)
		{
			userinfo_start = authority_start;
1309

1310 1311
			if (user)
				*user = g_uri_unescape_segment (userinfo_start, userinfo_end, NULL);
1312

1313 1314 1315 1316 1317 1318 1319
			if (user && *user == NULL)
			{
				if (scheme)
					g_free (*scheme);

				return FALSE;
			}
1320

1321 1322 1323
			host_start = userinfo_end + 1;
		}
		else
Garrett Regier's avatar
Garrett Regier committed
1324
		{
1325
			host_start = authority_start;
Garrett Regier's avatar
Garrett Regier committed
1326
		}
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337

		port_start = memchr (host_start, ':', authority_end - host_start);

		if (port_start)
		{
			host_end = port_start++;

			if (port)
				*port = g_strndup (port_start, authority_end - port_start);
		}
		else
Garrett Regier's avatar
Garrett Regier committed
1338
		{
1339
			host_end = authority_end;
Garrett Regier's avatar
Garrett Regier committed
1340
		}
1341 1342 1343 1344 1345 1346 1347 1348 1349

		if (host)
			*host = g_strndup (host_start, host_end - host_start);

		hier_part_start = authority_end;
	}

	if (path)
		*path = g_uri_unescape_segment (hier_part_start, hier_part_end, "/");
1350

1351 1352
	return TRUE;
}
1353

1354
GtkSourceCompressionType
1355 1356 1357 1358
gedit_utils_get_compression_type_from_content_type (const gchar *content_type)
{
	if (content_type == NULL)
	{
1359
		return GTK_SOURCE_COMPRESSION_TYPE_NONE;
1360 1361 1362 1363
	}

	if (g_content_type_is_a (content_type, "application/x-gzip"))
	{
1364
		return GTK_SOURCE_COMPRESSION_TYPE_GZIP;
1365 1366
	}

1367
	return GTK_SOURCE_COMPRESSION_TYPE_NONE;
1368 1369
}

1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
/* Copied from nautilus */
static gchar *
get_direct_save_filename (GdkDragContext *context)
{
	guchar *prop_text;
	gint prop_len;

	if (!gdk_property_get (gdk_drag_context_get_source_window  (context), gdk_atom_intern ("XdndDirectSave0", FALSE),
			       gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
			       &prop_len, &prop_text) && prop_text != NULL) {
		return NULL;
	}

	/* Zero-terminate the string */
	prop_text = g_realloc (prop_text, prop_len + 1);
	prop_text[prop_len] = '\0';

	/* Verify that the file name provided by the source is valid */
	if (*prop_text == '\0' ||
	    strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) {
		gedit_debug_message (DEBUG_UTILS, "Invalid filename provided by XDS drag site");
		g_free (prop_text);
		return NULL;
	}

	return (gchar *)prop_text;
}

gchar *
gedit_utils_set_direct_save_filename (GdkDragContext *context)
{
	gchar *uri;
	gchar *filename;

	uri = NULL;
	filename = get_direct_save_filename (context);

	if (filename != NULL)
	{
		gchar *tempdir;
		gchar *path;

		tempdir = g_dir_make_tmp ("gedit-drop-XXXXXX", NULL);
		if (tempdir == NULL)
		{
			tempdir = g_strdup (g_get_tmp_dir ());
		}

		path = g_build_filename (tempdir,
					filename,
					NULL);

		uri = g_filename_to_uri (path, NULL, NULL);

		/* Change the property */
		gdk_property_change (gdk_drag_context_get_source_window (context),
				     gdk_atom_intern ("XdndDirectSave0", FALSE),
				     gdk_atom_intern ("text/plain", FALSE), 8,
				     GDK_PROP_MODE_REPLACE, (const guchar *) uri,
				     strlen (uri));

		g_free (tempdir);
		g_free (path);
		g_free (filename);
	}

	return uri;
}

1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
const gchar *
gedit_utils_newline_type_to_string (GtkSourceNewlineType newline_type)
{
	switch (newline_type)
	{
	case GTK_SOURCE_NEWLINE_TYPE_LF:
		return _("Unix/Linux");
	case GTK_SOURCE_NEWLINE_TYPE_CR:
		return _("Mac OS Classic");
	case GTK_SOURCE_NEWLINE_TYPE_CR_LF:
		return _("Windows");
	}

	return NULL;
}

1455
/* ex:set ts=8 noet: */