diff options
| author | Perberos <[email protected]> | 2011-11-07 16:46:58 -0300 | 
|---|---|---|
| committer | Perberos <[email protected]> | 2011-11-07 16:46:58 -0300 | 
| commit | 528c1e5ff51e213936e800fc5a9a25da99c0bdf2 (patch) | |
| tree | 77f8aa456b09367ba81f04d4562fc935f898a951 /gedit | |
| download | pluma-528c1e5ff51e213936e800fc5a9a25da99c0bdf2.tar.bz2 pluma-528c1e5ff51e213936e800fc5a9a25da99c0bdf2.tar.xz  | |
initial
Diffstat (limited to 'gedit')
146 files changed, 62258 insertions, 0 deletions
diff --git a/gedit/Makefile.am b/gedit/Makefile.am new file mode 100755 index 00000000..f08c6427 --- /dev/null +++ b/gedit/Makefile.am @@ -0,0 +1,254 @@ +## Process this file with automake to produce Makefile.in +SUBDIRS = dialogs smclient + +if OS_OSX +SUBDIRS += osx +endif + +bin_PROGRAMS = gedit + +noinst_LTLIBRARIES = libgedit.la + +INCLUDES =								\ +	-I$(top_srcdir)							\ +	-I$(srcdir)							\ +	-I$(srcdir)/smclient						\ +	$(GEDIT_CFLAGS)							\ +	$(IGE_MAC_CFLAGS)						\ +	$(WARN_CFLAGS)							\ +	$(DISABLE_DEPRECATED_CFLAGS)					\ +	-DDATADIR=\""$(datadir)"\"					\ +	-DLIBDIR=\""$(libdir)"\"					 + +gedit_SOURCES = \ +	gedit.c + +gedit_LDADD = libgedit.la $(GEDIT_LIBS) $(IGE_MAC_LIBS) $(EGG_SMCLIENT_LIBS) +  +if PLATFORM_WIN32 +gedit_LDFLAGS = -Wl,--export-all-symbols -Wl,--out-implib,libgedit-$(GEDIT_API_VERSION).a +if OS_WIN32 +gedit_LDFLAGS += -mwindows +endif +else +gedit_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" +endif + +libgedit_la_LDFLAGS = -export-dynamic -no-undefined -export-symbols-regex "^[[^_]].*" + +libgedit_la_LIBADD = \ +	dialogs/libdialogs.la		\ +	smclient/libeggsmclient.la + +# GEDIT_LIBS must be the last to ensure correct order on some platforms +libgedit_la_LIBADD += $(GEDIT_LIBS) + +if OS_OSX +gedit_LDFLAGS += -framework Carbon + +libgedit_la_LIBADD += osx/libosx.la +endif + +BUILT_SOURCES = 			\ +	gedit-enum-types.c		\ +	gedit-enum-types.h		\ +	gedit-marshal.c			\ +	gedit-marshal.h + +if OS_WIN32 +gedit-res.o: gedit.rc +	$(WINDRES) -i gedit.rc --input-format=rc -o gedit-res.o -O coff + +gedit_LDADD += gedit-res.o +endif + +NOINST_H_FILES =			\ +	gedit-close-button.h		\ +	gedit-dirs.h			\ +	gedit-document-input-stream.h	\ +	gedit-document-loader.h		\ +	gedit-document-output-stream.h	\ +	gedit-document-saver.h		\ +	gedit-documents-panel.h		\ +	gedit-gio-document-loader.h	\ +	gedit-gio-document-saver.h	\ +	gedit-history-entry.h		\ +	gedit-io-error-message-area.h	\ +	gedit-language-manager.h	\ +	gedit-object-module.h		\ +	gedit-plugin-info.h		\ +	gedit-plugin-info-priv.h	\ +	gedit-plugin-loader.h		\ +	gedit-plugin-manager.h		\ +	gedit-plugins-engine.h		\ +	gedit-prefs-manager-private.h	\ +	gedit-print-job.h		\ +	gedit-print-preview.h		\ +	gedit-session.h			\ +	gedit-smart-charset-converter.h	\ +	gedit-style-scheme-manager.h	\ +	gedit-tab-label.h		\ +	gedittextregion.h		\ +	gedit-ui.h			\ +	gedit-window-private.h + +INST_H_FILES =				\ +	gedit-app.h			\ +	gedit-commands.h		\ +	gedit-debug.h			\ +	gedit-document.h 		\ +	gedit-encodings.h		\ +	gedit-encodings-combo-box.h	\ +	gedit-file-chooser-dialog.h	\ +	gedit-help.h 			\ +	gedit-message-bus.h		\ +	gedit-message-type.h		\ +	gedit-message.h			\ +	gedit-notebook.h		\ +	gedit-panel.h			\ +	gedit-plugin.h			\ +	gedit-prefs-manager-app.h	\ +	gedit-prefs-manager.h		\ +	gedit-progress-message-area.h	\ +	gedit-statusbar.h		\ +	gedit-status-combo-box.h	\ +	gedit-tab.h 			\ +	gedit-utils.h 			\ +	gedit-view.h 			\ +	gedit-window.h + +if !ENABLE_GVFS_METADATA +INST_H_FILES += gedit-metadata-manager.h +endif + +headerdir = $(prefix)/include/gedit-@GEDIT_API_VERSION@/gedit + +header_DATA = 				\ +	$(INST_H_FILES) + + +libgedit_la_SOURCES = 			\ +	$(BUILT_SOURCES)		\ +	$(BACON_FILES)			\ +	$(POSIXIO_FILES)		\ +	gedit-app.c			\ +	gedit-close-button.c		\ +	gedit-commands-documents.c	\ +	gedit-commands-edit.c		\ +	gedit-commands-file.c		\ +	gedit-commands-file-print.c	\ +	gedit-commands-help.c		\ +	gedit-commands-search.c		\ +	gedit-commands-view.c		\ +	gedit-debug.c			\ +	gedit-dirs.c			\ +	gedit-document.c 		\ +	gedit-document-input-stream.c	\ +	gedit-document-loader.c		\ +	gedit-document-output-stream.c	\ +	gedit-gio-document-loader.c	\ +	gedit-document-saver.c		\ +	gedit-gio-document-saver.c	\ +	gedit-documents-panel.c		\ +	gedit-encodings.c		\ +	gedit-encodings-combo-box.c	\ +	gedit-file-chooser-dialog.c	\ +	gedit-help.c			\ +	gedit-history-entry.c		\ +	gedit-io-error-message-area.c	\ +	gedit-language-manager.c	\ +	gedit-message-bus.c		\ +	gedit-message-type.c		\ +	gedit-message.c			\ +	gedit-object-module.c		\ +	gedit-notebook.c		\ +	gedit-panel.c			\ +	gedit-plugin-info.c		\ +	gedit-plugin.c			\ +	gedit-plugin-loader.c		\ +	gedit-plugin-manager.c		\ +	gedit-plugins-engine.c		\ +	gedit-prefs-manager-app.c	\ +	gedit-prefs-manager.c		\ +	gedit-prefs-manager-private.h	\ +	gedit-print-job.c		\ +	gedit-print-preview.c		\ +	gedit-progress-message-area.c	\ +	gedit-session.c			\ +	gedit-smart-charset-converter.c	\ +	gedit-statusbar.c		\ +	gedit-status-combo-box.c	\ +	gedit-style-scheme-manager.c	\ +	gedit-tab.c 			\ +	gedit-tab-label.c		\ +	gedit-utils.c 			\ +	gedit-view.c 			\ +	gedit-window.c			\ +	gedittextregion.c		\ +	$(NOINST_H_FILES)		\ +	$(INST_H_FILES) + +if !ENABLE_GVFS_METADATA +libgedit_la_SOURCES += gedit-metadata-manager.c  +endif + +gedit-enum-types.h: gedit-enum-types.h.template $(INST_H_FILES) $(GLIB_MKENUMS) +	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template gedit-enum-types.h.template $(INST_H_FILES)) > $@ + +gedit-enum-types.c: gedit-enum-types.c.template $(INST_H_FILES) $(GLIB_MKENUMS) +	$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template gedit-enum-types.c.template $(INST_H_FILES)) > $@ + +gedit-marshal.h: gedit-marshal.list $(GLIB_GENMARSHAL) +	$(AM_V_GEN) $(GLIB_GENMARSHAL) $< --header --prefix=gedit_marshal > $@ + +gedit-marshal.c: gedit-marshal.list $(GLIB_GENMARSHAL) +	$(AM_V_GEN) echo "#include \"gedit-marshal.h\"" > $@ && \ +	$(GLIB_GENMARSHAL) $< --body --prefix=gedit_marshal >> $@ + +uidir = $(datadir)/gedit-2/ui/ +ui_DATA = 				\ +	gedit-ui.xml			\ +	gedit-print-preferences.ui + +EXTRA_DIST = 				\ +	$(ui_DATA)			\ +	gedit-enum-types.h.template	\ +	gedit-enum-types.c.template	\ +	gedit-marshal.list		\ +	gedit.rc + +CLEANFILES = $(BUILT_SOURCES) + +dist-hook: +	cd $(distdir); rm -f $(BUILT_SOURCES) + +install-exec-hook: +if PLATFORM_WIN32 +	$(mkinstalldirs) "$(DESTDIR)$(libdir)" +	$(INSTALL_DATA) libgedit-$(GEDIT_API_VERSION).a "$(DESTDIR)$(libdir)" +else +	rm -f $(DESTDIR)$(bindir)/mate-text-editor +	ln -s gedit $(DESTDIR)$(bindir)/mate-text-editor +endif + +if !OS_WIN32 +BACON_DIR=$(srcdir)/../../libbacon/src/ +BACON_FILES=bacon-message-connection.h bacon-message-connection.c + +regenerate-built-sources: +	BACONFILES="$(BACON_FILES)" BACONDIR="$(BACON_DIR)" $(top_srcdir)/gedit/update-from-bacon.sh +else +BACON_DIR= +endif + +if BUILD_MESSAGE_AREA +libgedit_la_SOURCES += gedit-message-area.c +INST_H_FILES += gedit-message-area.h +endif + +if BUILD_SPINNER +libgedit_la_SOURCES += gedit-spinner.c +NOINST_H_FILES += gedit-spinner.h +endif + +-include $(top_srcdir)/git.mk diff --git a/gedit/bacon-message-connection.c b/gedit/bacon-message-connection.c new file mode 100755 index 00000000..c8000de2 --- /dev/null +++ b/gedit/bacon-message-connection.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2003 Bastien Nocera <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> + +#include "bacon-message-connection.h" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +struct BaconMessageConnection { +	/* A server accepts connections */ +	gboolean is_server; + +	/* The socket path itself */ +	char *path; + +	/* File descriptor of the socket */ +	int fd; +	/* Channel to watch */ +	GIOChannel *chan; +	/* Event id returned by g_io_add_watch() */ +	int conn_id; + +	/* Connections accepted by this connection */ +	GSList *accepted_connections; + +	/* callback */ +	void (*func) (const char *message, gpointer user_data); +	gpointer data; +}; + +static gboolean +test_is_socket (const char *path) +{ +	struct stat s; + +	if (stat (path, &s) == -1) +		return FALSE; + +	if (S_ISSOCK (s.st_mode)) +		return TRUE; + +	return FALSE; +} + +static gboolean +is_owned_by_user_and_socket (const char *path) +{ +	struct stat s; + +	if (stat (path, &s) == -1) +		return FALSE; + +	if (s.st_uid != geteuid ()) +		return FALSE; + +	if ((s.st_mode & S_IFSOCK) != S_IFSOCK) +		return FALSE; +	 +	return TRUE; +} + +static gboolean server_cb (GIOChannel *source, +			   GIOCondition condition, gpointer data); + +static gboolean +setup_connection (BaconMessageConnection *conn) +{ +	g_return_val_if_fail (conn->chan == NULL, FALSE); + +	conn->chan = g_io_channel_unix_new (conn->fd); +	if (!conn->chan) { +		return FALSE; +	} +	g_io_channel_set_line_term (conn->chan, "\n", 1); +	conn->conn_id = g_io_add_watch (conn->chan, G_IO_IN, server_cb, conn); + +	return TRUE; +} + +static void +accept_new_connection (BaconMessageConnection *server_conn) +{ +	BaconMessageConnection *conn; +	int alen; + +	g_return_if_fail (server_conn->is_server); + +	conn = g_new0 (BaconMessageConnection, 1); +	conn->is_server = FALSE; +	conn->func = server_conn->func; +	conn->data = server_conn->data; + +	conn->fd = accept (server_conn->fd, NULL, (guint *)&alen); + +	server_conn->accepted_connections = +		g_slist_prepend (server_conn->accepted_connections, conn); + +	setup_connection (conn); +} + +static gboolean +server_cb (GIOChannel *source, GIOCondition condition, gpointer data) +{ +	BaconMessageConnection *conn = (BaconMessageConnection *)data; +	char *message, *subs, buf; +	int cd, rc, offset; +	gboolean finished; + +	offset = 0; +	if (conn->is_server && conn->fd == g_io_channel_unix_get_fd (source)) { +		accept_new_connection (conn); +		return TRUE; +	} +	message = g_malloc (1); +	cd = conn->fd; +	rc = read (cd, &buf, 1); +	while (rc > 0 && buf != '\n') +	{ +		message = g_realloc (message, rc + offset + 1); +		message[offset] = buf; +		offset = offset + rc; +		rc = read (cd, &buf, 1); +	} +	if (rc <= 0) { +		g_io_channel_shutdown (conn->chan, FALSE, NULL); +		g_io_channel_unref (conn->chan); +		conn->chan = NULL; +		close (conn->fd); +		conn->fd = -1; +		g_free (message); +		conn->conn_id = 0; + +		return FALSE; +	} +	message[offset] = '\0'; + +	subs = message; +	finished = FALSE; + +	while (finished == FALSE && *subs != '\0') +	{ +		if (conn->func != NULL) +			(*conn->func) (subs, conn->data); + +		subs += strlen (subs) + 1; +		if (subs - message >= offset) +			finished = TRUE; +	} + +	g_free (message); + +	return TRUE; +} + +static char * +find_file_with_pattern (const char *dir, const char *pattern) +{ +	GDir *filedir; +	char *found_filename; +	const char *filename; +	GPatternSpec *pat; + +	filedir = g_dir_open (dir, 0, NULL); +	if (filedir == NULL) +		return NULL; + +	pat = g_pattern_spec_new (pattern); +	if (pat == NULL) +	{ +		g_dir_close (filedir); +		return NULL; +	} + +	found_filename = NULL; + +	while ((filename = g_dir_read_name (filedir))) +	{ +		if (g_pattern_match_string (pat, filename)) +		{ +			char *tmp = g_build_filename (dir, filename, NULL); +			if (is_owned_by_user_and_socket (tmp)) +				found_filename = g_strdup (filename); +			g_free (tmp); +		} + +		if (found_filename != NULL) +			break; +	} + +	g_pattern_spec_free (pat); +	g_dir_close (filedir); + +	return found_filename; +} + +static char * +socket_filename (const char *prefix) +{ +	char *pattern, *newfile, *path, *filename; +	const char *tmpdir; + +	pattern = g_strdup_printf ("%s.%s.*", prefix, g_get_user_name ()); +	tmpdir = g_get_tmp_dir (); +	filename = find_file_with_pattern (tmpdir, pattern); +	if (filename == NULL) +	{ +		newfile = g_strdup_printf ("%s.%s.%u", prefix, +				g_get_user_name (), g_random_int ()); +		path = g_build_filename (tmpdir, newfile, NULL); +		g_free (newfile); +	} else { +		path = g_build_filename (tmpdir, filename, NULL); +		g_free (filename); +	} + +	g_free (pattern); +	return path; +} + +static gboolean +try_server (BaconMessageConnection *conn) +{ +	struct sockaddr_un uaddr; + +	uaddr.sun_family = AF_UNIX; +	strncpy (uaddr.sun_path, conn->path, +			MIN (strlen(conn->path)+1, UNIX_PATH_MAX)); +	conn->fd = socket (PF_UNIX, SOCK_STREAM, 0); +	if (bind (conn->fd, (struct sockaddr *) &uaddr, sizeof (uaddr)) == -1) +	{ +		conn->fd = -1; +		return FALSE; +	} +	listen (conn->fd, 5); + +	if (!setup_connection (conn)) +		return FALSE; +	return TRUE; +} + +static gboolean +try_client (BaconMessageConnection *conn) +{ +	struct sockaddr_un uaddr; + +	uaddr.sun_family = AF_UNIX; +	strncpy (uaddr.sun_path, conn->path, +			MIN(strlen(conn->path)+1, UNIX_PATH_MAX)); +	conn->fd = socket (PF_UNIX, SOCK_STREAM, 0); +	if (connect (conn->fd, (struct sockaddr *) &uaddr, +				sizeof (uaddr)) == -1) +	{ +		conn->fd = -1; +		return FALSE; +	} + +	return setup_connection (conn); +} + +BaconMessageConnection * +bacon_message_connection_new (const char *prefix) +{ +	BaconMessageConnection *conn; + +	g_return_val_if_fail (prefix != NULL, NULL); + +	conn = g_new0 (BaconMessageConnection, 1); +	conn->path = socket_filename (prefix); + +	if (test_is_socket (conn->path) == FALSE) +	{ +		if (!try_server (conn)) +		{ +			bacon_message_connection_free (conn); +			return NULL; +		} + +		conn->is_server = TRUE; +		return conn; +	} + +	if (try_client (conn) == FALSE) +	{ +		unlink (conn->path); +		try_server (conn); +		if (conn->fd == -1) +		{ +			bacon_message_connection_free (conn); +			return NULL; +		} + +		conn->is_server = TRUE; +		return conn; +	} + +	conn->is_server = FALSE; +	return conn; +} + +void +bacon_message_connection_free (BaconMessageConnection *conn) +{ +	GSList *child_conn; + +	g_return_if_fail (conn != NULL); +	/* Only servers can accept other connections */ +	g_return_if_fail (conn->is_server != FALSE || +			  conn->accepted_connections == NULL); + +	child_conn = conn->accepted_connections; +	while (child_conn != NULL) { +		bacon_message_connection_free (child_conn->data); +		child_conn = g_slist_next (child_conn); +	} +	g_slist_free (conn->accepted_connections); + +	if (conn->conn_id) { +		g_source_remove (conn->conn_id); +		conn->conn_id = 0; +	} +	if (conn->chan) { +		g_io_channel_shutdown (conn->chan, FALSE, NULL); +		g_io_channel_unref (conn->chan); +	} + +	if (conn->is_server != FALSE) { +		unlink (conn->path); +	} +	if (conn->fd != -1) { +		close (conn->fd); +	} + +	g_free (conn->path); +	g_free (conn); +} + +void +bacon_message_connection_set_callback (BaconMessageConnection *conn, +				       BaconMessageReceivedFunc func, +				       gpointer user_data) +{ +	g_return_if_fail (conn != NULL); + +	conn->func = func; +	conn->data = user_data; +} + +void +bacon_message_connection_send (BaconMessageConnection *conn, +			       const char *message) +{ +	g_return_if_fail (conn != NULL); +	g_return_if_fail (message != NULL); + +	g_io_channel_write_chars (conn->chan, message, strlen (message), +				  NULL, NULL); +	g_io_channel_write_chars (conn->chan, "\n", 1, NULL, NULL); +	g_io_channel_flush (conn->chan, NULL); +} + +gboolean +bacon_message_connection_get_is_server (BaconMessageConnection *conn) +{ +	g_return_val_if_fail (conn != NULL, FALSE); + +	return conn->is_server; +} + diff --git a/gedit/bacon-message-connection.h b/gedit/bacon-message-connection.h new file mode 100755 index 00000000..aac7a2d1 --- /dev/null +++ b/gedit/bacon-message-connection.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003 Bastien Nocera <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef BACON_MESSAGE_CONNECTION_H +#define BACON_MESSAGE_CONNECTION_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef void (*BaconMessageReceivedFunc) (const char *message, +					  gpointer user_data); + +typedef struct BaconMessageConnection BaconMessageConnection; + +BaconMessageConnection *bacon_message_connection_new	(const char *prefix); +void bacon_message_connection_free			(BaconMessageConnection *conn); +void bacon_message_connection_set_callback		(BaconMessageConnection *conn, +							 BaconMessageReceivedFunc func, +							 gpointer user_data); +void bacon_message_connection_send			(BaconMessageConnection *conn, +							 const char *message); +gboolean bacon_message_connection_get_is_server		(BaconMessageConnection *conn); + +G_END_DECLS + +#endif /* BACON_MESSAGE_CONNECTION_H */ diff --git a/gedit/dialogs/Makefile.am b/gedit/dialogs/Makefile.am new file mode 100755 index 00000000..c0762571 --- /dev/null +++ b/gedit/dialogs/Makefile.am @@ -0,0 +1,31 @@ +uidir = $(datadir)/gedit-2/ui/ + +INCLUDES = 							\ +	-I$(top_srcdir)						\ +	-I$(top_builddir)					\ +	-I$(top_srcdir)/gedit 					\ +	-I$(top_builddir)/gedit					\ +	$(GEDIT_CFLAGS) 					\ +	$(WARN_CFLAGS)						\ +	$(DISABLE_DEPRECATED_CFLAGS) + +noinst_LTLIBRARIES = libdialogs.la + +libdialogs_la_SOURCES = 			\ +	gedit-preferences-dialog.h		\ +	gedit-preferences-dialog.c		\ +	gedit-close-confirmation-dialog.c 	\ +	gedit-close-confirmation-dialog.h 	\ +	gedit-encodings-dialog.c		\ +	gedit-encodings-dialog.h		\ +	gedit-search-dialog.h			\ +	gedit-search-dialog.c + +ui_DATA =					\ +	gedit-encodings-dialog.ui		\ +	gedit-preferences-dialog.ui		\ +	gedit-search-dialog.ui + +EXTRA_DIST = $(ui_DATA)  + +-include $(top_srcdir)/git.mk diff --git a/gedit/dialogs/gedit-close-confirmation-dialog.c b/gedit/dialogs/gedit-close-confirmation-dialog.c new file mode 100755 index 00000000..36cbf117 --- /dev/null +++ b/gedit/dialogs/gedit-close-confirmation-dialog.c @@ -0,0 +1,790 @@ +/* + * gedit-close-confirmation-dialog.c + * This file is part of gedit + * + * Copyright (C) 2004-2005 MATE Foundation  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2004-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "gedit-close-confirmation-dialog.h" +#include <gedit/gedit-app.h> +#include <gedit/gedit-utils.h> +#include <gedit/gedit-window.h> + + +/* Properties */ +enum  +{ +	PROP_0,	 +	PROP_UNSAVED_DOCUMENTS, +	PROP_LOGOUT_MODE +}; + +/* Mode */ +enum +{ +	SINGLE_DOC_MODE, +	MULTIPLE_DOCS_MODE +}; + +/* Columns */ +enum +{ +	SAVE_COLUMN, +	NAME_COLUMN, +	DOC_COLUMN, /* a handy pointer to the document */ +	N_COLUMNS +}; + +struct _GeditCloseConfirmationDialogPrivate  +{ +	gboolean     logout_mode; + +	GList       *unsaved_documents; +	 +	GList       *selected_documents; + +	GtkTreeModel *list_store; +	 +	gboolean     disable_save_to_disk; +}; + +#define GEDIT_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ +							GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG, \ +							GeditCloseConfirmationDialogPrivate)) + +#define GET_MODE(priv) (((priv->unsaved_documents != NULL) && \ +			 (priv->unsaved_documents->next == NULL)) ? \ +			  SINGLE_DOC_MODE : MULTIPLE_DOCS_MODE) + +G_DEFINE_TYPE(GeditCloseConfirmationDialog, gedit_close_confirmation_dialog, GTK_TYPE_DIALOG) + +static void 	 set_unsaved_document 		(GeditCloseConfirmationDialog *dlg, +						 const GList                  *list); + +static GList 	*get_selected_docs 		(GtkTreeModel                 *store); + +/*  Since we connect in the costructor we are sure this handler will be called  + *  before the user ones + */ +static void +response_cb (GeditCloseConfirmationDialog *dlg, +             gint                          response_id, +             gpointer                      data) +{ +	GeditCloseConfirmationDialogPrivate *priv; + +	g_return_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg)); + +	priv = dlg->priv; +	 +	if (priv->selected_documents != NULL) +		g_list_free (priv->selected_documents); + +	if (response_id == GTK_RESPONSE_YES) +	{ +		if (GET_MODE (priv) == SINGLE_DOC_MODE) +		{ +			priv->selected_documents =  +				g_list_copy (priv->unsaved_documents); +		} +		else +		{ +			g_return_if_fail (priv->list_store); + +			priv->selected_documents = +				get_selected_docs (priv->list_store); +		} +	} +	else +		priv->selected_documents = NULL; +} + +static void +set_logout_mode (GeditCloseConfirmationDialog *dlg, +		 gboolean                      logout_mode) +{ +	dlg->priv->logout_mode = logout_mode; +	 +	if (logout_mode) +	{ +		gtk_dialog_add_button (GTK_DIALOG (dlg), +				       _("Log Out _without Saving"), +				       GTK_RESPONSE_NO); + +		gedit_dialog_add_button (GTK_DIALOG (dlg), +					 _("_Cancel Logout"), +					 GTK_STOCK_CANCEL, +					 GTK_RESPONSE_CANCEL); +	} +	else +	{ +		gtk_dialog_add_button (GTK_DIALOG (dlg), +				       _("Close _without Saving"), +				       GTK_RESPONSE_NO); + +		gtk_dialog_add_button (GTK_DIALOG (dlg), +				       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); +	} +	 +	if (dlg->priv->disable_save_to_disk) +	{ +		gtk_dialog_set_default_response	(GTK_DIALOG (dlg), +						 GTK_RESPONSE_NO); +	} +	else +	{ +		const gchar *stock_id = GTK_STOCK_SAVE; +		 +		if (GET_MODE (dlg->priv) == SINGLE_DOC_MODE) +		{ +			GeditDocument *doc; +			 +			doc = GEDIT_DOCUMENT (dlg->priv->unsaved_documents->data); +			 +			if (gedit_document_get_readonly (doc) ||  +			    gedit_document_is_untitled (doc)) +				stock_id = GTK_STOCK_SAVE_AS; +		} + +		gtk_dialog_add_button (GTK_DIALOG (dlg), +				       stock_id,  +				       GTK_RESPONSE_YES); + +		gtk_dialog_set_default_response	(GTK_DIALOG (dlg),  +						 GTK_RESPONSE_YES); +	} +} + +static void  +gedit_close_confirmation_dialog_init (GeditCloseConfirmationDialog *dlg) +{ +	AtkObject *atk_obj; + +	dlg->priv = GEDIT_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE (dlg); + +	dlg->priv->disable_save_to_disk =  +			gedit_app_get_lockdown (gedit_app_get_default ())  +			& GEDIT_LOCKDOWN_SAVE_TO_DISK; + +	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);		 +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			     14); +	gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); +	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); +	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), TRUE); +	 +	gtk_window_set_title (GTK_WINDOW (dlg), ""); + +	gtk_window_set_modal (GTK_WINDOW (dlg), TRUE); +	gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE); + +	atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dlg)); +	atk_object_set_role (atk_obj, ATK_ROLE_ALERT); +	atk_object_set_name (atk_obj, _("Question")); +	 +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (response_cb), +			  NULL); +} + +static void  +gedit_close_confirmation_dialog_finalize (GObject *object) +{ +	GeditCloseConfirmationDialogPrivate *priv; + +	priv = GEDIT_CLOSE_CONFIRMATION_DIALOG (object)->priv; + +	if (priv->unsaved_documents != NULL) +		g_list_free (priv->unsaved_documents); + +	if (priv->selected_documents != NULL) +		g_list_free (priv->selected_documents); + +	/* Call the parent's destructor */ +	G_OBJECT_CLASS (gedit_close_confirmation_dialog_parent_class)->finalize (object); +} + +static void +gedit_close_confirmation_dialog_set_property (GObject      *object,  +					      guint         prop_id,  +					      const GValue *value,  +					      GParamSpec   *pspec) +{ +	GeditCloseConfirmationDialog *dlg; + +	dlg = GEDIT_CLOSE_CONFIRMATION_DIALOG (object); + +	switch (prop_id) +	{ +		case PROP_UNSAVED_DOCUMENTS: +			set_unsaved_document (dlg, g_value_get_pointer (value)); +			break; +			 +		case PROP_LOGOUT_MODE: +			set_logout_mode (dlg, g_value_get_boolean (value)); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_close_confirmation_dialog_get_property (GObject    *object,  +					      guint       prop_id,  +					      GValue     *value,  +					      GParamSpec *pspec) +{ +	GeditCloseConfirmationDialogPrivate *priv; + +	priv = GEDIT_CLOSE_CONFIRMATION_DIALOG (object)->priv; + +	switch( prop_id ) +	{ +		case PROP_UNSAVED_DOCUMENTS: +			g_value_set_pointer (value, priv->unsaved_documents); +			break; + +		case PROP_LOGOUT_MODE: +			g_value_set_boolean (value, priv->logout_mode); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void  +gedit_close_confirmation_dialog_class_init (GeditCloseConfirmationDialogClass *klass) +{ +	GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + +	gobject_class->set_property = gedit_close_confirmation_dialog_set_property; +	gobject_class->get_property = gedit_close_confirmation_dialog_get_property; +	gobject_class->finalize = gedit_close_confirmation_dialog_finalize; + +	g_type_class_add_private (klass, sizeof (GeditCloseConfirmationDialogPrivate)); + +	g_object_class_install_property (gobject_class, +					 PROP_UNSAVED_DOCUMENTS, +					 g_param_spec_pointer ("unsaved_documents", +						 	       "Unsaved Documents", +							       "List of Unsaved Documents", +							       (G_PARAM_READWRITE |  +							        G_PARAM_CONSTRUCT_ONLY))); + +	g_object_class_install_property (gobject_class, +					 PROP_LOGOUT_MODE, +					 g_param_spec_boolean ("logout_mode", +						 	       "Logout Mode", +							       "Whether the dialog is in logout mode", +							       FALSE, +							       (G_PARAM_READWRITE |  +							        G_PARAM_CONSTRUCT_ONLY)));							         +} + +static GList * +get_selected_docs (GtkTreeModel *store) +{ +	GList      *list; +	gboolean     valid; +	GtkTreeIter  iter; + +	list = NULL; +	valid = gtk_tree_model_get_iter_first (store, &iter); + +	while (valid) +	{ +		gboolean       to_save; +		GeditDocument *doc; + +		gtk_tree_model_get (store, &iter,  +				    SAVE_COLUMN, &to_save, +				    DOC_COLUMN, &doc, +				    -1); +		if (to_save) +			list = g_list_prepend (list, doc); + +		valid = gtk_tree_model_iter_next (store, &iter); +	} + +	list = g_list_reverse (list); + +	return list; +} + +GList * +gedit_close_confirmation_dialog_get_selected_documents (GeditCloseConfirmationDialog *dlg) +{ +	g_return_val_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + +	return g_list_copy (dlg->priv->selected_documents); +} + +GtkWidget * +gedit_close_confirmation_dialog_new (GtkWindow *parent,  +				     GList     *unsaved_documents, +				     gboolean   logout_mode) +{ +	GtkWidget *dlg; +	g_return_val_if_fail (unsaved_documents != NULL, NULL); + +	dlg = GTK_WIDGET (g_object_new (GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG, +				        "unsaved_documents", unsaved_documents, +				        "logout_mode", logout_mode, +				        NULL)); +	g_return_val_if_fail (dlg != NULL, NULL); + +	if (parent != NULL) +	{ +		gtk_window_group_add_window (gedit_window_get_group (GEDIT_WINDOW (parent)), +					     GTK_WINDOW (dlg)); +		 +		gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);					      +	} + +	return dlg; +} + +GtkWidget * +gedit_close_confirmation_dialog_new_single (GtkWindow     *parent,  +					    GeditDocument *doc, +					    gboolean       logout_mode) +{ +	GtkWidget *dlg; +	GList *unsaved_documents; +	g_return_val_if_fail (doc != NULL, NULL); +	 +	unsaved_documents = g_list_prepend (NULL, doc); + +	dlg = gedit_close_confirmation_dialog_new (parent,  +						   unsaved_documents, +						   logout_mode); +	 +	g_list_free (unsaved_documents); + +	return dlg; +} + +static gchar * +get_text_secondary_label (GeditDocument *doc) +{ +	glong  seconds; +	gchar *secondary_msg; +	 +	seconds = MAX (1, _gedit_document_get_seconds_since_last_save_or_load (doc)); + +	if (seconds < 55)	 +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("If you don't save, changes from the last %ld second " +					    	  "will be permanently lost.", +						  "If you don't save, changes from the last %ld seconds " +					    	  "will be permanently lost.", +						  seconds), +					seconds); +	} +	else if (seconds < 75) /* 55 <= seconds < 75 */ +	{ +		secondary_msg = g_strdup (_("If you don't save, changes from the last minute " +					    "will be permanently lost.")); +	} +	else if (seconds < 110) /* 75 <= seconds < 110 */ +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("If you don't save, changes from the last minute and %ld " +						  "second will be permanently lost.", +						  "If you don't save, changes from the last minute and %ld " +						  "seconds will be permanently lost.", +						  seconds - 60 ), +					seconds - 60); +	} +	else if (seconds < 3600) +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("If you don't save, changes from the last %ld minute " +					    	  "will be permanently lost.", +						  "If you don't save, changes from the last %ld minutes " +					    	  "will be permanently lost.", +						  seconds / 60), +					seconds / 60); +	} +	else if (seconds < 7200) +	{ +		gint minutes; +		seconds -= 3600; + +		minutes = seconds / 60; +		if (minutes < 5) +		{ +			secondary_msg = g_strdup (_("If you don't save, changes from the last hour " +						    "will be permanently lost.")); +		} +		else +		{ +			secondary_msg = g_strdup_printf ( +					ngettext ("If you don't save, changes from the last hour and %d " +						  "minute will be permanently lost.", +						  "If you don't save, changes from the last hour and %d " +						  "minutes will be permanently lost.", +						  minutes), +					minutes); +		} +	} +	else +	{ +		gint hours; + +		hours = seconds / 3600; + +		secondary_msg = g_strdup_printf ( +					ngettext ("If you don't save, changes from the last %d hour " +					    	  "will be permanently lost.", +						  "If you don't save, changes from the last %d hours " +					    	  "will be permanently lost.", +						  hours), +					hours); +	} + +	return secondary_msg; +} + +static void +build_single_doc_dialog (GeditCloseConfirmationDialog *dlg) +{ +	GtkWidget     *hbox; +	GtkWidget     *vbox; +	GtkWidget     *primary_label; +	GtkWidget     *secondary_label; +	GtkWidget     *image; +	GeditDocument *doc; +	gchar         *doc_name; +	gchar         *str; +	gchar         *markup_str; + +	g_return_if_fail (dlg->priv->unsaved_documents->data != NULL); +	doc = GEDIT_DOCUMENT (dlg->priv->unsaved_documents->data); + +	/* Image */ +	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,  +					  GTK_ICON_SIZE_DIALOG); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + +	/* Primary label */ +	primary_label = gtk_label_new (NULL); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	doc_name = gedit_document_get_short_name_for_display (doc); + +	if (dlg->priv->disable_save_to_disk) +	{ +		str = g_markup_printf_escaped (_("Changes to document \"%s\" will be permanently lost."), +					       doc_name); +	} +	else +	{ +		str = g_markup_printf_escaped (_("Save changes to document \"%s\" before closing?"), +					       doc_name); +	} + +	g_free (doc_name); + +	markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); +	g_free (str); + +	gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); +	g_free (markup_str); + +	/* Secondary label */ +	if (dlg->priv->disable_save_to_disk) +		str = g_strdup (_("Saving has been disabled by the system administrator."));	 +	else +		str = get_text_secondary_label (doc); +	secondary_label = gtk_label_new (str); +	g_free (str); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + +	hbox = gtk_hbox_new (FALSE, 12); +	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + +	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + +	vbox = gtk_vbox_new (FALSE, 12); +	 +	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); +		       +	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, FALSE, FALSE, 0); + +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			    hbox,  +	                    FALSE,  +			    FALSE,  +			    0); + +	gtk_widget_show_all (hbox); +} + +static void +populate_model (GtkTreeModel *store, GList *docs) +{ +	GtkTreeIter iter; + +	while (docs != NULL) +	{ +		GeditDocument *doc; +		gchar *name; + +		doc = GEDIT_DOCUMENT (docs->data); + +		name = gedit_document_get_short_name_for_display (doc); + +		gtk_list_store_append (GTK_LIST_STORE (store), &iter); +		gtk_list_store_set (GTK_LIST_STORE (store), &iter, +				    SAVE_COLUMN, TRUE, +				    NAME_COLUMN, name, +				    DOC_COLUMN, doc, +			            -1); + +		g_free (name); + +		docs = g_list_next (docs); +	} +} + +static void +save_toggled (GtkCellRendererToggle *renderer, gchar *path_str, GtkTreeModel *store) +{ +	GtkTreePath *path = gtk_tree_path_new_from_string (path_str); +	GtkTreeIter iter; +	gboolean active; + +	gtk_tree_model_get_iter (store, &iter, path); +	gtk_tree_model_get (store, &iter, SAVE_COLUMN, &active, -1); + +	active ^= 1; + +	gtk_list_store_set (GTK_LIST_STORE (store), &iter, +			    SAVE_COLUMN, active, -1); + +	gtk_tree_path_free (path); +} + +static GtkWidget * +create_treeview (GeditCloseConfirmationDialogPrivate *priv) +{ +	GtkListStore *store; +	GtkWidget *treeview; +	GtkCellRenderer *renderer; +	GtkTreeViewColumn *column; + +	treeview = gtk_tree_view_new (); +	gtk_widget_set_size_request (treeview, 260, 120); +	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); +	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), FALSE); + +	/* Create and populate the model */ +	store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); +	populate_model (GTK_TREE_MODEL (store), priv->unsaved_documents); + +	/* Set model to the treeview */ +	gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store)); +	g_object_unref (store); + +	priv->list_store = GTK_TREE_MODEL (store); +	 +	/* Add columns */ +	if (!priv->disable_save_to_disk) +	{ +		renderer = gtk_cell_renderer_toggle_new (); +		g_signal_connect (renderer, "toggled", +				  G_CALLBACK (save_toggled), store); + +		column = gtk_tree_view_column_new_with_attributes ("Save?", +								   renderer, +								   "active", +								   SAVE_COLUMN, +								   NULL); +		gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); +	} + +	renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes ("Name", +							   renderer, +							   "text", +							   NAME_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + +	return treeview; +} + +static void +build_multiple_docs_dialog (GeditCloseConfirmationDialog *dlg) +{ +	GeditCloseConfirmationDialogPrivate *priv; +	GtkWidget *hbox; +	GtkWidget *image; +	GtkWidget *vbox; +	GtkWidget *primary_label; +	GtkWidget *vbox2; +	GtkWidget *select_label; +	GtkWidget *scrolledwindow; +	GtkWidget *treeview; +	GtkWidget *secondary_label; +	gchar     *str; +	gchar     *markup_str; + +	priv = dlg->priv; + +	hbox = gtk_hbox_new (FALSE, 12); +	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			    hbox, TRUE, TRUE, 0); + +	/* Image */ +	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,  +					  GTK_ICON_SIZE_DIALOG); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); +	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + +	vbox = gtk_vbox_new (FALSE, 12); +	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + +	/* Primary label */ +	primary_label = gtk_label_new (NULL); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0.0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	if (priv->disable_save_to_disk) +		str = g_strdup_printf ( +				ngettext ("Changes to %d document will be permanently lost.", +					  "Changes to %d documents will be permanently lost.", +					  g_list_length (priv->unsaved_documents)), +				g_list_length (priv->unsaved_documents)); +	else +		str = g_strdup_printf ( +				ngettext ("There is %d document with unsaved changes. " +					  "Save changes before closing?", +					  "There are %d documents with unsaved changes. " +					  "Save changes before closing?", +					  g_list_length (priv->unsaved_documents)), +				g_list_length (priv->unsaved_documents)); + +	markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL); +	g_free (str); +	 +	gtk_label_set_markup (GTK_LABEL (primary_label), markup_str); +	g_free (markup_str); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0); +	 +	vbox2 = gtk_vbox_new (FALSE, 8); +	gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + +	if (priv->disable_save_to_disk) +		select_label = gtk_label_new_with_mnemonic (_("Docum_ents with unsaved changes:")); +	else +		select_label = gtk_label_new_with_mnemonic (_("S_elect the documents you want to save:")); + +	gtk_box_pack_start (GTK_BOX (vbox2), select_label, FALSE, FALSE, 0); +	gtk_label_set_line_wrap (GTK_LABEL (select_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (select_label), 0.0, 0.5); + +	scrolledwindow = gtk_scrolled_window_new (NULL, NULL); +	gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow, TRUE, TRUE, 0); +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),  +					GTK_POLICY_AUTOMATIC,  +					GTK_POLICY_AUTOMATIC); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),  +					     GTK_SHADOW_IN); + +	treeview = create_treeview (priv); +	gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview); + +	/* Secondary label */ +	if (priv->disable_save_to_disk) +		secondary_label = gtk_label_new (_("Saving has been disabled by the system administrator.")); +	else +		secondary_label = gtk_label_new (_("If you don't save, " +						   "all your changes will be permanently lost.")); + +	gtk_box_pack_start (GTK_BOX (vbox2), secondary_label, FALSE, FALSE, 0); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (select_label), treeview); + +	gtk_widget_show_all (hbox);	 +} + +static void +set_unsaved_document (GeditCloseConfirmationDialog *dlg, +		      const GList                  *list) +{ +	GeditCloseConfirmationDialogPrivate *priv; + +	g_return_if_fail (list != NULL);	 + +	priv = dlg->priv; +	g_return_if_fail (priv->unsaved_documents == NULL); + +	priv->unsaved_documents = g_list_copy ((GList *)list); + +	if (GET_MODE (priv) == SINGLE_DOC_MODE) +	{ +		build_single_doc_dialog (dlg); +	} +	else +	{ +		build_multiple_docs_dialog (dlg); +	}	 +} + +const GList * +gedit_close_confirmation_dialog_get_unsaved_documents (GeditCloseConfirmationDialog *dlg) +{ +	g_return_val_if_fail (GEDIT_IS_CLOSE_CONFIRMATION_DIALOG (dlg), NULL); + +	return dlg->priv->unsaved_documents; +} diff --git a/gedit/dialogs/gedit-close-confirmation-dialog.h b/gedit/dialogs/gedit-close-confirmation-dialog.h new file mode 100755 index 00000000..887fc1b4 --- /dev/null +++ b/gedit/dialogs/gedit-close-confirmation-dialog.h @@ -0,0 +1,75 @@ +/* + * gedit-close-confirmation-dialog.h + * This file is part of gedit + * + * Copyright (C) 2004-2005 MATE Foundation  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2004-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef __GEDIT_CLOSE_CONFIRMATION_DIALOG_H__ +#define __GEDIT_CLOSE_CONFIRMATION_DIALOG_H__ + +#include <glib.h> +#include <gtk/gtk.h> + +#include <gedit/gedit-document.h> + +#define GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG		(gedit_close_confirmation_dialog_get_type ()) +#define GEDIT_CLOSE_CONFIRMATION_DIALOG(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG, GeditCloseConfirmationDialog)) +#define GEDIT_CLOSE_CONFIRMATION_DIALOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG, GeditCloseConfirmationDialogClass)) +#define GEDIT_IS_CLOSE_CONFIRMATION_DIALOG(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define GEDIT_IS_CLOSE_CONFIRMATION_DIALOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG)) +#define GEDIT_CLOSE_CONFIRMATION_DIALOG_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),GEDIT_TYPE_CLOSE_CONFIRMATION_DIALOG, GeditCloseConfirmationDialogClass)) + +typedef struct _GeditCloseConfirmationDialog 		GeditCloseConfirmationDialog; +typedef struct _GeditCloseConfirmationDialogClass 	GeditCloseConfirmationDialogClass; +typedef struct _GeditCloseConfirmationDialogPrivate 	GeditCloseConfirmationDialogPrivate; + +struct _GeditCloseConfirmationDialog  +{ +	GtkDialog parent; + +	/*< private > */ +	GeditCloseConfirmationDialogPrivate *priv; +}; + +struct _GeditCloseConfirmationDialogClass  +{ +	GtkDialogClass parent_class; +}; + +GType 		 gedit_close_confirmation_dialog_get_type		(void) G_GNUC_CONST; + +GtkWidget	*gedit_close_confirmation_dialog_new			(GtkWindow     *parent, +									 GList         *unsaved_documents, +									 gboolean       logout_mode); +GtkWidget 	*gedit_close_confirmation_dialog_new_single 		(GtkWindow     *parent,  +									 GeditDocument *doc, + 									 gboolean       logout_mode); + +const GList	*gedit_close_confirmation_dialog_get_unsaved_documents  (GeditCloseConfirmationDialog *dlg); + +GList		*gedit_close_confirmation_dialog_get_selected_documents	(GeditCloseConfirmationDialog *dlg); + +#endif /* __GEDIT_CLOSE_CONFIRMATION_DIALOG_H__ */ + diff --git a/gedit/dialogs/gedit-encodings-dialog.c b/gedit/dialogs/gedit-encodings-dialog.c new file mode 100755 index 00000000..b4800805 --- /dev/null +++ b/gedit/dialogs/gedit-encodings-dialog.c @@ -0,0 +1,499 @@ +/* + * gedit-encodings-dialog.c + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-encodings-dialog.h" +#include "gedit-encodings.h" +#include "gedit-prefs-manager.h" +#include "gedit-utils.h" +#include "gedit-debug.h" +#include "gedit-help.h" +#include "gedit-dirs.h" + +#define GEDIT_ENCODINGS_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +						   GEDIT_TYPE_ENCODINGS_DIALOG,           \ +						   GeditEncodingsDialogPrivate)) + +struct _GeditEncodingsDialogPrivate +{ +	GtkListStore	*available_liststore; +	GtkListStore	*displayed_liststore; +	GtkWidget	*available_treeview; +	GtkWidget	*displayed_treeview; +	GtkWidget	*add_button; +	GtkWidget	*remove_button; + +	GSList		*show_in_menu_list; +}; + +G_DEFINE_TYPE(GeditEncodingsDialog, gedit_encodings_dialog, GTK_TYPE_DIALOG) + +static void +gedit_encodings_dialog_finalize (GObject *object) +{ +	GeditEncodingsDialogPrivate *priv = GEDIT_ENCODINGS_DIALOG (object)->priv; + +	g_slist_free (priv->show_in_menu_list); + +	G_OBJECT_CLASS (gedit_encodings_dialog_parent_class)->finalize (object); +} + +static void +gedit_encodings_dialog_class_init (GeditEncodingsDialogClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_encodings_dialog_finalize; + +	g_type_class_add_private (object_class, sizeof (GeditEncodingsDialogPrivate)); +} + +enum { +	COLUMN_NAME, +	COLUMN_CHARSET, +	N_COLUMNS +}; + +static void +count_selected_items_func (GtkTreeModel *model, +			   GtkTreePath  *path, +			   GtkTreeIter  *iter,  +			   gpointer      data) +{ +	int *count = data; + +	*count += 1; +} + +static void +available_selection_changed_callback (GtkTreeSelection     *selection, +				      GeditEncodingsDialog *dialogs) +{ +	int count; + +	count = 0; +	gtk_tree_selection_selected_foreach (selection, +					     count_selected_items_func, +					     &count); + +	gtk_widget_set_sensitive (dialogs->priv->add_button, count > 0); +} + +static void +displayed_selection_changed_callback (GtkTreeSelection     *selection, +				      GeditEncodingsDialog *dialogs) +{ +	int count; + +	count = 0; +	gtk_tree_selection_selected_foreach (selection, +					     count_selected_items_func, +					     &count); + +	gtk_widget_set_sensitive (dialogs->priv->remove_button, count > 0); +} + +static void +get_selected_encodings_func (GtkTreeModel *model, +			     GtkTreePath  *path, +			     GtkTreeIter  *iter,  +			     gpointer      data) +{ +	GSList **list = data; +	gchar *charset; +	const GeditEncoding *enc; + +	charset = NULL; +	gtk_tree_model_get (model, iter, COLUMN_CHARSET, &charset, -1); + +	enc = gedit_encoding_get_from_charset (charset); +	g_free (charset); + +	*list = g_slist_prepend (*list, (gpointer)enc); +} + +static void +update_shown_in_menu_tree_model (GtkListStore *store, +				 GSList       *list) +{ +	GtkTreeIter iter; + +	gtk_list_store_clear (store); + +	while (list != NULL) +	{ +		const GeditEncoding *enc; + +		enc = (const GeditEncoding*) list->data; + +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store, &iter, +				    COLUMN_CHARSET, +				    gedit_encoding_get_charset (enc), +				    COLUMN_NAME, +				    gedit_encoding_get_name (enc), -1); + +		list = g_slist_next (list); +	} +} + +static void +add_button_clicked_callback (GtkWidget            *button, +			     GeditEncodingsDialog *dialog) +{ +	GtkTreeSelection *selection; +	GSList *encodings; +	GSList *tmp; + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->available_treeview)); + +	encodings = NULL; +	gtk_tree_selection_selected_foreach (selection, +					     get_selected_encodings_func, +					     &encodings); + +	tmp = encodings; +	while (tmp != NULL) +	{ +		if (g_slist_find (dialog->priv->show_in_menu_list, tmp->data) == NULL) +			dialog->priv->show_in_menu_list = g_slist_prepend (dialog->priv->show_in_menu_list, +									   tmp->data); + +		tmp = g_slist_next (tmp); +	} + +	g_slist_free (encodings); + +	update_shown_in_menu_tree_model (GTK_LIST_STORE (dialog->priv->displayed_liststore), +					 dialog->priv->show_in_menu_list); +} + +static void +remove_button_clicked_callback (GtkWidget            *button, +				GeditEncodingsDialog *dialog) +{ +	GtkTreeSelection *selection; +	GSList *encodings; +	GSList *tmp; + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->displayed_treeview)); + +	encodings = NULL; +	gtk_tree_selection_selected_foreach (selection, +					     get_selected_encodings_func, +					     &encodings); + +	tmp = encodings; +	while (tmp != NULL)  +	{ +		dialog->priv->show_in_menu_list = g_slist_remove (dialog->priv->show_in_menu_list, +								  tmp->data); + +		tmp = g_slist_next (tmp); +	} + +	g_slist_free (encodings); + +	update_shown_in_menu_tree_model (GTK_LIST_STORE (dialog->priv->displayed_liststore), +					 dialog->priv->show_in_menu_list); +} + +static void +init_shown_in_menu_tree_model (GeditEncodingsDialog *dialog) +{ +	GtkTreeIter iter; +	GSList *list, *tmp; + +	/* add data to the list store */ +	list = gedit_prefs_manager_get_shown_in_menu_encodings (); + +	tmp = list; + +	while (tmp != NULL) +	{ +		const GeditEncoding *enc; + +		enc = (const GeditEncoding *) tmp->data; + +		dialog->priv->show_in_menu_list = g_slist_prepend (dialog->priv->show_in_menu_list, +								   tmp->data); + +		gtk_list_store_append (dialog->priv->displayed_liststore, +				       &iter); +		gtk_list_store_set (dialog->priv->displayed_liststore, +				    &iter, +				    COLUMN_CHARSET, +				    gedit_encoding_get_charset (enc), +				    COLUMN_NAME, +				    gedit_encoding_get_name (enc), -1); + +		tmp = g_slist_next (tmp); +	} + +	g_slist_free (list); +} + +static void  +response_handler (GtkDialog            *dialog, +		  gint                  response_id, +                  GeditEncodingsDialog *dlg) +{ +	if (response_id == GTK_RESPONSE_HELP) +	{ +		gedit_help_display (GTK_WINDOW (dialog), "gedit", NULL); +		g_signal_stop_emission_by_name (dialog, "response"); +		return; +	} + +	if (response_id == GTK_RESPONSE_OK) +	{ +		g_return_if_fail (gedit_prefs_manager_shown_in_menu_encodings_can_set ()); +		gedit_prefs_manager_set_shown_in_menu_encodings (dlg->priv->show_in_menu_list); +	} +} + +static void +gedit_encodings_dialog_init (GeditEncodingsDialog *dlg) +{ +	GtkWidget *content; +	GtkCellRenderer *cell_renderer; +	GtkTreeModel *sort_model; +	GtkTreeViewColumn *column; +	GtkTreeIter parent_iter; +	GtkTreeSelection *selection; +	const GeditEncoding *enc; +	GtkWidget *error_widget; +	int i; +	gboolean ret; +	gchar *file; +	gchar *root_objects[] = { +		"encodings-dialog-contents", +		NULL +	}; + +	dlg->priv = GEDIT_ENCODINGS_DIALOG_GET_PRIVATE (dlg); +	 +	gtk_dialog_add_buttons (GTK_DIALOG (dlg), +				GTK_STOCK_CANCEL,  +				GTK_RESPONSE_CANCEL, +				GTK_STOCK_OK, +				GTK_RESPONSE_OK, +				GTK_STOCK_HELP, +				GTK_RESPONSE_HELP, +				NULL); + +	gtk_window_set_title (GTK_WINDOW (dlg), _("Character Encodings")); +	gtk_window_set_default_size (GTK_WINDOW (dlg), 650, 400); +	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); +	 +	/* HIG defaults */ +	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			     2); /* 2 * 5 + 2 = 12 */ +	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), +					5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), 6); + +	gtk_dialog_set_default_response (GTK_DIALOG (dlg), +					 GTK_RESPONSE_OK); + +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (response_handler), +			  dlg); + +	file = gedit_dirs_get_ui_file ("gedit-encodings-dialog.ui"); +	ret = gedit_utils_get_ui_objects (file, +					  root_objects, +					  &error_widget, +					  "encodings-dialog-contents", &content, +					  "add-button", &dlg->priv->add_button, +					  "remove-button", &dlg->priv->remove_button, +					  "available-treeview", &dlg->priv->available_treeview, +					  "displayed-treeview", &dlg->priv->displayed_treeview, +					  NULL); +	g_free (file); + +	if (!ret) +	{ +		gtk_widget_show (error_widget); + +		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +		                    error_widget, +		                    TRUE, TRUE, 0); +		gtk_container_set_border_width (GTK_CONTAINER (error_widget), 5);			      + +		return; +	} + +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			    content, TRUE, TRUE, 0); +	g_object_unref (content); +	gtk_container_set_border_width (GTK_CONTAINER (content), 5);			      + +	g_signal_connect (dlg->priv->add_button, +			  "clicked", +			  G_CALLBACK (add_button_clicked_callback), +			  dlg); +	g_signal_connect (dlg->priv->remove_button, +			  "clicked", +			  G_CALLBACK (remove_button_clicked_callback), +			  dlg); + +	/* Tree view of available encodings */ +	dlg->priv->available_liststore = gtk_list_store_new (N_COLUMNS, +							     G_TYPE_STRING, +							     G_TYPE_STRING); + +	cell_renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes (_("_Description"), +							   cell_renderer, +							   "text", COLUMN_NAME, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->available_treeview), +				     column); +	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + +	cell_renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes (_("_Encoding"), +							   cell_renderer, +							   "text", +							   COLUMN_CHARSET, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->available_treeview), +				     column); +	gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET); + +	/* Add the data */ +	i = 0; +	while ((enc = gedit_encoding_get_from_index (i)) != NULL)  +	{ +		gtk_list_store_append (dlg->priv->available_liststore, +				       &parent_iter); +		gtk_list_store_set (dlg->priv->available_liststore, +				    &parent_iter, +				    COLUMN_CHARSET, +				    gedit_encoding_get_charset (enc), +				    COLUMN_NAME, +				    gedit_encoding_get_name (enc), -1); + +		++i; +	} + +	/* Sort model */ +	sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dlg->priv->available_liststore)); +	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), +					      COLUMN_NAME, +					      GTK_SORT_ASCENDING); + +	gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->priv->available_treeview), +				 sort_model); +	g_object_unref (G_OBJECT (dlg->priv->available_liststore)); +	g_object_unref (G_OBJECT (sort_model)); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->available_treeview)); +	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection), +				     GTK_SELECTION_MULTIPLE); + +	available_selection_changed_callback (selection, dlg); +	g_signal_connect (selection, +			  "changed", +			  G_CALLBACK (available_selection_changed_callback), +			  dlg); + +	/* Tree view of selected encodings */ +	dlg->priv->displayed_liststore = gtk_list_store_new (N_COLUMNS, +							     G_TYPE_STRING, +							     G_TYPE_STRING); + +	cell_renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes (_("_Description"), +							   cell_renderer, +							   "text", COLUMN_NAME, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->displayed_treeview), +				     column); +	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + +	cell_renderer = gtk_cell_renderer_text_new (); +	column = gtk_tree_view_column_new_with_attributes (_("_Encoding"), +							   cell_renderer, +							   "text", +							   COLUMN_CHARSET, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->displayed_treeview), +				     column); +	gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET); + +	/* Add the data */ +	init_shown_in_menu_tree_model (dlg); + +	/* Sort model */ +	sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dlg->priv->displayed_liststore)); + +	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE +					      (sort_model), COLUMN_NAME, +					      GTK_SORT_ASCENDING); + +	gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->priv->displayed_treeview), +				 sort_model); +	g_object_unref (G_OBJECT (sort_model)); +	g_object_unref (G_OBJECT (dlg->priv->displayed_liststore)); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->displayed_treeview)); +	gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection), +				     GTK_SELECTION_MULTIPLE); + +	displayed_selection_changed_callback (selection, dlg); +	g_signal_connect (selection, +			  "changed", +			  G_CALLBACK (displayed_selection_changed_callback), +			  dlg); +} + +GtkWidget * +gedit_encodings_dialog_new (void) +{ +	GtkWidget *dlg; + +	dlg = GTK_WIDGET (g_object_new (GEDIT_TYPE_ENCODINGS_DIALOG, NULL)); + +	return dlg; +} + diff --git a/gedit/dialogs/gedit-encodings-dialog.h b/gedit/dialogs/gedit-encodings-dialog.h new file mode 100755 index 00000000..a09cbe5c --- /dev/null +++ b/gedit/dialogs/gedit-encodings-dialog.h @@ -0,0 +1,86 @@ +/* + * gedit-encodings-dialog.h + * This file is part of gedit + * + * Copyright (C) 2003-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2003-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_ENCODINGS_DIALOG_H__ +#define __GEDIT_ENCODINGS_DIALOG_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_ENCODINGS_DIALOG              (gedit_encodings_dialog_get_type()) +#define GEDIT_ENCODINGS_DIALOG(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_ENCODINGS_DIALOG, GeditEncodingsDialog)) +#define GEDIT_ENCODINGS_DIALOG_CONST(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_ENCODINGS_DIALOG, GeditEncodingsDialog const)) +#define GEDIT_ENCODINGS_DIALOG_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_ENCODINGS_DIALOG, GeditEncodingsDialogClass)) +#define GEDIT_IS_ENCODINGS_DIALOG(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_ENCODINGS_DIALOG)) +#define GEDIT_IS_ENCODINGS_DIALOG_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_ENCODINGS_DIALOG)) +#define GEDIT_ENCODINGS_DIALOG_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_ENCODINGS_DIALOG, GeditEncodingsDialogClass)) + + +/* Private structure type */ +typedef struct _GeditEncodingsDialogPrivate GeditEncodingsDialogPrivate; + +/* + * Main object structure + */ +typedef struct _GeditEncodingsDialog GeditEncodingsDialog; + +struct _GeditEncodingsDialog  +{ +	GtkDialog dialog; + +	/*< private > */ +	GeditEncodingsDialogPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditEncodingsDialogClass GeditEncodingsDialogClass; + +struct _GeditEncodingsDialogClass  +{ +	GtkDialogClass parent_class; +}; + +/* + * Public methods + */ +GType		 gedit_encodings_dialog_get_type	(void) G_GNUC_CONST; + +GtkWidget	*gedit_encodings_dialog_new		(void); + +G_END_DECLS + +#endif /* __GEDIT_ENCODINGS_DIALOG_H__ */ + diff --git a/gedit/dialogs/gedit-encodings-dialog.ui b/gedit/dialogs/gedit-encodings-dialog.ui new file mode 100755 index 00000000..0f37b432 --- /dev/null +++ b/gedit/dialogs/gedit-encodings-dialog.ui @@ -0,0 +1,256 @@ +<?xml version="1.0"?> +<!--*- mode: xml -*--> +<interface> +  <object class="GtkDialog" id="encodings-dialog"> +    <property name="width_request">650</property> +    <property name="height_request">400</property> +    <property name="title" translatable="yes">Character encodings</property> +    <property name="type">GTK_WINDOW_TOPLEVEL</property> +    <property name="window_position">GTK_WIN_POS_NONE</property> +    <property name="modal">True</property> +    <property name="resizable">True</property> +    <property name="destroy_with_parent">True</property> +    <property name="has_separator">False</property> +    <child internal-child="vbox"> +      <object class="GtkVBox" id="dialog-vbox3"> +        <property name="visible">True</property> +        <property name="homogeneous">False</property> +        <property name="spacing">0</property> +        <child internal-child="action_area"> +          <object class="GtkHButtonBox" id="dialog-action_area3"> +            <property name="visible">True</property> +            <property name="layout_style">GTK_BUTTONBOX_END</property> +            <child> +              <object class="GtkButton" id="helpbutton1"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label">gtk-help</property> +                <property name="use_stock">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +              </object> +            </child> +            <child> +              <object class="GtkButton" id="closebutton1"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label">gtk-cancel</property> +                <property name="use_stock">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +              </object> +            </child> +            <child> +              <object class="GtkButton" id="button1"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label">gtk-ok</property> +                <property name="use_stock">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +              </object> +            </child> +          </object> +          <packing> +            <property name="padding">0</property> +            <property name="expand">False</property> +            <property name="fill">True</property> +            <property name="pack_type">GTK_PACK_END</property> +          </packing> +        </child> +        <child> +          <object class="GtkHBox" id="encodings-dialog-contents"> +            <property name="border_width">6</property> +            <property name="visible">True</property> +            <property name="homogeneous">True</property> +            <property name="spacing">6</property> +            <child> +              <object class="GtkVBox" id="vbox6"> +                <property name="visible">True</property> +                <property name="homogeneous">False</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="available-label"> +                    <property name="visible">True</property> +                    <property name="label" translatable="yes">A_vailable encodings:</property> +                    <property name="use_underline">True</property> +                    <property name="use_markup">False</property> +                    <property name="justify">GTK_JUSTIFY_LEFT</property> +                    <property name="wrap">False</property> +                    <property name="selectable">False</property> +                    <property name="xalign">0</property> +                    <property name="yalign">0.5</property> +                    <property name="xpad">0</property> +                    <property name="ypad">0</property> +                    <property name="mnemonic_widget">available-treeview</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkScrolledWindow" id="scrolledwindow2"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> +                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> +                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property> +                    <property name="window_placement">GTK_CORNER_TOP_LEFT</property> +                    <child> +                      <object class="GtkTreeView" id="available-treeview"> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="headers_visible">True</property> +                        <property name="rules_hint">True</property> +                        <property name="reorderable">False</property> +                        <property name="enable_search">True</property> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">True</property> +                    <property name="fill">True</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkHBox" id="hbox6"> +                    <property name="visible">True</property> +                    <property name="homogeneous">False</property> +                    <property name="spacing">0</property> +                    <child> +                      <object class="GtkButton" id="add-button"> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="label">gtk-add</property> +                        <property name="use_stock">True</property> +                        <property name="relief">GTK_RELIEF_NORMAL</property> +                      </object> +                      <packing> +                        <property name="padding">0</property> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                      </packing> +                    </child> +                    <child> +                      <placeholder/> +                    </child> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="padding">0</property> +                <property name="expand">True</property> +                <property name="fill">True</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox7"> +                <property name="visible">True</property> +                <property name="homogeneous">False</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="displayed-label"> +                    <property name="visible">True</property> +                    <property name="label" translatable="yes">E_ncodings shown in menu:</property> +                    <property name="use_underline">True</property> +                    <property name="use_markup">False</property> +                    <property name="justify">GTK_JUSTIFY_LEFT</property> +                    <property name="wrap">False</property> +                    <property name="selectable">False</property> +                    <property name="xalign">0</property> +                    <property name="yalign">0.5</property> +                    <property name="xpad">0</property> +                    <property name="ypad">0</property> +                    <property name="mnemonic_widget">displayed-treeview</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkScrolledWindow" id="scrolledwindow3"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> +                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> +                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property> +                    <property name="window_placement">GTK_CORNER_TOP_LEFT</property> +                    <child> +                      <object class="GtkTreeView" id="displayed-treeview"> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="headers_visible">True</property> +                        <property name="rules_hint">True</property> +                        <property name="reorderable">False</property> +                        <property name="enable_search">True</property> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">True</property> +                    <property name="fill">True</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkHBox" id="hbox8"> +                    <property name="visible">True</property> +                    <property name="homogeneous">False</property> +                    <property name="spacing">0</property> +                    <child> +                      <object class="GtkButton" id="remove-button"> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="label">gtk-remove</property> +                        <property name="use_stock">True</property> +                        <property name="relief">GTK_RELIEF_NORMAL</property> +                      </object> +                      <packing> +                        <property name="padding">0</property> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                      </packing> +                    </child> +                    <child> +                      <placeholder/> +                    </child> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="padding">0</property> +                <property name="expand">True</property> +                <property name="fill">True</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="padding">0</property> +            <property name="expand">True</property> +            <property name="fill">True</property> +          </packing> +        </child> +      </object> +    </child> +    <action-widgets> +      <action-widget response="-11">helpbutton1</action-widget> +      <action-widget response="-6">closebutton1</action-widget> +      <action-widget response="-5">button1</action-widget> +    </action-widgets> +  </object> +</interface> diff --git a/gedit/dialogs/gedit-preferences-dialog.c b/gedit/dialogs/gedit-preferences-dialog.c new file mode 100755 index 00000000..52f180fa --- /dev/null +++ b/gedit/dialogs/gedit-preferences-dialog.c @@ -0,0 +1,1189 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-preferences-dialog.c + * This file is part of gedit + * + * Copyright (C) 2001-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2001-2003. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <mateconf/mateconf-client.h> + +#include <gedit/gedit-prefs-manager.h> + +#include "gedit-preferences-dialog.h" +#include "gedit-utils.h" +#include "gedit-debug.h" +#include "gedit-document.h" +#include "gedit-style-scheme-manager.h" +#include "gedit-plugin-manager.h" +#include "gedit-help.h" +#include "gedit-dirs.h" + +/* + * gedit-preferences dialog is a singleton since we don't + * want two dialogs showing an inconsistent state of the + * preferences. + * When gedit_show_preferences_dialog is called and there + * is already a prefs dialog dialog open, it is reparented + * and shown. + */ + +static GtkWidget *preferences_dialog = NULL; + + +enum +{ +	ID_COLUMN = 0, +	NAME_COLUMN, +	DESC_COLUMN, +	NUM_COLUMNS +}; + + +#define GEDIT_PREFERENCES_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +						     GEDIT_TYPE_PREFERENCES_DIALOG, \ +						     GeditPreferencesDialogPrivate)) + +struct _GeditPreferencesDialogPrivate +{ +	GtkWidget	*notebook; + +	/* Font */ +	GtkWidget	*default_font_checkbutton; +	GtkWidget	*font_button; +	GtkWidget	*font_hbox; + +	/* Style Scheme */ +	GtkListStore	*schemes_treeview_model; +	GtkWidget	*schemes_treeview; +	GtkWidget	*install_scheme_button; +	GtkWidget	*uninstall_scheme_button; +	 +	GtkWidget	*install_scheme_file_schooser; + +	/* Tabs */ +	GtkWidget	*tabs_width_spinbutton; +	GtkWidget	*insert_spaces_checkbutton; +	GtkWidget	*tabs_width_hbox; + +	/* Auto indentation */ +	GtkWidget	*auto_indent_checkbutton; + +	/* Text Wrapping */ +	GtkWidget	*wrap_text_checkbutton; +	GtkWidget	*split_checkbutton; + +	/* File Saving */ +	GtkWidget	*backup_copy_checkbutton; +	GtkWidget	*auto_save_checkbutton; +	GtkWidget	*auto_save_spinbutton; +	GtkWidget	*autosave_hbox; +	 +	/* Line numbers */ +	GtkWidget	*display_line_numbers_checkbutton; + +	/* Highlight current line */ +	GtkWidget	*highlight_current_line_checkbutton; +	 +	/* Highlight matching bracket */ +	GtkWidget	*bracket_matching_checkbutton; +	 +	/* Right margin */ +	GtkWidget	*right_margin_checkbutton; +	GtkWidget	*right_margin_position_spinbutton; +	GtkWidget	*right_margin_position_hbox; + +	/* Plugins manager */ +	GtkWidget	*plugin_manager_place_holder; + +	/* Style Scheme editor dialog */ +	GtkWidget	*style_scheme_dialog; +}; + + +G_DEFINE_TYPE(GeditPreferencesDialog, gedit_preferences_dialog, GTK_TYPE_DIALOG) + + +static void  +gedit_preferences_dialog_class_init (GeditPreferencesDialogClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	g_type_class_add_private (object_class, sizeof (GeditPreferencesDialogPrivate)); +} + +static void +dialog_response_handler (GtkDialog *dlg,  +			 gint       res_id) +{ +	gedit_debug (DEBUG_PREFS); + +	switch (res_id) +	{ +		case GTK_RESPONSE_HELP: +			gedit_help_display (GTK_WINDOW (dlg), +					    NULL, +					    "gedit-prefs"); + +			g_signal_stop_emission_by_name (dlg, "response"); + +			break; + +		default: +			gtk_widget_destroy (GTK_WIDGET(dlg)); +	} +} + +static void +tabs_width_spinbutton_value_changed (GtkSpinButton          *spin_button, +				     GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (spin_button == GTK_SPIN_BUTTON (dlg->priv->tabs_width_spinbutton)); + +	gedit_prefs_manager_set_tabs_size (gtk_spin_button_get_value_as_int (spin_button)); +} +	 +static void +insert_spaces_checkbutton_toggled (GtkToggleButton        *button, +				   GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->insert_spaces_checkbutton)); + +	gedit_prefs_manager_set_insert_spaces (gtk_toggle_button_get_active (button)); +} + +static void +auto_indent_checkbutton_toggled (GtkToggleButton        *button, +				 GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->auto_indent_checkbutton)); + +	gedit_prefs_manager_set_auto_indent (gtk_toggle_button_get_active (button)); +} + +static void +auto_save_checkbutton_toggled (GtkToggleButton        *button, +			       GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->auto_save_checkbutton)); + +	if (gtk_toggle_button_get_active (button)) +	{ +		gtk_widget_set_sensitive (dlg->priv->auto_save_spinbutton,  +					  gedit_prefs_manager_auto_save_interval_can_set()); + +		gedit_prefs_manager_set_auto_save (TRUE); +	} +	else	 +	{ +		gtk_widget_set_sensitive (dlg->priv->auto_save_spinbutton, FALSE); +		gedit_prefs_manager_set_auto_save (FALSE); +	} +} + +static void +backup_copy_checkbutton_toggled (GtkToggleButton        *button, +				 GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->backup_copy_checkbutton)); +	 +	gedit_prefs_manager_set_create_backup_copy (gtk_toggle_button_get_active (button)); +} + +static void +auto_save_spinbutton_value_changed (GtkSpinButton          *spin_button, +				    GeditPreferencesDialog *dlg) +{ +	g_return_if_fail (spin_button == GTK_SPIN_BUTTON (dlg->priv->auto_save_spinbutton)); + +	gedit_prefs_manager_set_auto_save_interval ( +			MAX (1, gtk_spin_button_get_value_as_int (spin_button))); +} + +static void +setup_editor_page (GeditPreferencesDialog *dlg) +{ +	gboolean auto_save; +	gint auto_save_interval; + +	gedit_debug (DEBUG_PREFS); + +	/* Set initial state */ +	gtk_spin_button_set_value (GTK_SPIN_BUTTON (dlg->priv->tabs_width_spinbutton), +				   (guint) gedit_prefs_manager_get_tabs_size ()); +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->insert_spaces_checkbutton),  +				      gedit_prefs_manager_get_insert_spaces ()); +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->auto_indent_checkbutton),  +				      gedit_prefs_manager_get_auto_indent ()); +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->backup_copy_checkbutton), +				      gedit_prefs_manager_get_create_backup_copy ()); + +	auto_save = gedit_prefs_manager_get_auto_save (); +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->auto_save_checkbutton), +				      auto_save); + +	auto_save_interval = gedit_prefs_manager_get_auto_save_interval (); +	if (auto_save_interval <= 0) +		auto_save_interval = GPM_DEFAULT_AUTO_SAVE_INTERVAL; + +	gtk_spin_button_set_value (GTK_SPIN_BUTTON (dlg->priv->auto_save_spinbutton), +				   auto_save_interval); + +	/* Set widget sensitivity */ +	gtk_widget_set_sensitive (dlg->priv->tabs_width_hbox,  +				  gedit_prefs_manager_tabs_size_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->insert_spaces_checkbutton, +				  gedit_prefs_manager_insert_spaces_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->auto_indent_checkbutton, +				  gedit_prefs_manager_auto_indent_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->backup_copy_checkbutton, +				  gedit_prefs_manager_create_backup_copy_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->autosave_hbox,  +				  gedit_prefs_manager_auto_save_can_set ());  +	gtk_widget_set_sensitive (dlg->priv->auto_save_spinbutton,  +			          auto_save && +				  gedit_prefs_manager_auto_save_interval_can_set ()); + +	/* Connect signal */ +	g_signal_connect (dlg->priv->tabs_width_spinbutton, +			  "value_changed", +			  G_CALLBACK (tabs_width_spinbutton_value_changed), +			  dlg); +	g_signal_connect (dlg->priv->insert_spaces_checkbutton, +			 "toggled", +			  G_CALLBACK (insert_spaces_checkbutton_toggled), +			  dlg); +	g_signal_connect (dlg->priv->auto_indent_checkbutton, +			  "toggled", +			  G_CALLBACK (auto_indent_checkbutton_toggled), +			  dlg); +	g_signal_connect (dlg->priv->auto_save_checkbutton, +			  "toggled", +			  G_CALLBACK (auto_save_checkbutton_toggled), +			  dlg); +	g_signal_connect (dlg->priv->backup_copy_checkbutton, +			  "toggled", +			  G_CALLBACK (backup_copy_checkbutton_toggled), +			  dlg); +	g_signal_connect (dlg->priv->auto_save_spinbutton, +			  "value_changed", +			  G_CALLBACK (auto_save_spinbutton_value_changed), +			  dlg); +} + +static void +display_line_numbers_checkbutton_toggled (GtkToggleButton        *button, +					  GeditPreferencesDialog *dlg) +{ +	g_return_if_fail (button ==  +			GTK_TOGGLE_BUTTON (dlg->priv->display_line_numbers_checkbutton)); + +	gedit_prefs_manager_set_display_line_numbers (gtk_toggle_button_get_active (button)); +} + +static void +highlight_current_line_checkbutton_toggled (GtkToggleButton        *button, +					    GeditPreferencesDialog *dlg) +{ +	g_return_if_fail (button ==  +			GTK_TOGGLE_BUTTON (dlg->priv->highlight_current_line_checkbutton)); + +	gedit_prefs_manager_set_highlight_current_line (gtk_toggle_button_get_active (button)); +} + +static void +bracket_matching_checkbutton_toggled (GtkToggleButton        *button, +				      GeditPreferencesDialog *dlg) +{ +	g_return_if_fail (button ==  +			GTK_TOGGLE_BUTTON (dlg->priv->bracket_matching_checkbutton)); + +	gedit_prefs_manager_set_bracket_matching ( +				gtk_toggle_button_get_active (button)); +} + +static gboolean split_button_state = TRUE; + +static void +wrap_mode_checkbutton_toggled (GtkToggleButton        *button,  +			       GeditPreferencesDialog *dlg) +{ +	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dlg->priv->wrap_text_checkbutton))) +	{ +		gedit_prefs_manager_set_wrap_mode (GTK_WRAP_NONE); +		 +		gtk_widget_set_sensitive (dlg->priv->split_checkbutton,  +					  FALSE); +		gtk_toggle_button_set_inconsistent ( +			GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), TRUE); +	} +	else +	{ +		gtk_widget_set_sensitive (dlg->priv->split_checkbutton,  +					  TRUE); + +		gtk_toggle_button_set_inconsistent ( +			GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), FALSE); + + +		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton))) +		{ +			split_button_state = TRUE; +			 +			gedit_prefs_manager_set_wrap_mode (GTK_WRAP_WORD); +		} +		else +		{ +			split_button_state = FALSE; +			 +			gedit_prefs_manager_set_wrap_mode (GTK_WRAP_CHAR); +		} +	} +} + +static void +right_margin_checkbutton_toggled (GtkToggleButton        *button, +				  GeditPreferencesDialog *dlg) +{ +	gboolean active; +	 +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->right_margin_checkbutton)); + +	active = gtk_toggle_button_get_active (button); +	 +	gedit_prefs_manager_set_display_right_margin (active); + +	gtk_widget_set_sensitive (dlg->priv->right_margin_position_hbox, +				  active &&  +				  gedit_prefs_manager_right_margin_position_can_set ()); +} + +static void +right_margin_position_spinbutton_value_changed (GtkSpinButton          *spin_button,  +						GeditPreferencesDialog *dlg) +{ +	gint value; +	 +	g_return_if_fail (spin_button == GTK_SPIN_BUTTON (dlg->priv->right_margin_position_spinbutton)); + +	value = CLAMP (gtk_spin_button_get_value_as_int (spin_button), 1, 160); + +	gedit_prefs_manager_set_right_margin_position (value); +} + +static void +setup_view_page (GeditPreferencesDialog *dlg) +{ +	GtkWrapMode wrap_mode; +	gboolean display_right_margin; +	gboolean wrap_mode_can_set; + +	gedit_debug (DEBUG_PREFS); +	 +	/* Set initial state */ +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->display_line_numbers_checkbutton), +				      gedit_prefs_manager_get_display_line_numbers ()); +	 +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->highlight_current_line_checkbutton), +				      gedit_prefs_manager_get_highlight_current_line ()); +	 +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->bracket_matching_checkbutton), +				      gedit_prefs_manager_get_bracket_matching ()); + +	wrap_mode = gedit_prefs_manager_get_wrap_mode (); +	switch (wrap_mode ) +	{ +		case GTK_WRAP_WORD: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->wrap_text_checkbutton), TRUE); +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), TRUE); +			break; +		case GTK_WRAP_CHAR: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->wrap_text_checkbutton), TRUE); +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), FALSE); +			break; +		default: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->wrap_text_checkbutton), FALSE); +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), split_button_state); +			gtk_toggle_button_set_inconsistent ( +				GTK_TOGGLE_BUTTON (dlg->priv->split_checkbutton), TRUE); + +	} + +	display_right_margin = gedit_prefs_manager_get_display_right_margin (); +	 +	gtk_toggle_button_set_active ( +		GTK_TOGGLE_BUTTON (dlg->priv->right_margin_checkbutton),  +		display_right_margin); +	 +	gtk_spin_button_set_value ( +		GTK_SPIN_BUTTON (dlg->priv->right_margin_position_spinbutton), +		(guint)CLAMP (gedit_prefs_manager_get_right_margin_position (), 1, 160)); +		 +	/* Set widgets sensitivity */ +	gtk_widget_set_sensitive (dlg->priv->display_line_numbers_checkbutton, +				  gedit_prefs_manager_display_line_numbers_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->highlight_current_line_checkbutton, +				  gedit_prefs_manager_highlight_current_line_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->bracket_matching_checkbutton, +				  gedit_prefs_manager_bracket_matching_can_set ()); +	wrap_mode_can_set = gedit_prefs_manager_wrap_mode_can_set (); +	gtk_widget_set_sensitive (dlg->priv->wrap_text_checkbutton,  +				  wrap_mode_can_set); +	gtk_widget_set_sensitive (dlg->priv->split_checkbutton,  +				  wrap_mode_can_set &&  +				  (wrap_mode != GTK_WRAP_NONE)); +	gtk_widget_set_sensitive (dlg->priv->right_margin_checkbutton, +				  gedit_prefs_manager_display_right_margin_can_set ()); +	gtk_widget_set_sensitive (dlg->priv->right_margin_position_hbox, +				  display_right_margin &&  +				  gedit_prefs_manager_right_margin_position_can_set ()); +				   +	/* Connect signals */ +	g_signal_connect (dlg->priv->display_line_numbers_checkbutton, +			  "toggled", +			  G_CALLBACK (display_line_numbers_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->highlight_current_line_checkbutton, +			  "toggled",  +			  G_CALLBACK (highlight_current_line_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->bracket_matching_checkbutton, +			  "toggled",  +			  G_CALLBACK (bracket_matching_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->wrap_text_checkbutton, +			  "toggled",  +			  G_CALLBACK (wrap_mode_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->split_checkbutton, +			  "toggled",  +			  G_CALLBACK (wrap_mode_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->right_margin_checkbutton, +			  "toggled",  +			  G_CALLBACK (right_margin_checkbutton_toggled),  +			  dlg); +	g_signal_connect (dlg->priv->right_margin_position_spinbutton, +			  "value_changed", +			  G_CALLBACK (right_margin_position_spinbutton_value_changed),  +			  dlg); +} + +static void +default_font_font_checkbutton_toggled (GtkToggleButton        *button, +				       GeditPreferencesDialog *dlg) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (button == GTK_TOGGLE_BUTTON (dlg->priv->default_font_checkbutton)); + +	if (gtk_toggle_button_get_active (button)) +	{ +		gtk_widget_set_sensitive (dlg->priv->font_hbox, FALSE); +		gedit_prefs_manager_set_use_default_font (TRUE); +	} +	else +	{ +		gtk_widget_set_sensitive (dlg->priv->font_hbox,  +					  gedit_prefs_manager_editor_font_can_set ()); +		gedit_prefs_manager_set_use_default_font (FALSE); +	} +} + +static void +editor_font_button_font_set (GtkFontButton          *font_button, +			     GeditPreferencesDialog *dlg) +{ +	const gchar *font_name; + +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (font_button == GTK_FONT_BUTTON (dlg->priv->font_button)); + +	/* FIXME: Can this fail? Gtk docs are a bit terse... 21-02-2004 pbor */ +	font_name = gtk_font_button_get_font_name (font_button); +	if (!font_name) +	{ +		g_warning ("Could not get font name"); +		return; +	} + +	gedit_prefs_manager_set_editor_font (font_name); +} + +static void +setup_font_colors_page_font_section (GeditPreferencesDialog *dlg) +{ +	gboolean use_default_font; +	gchar *editor_font = NULL; +	gchar *label; + +	gedit_debug (DEBUG_PREFS); + +	gtk_widget_set_tooltip_text (dlg->priv->font_button, +			 _("Click on this button to select the font to be used by the editor")); + +	gedit_utils_set_atk_relation (dlg->priv->font_button, +				      dlg->priv->default_font_checkbutton, +				      ATK_RELATION_CONTROLLED_BY); +	gedit_utils_set_atk_relation (dlg->priv->default_font_checkbutton, +				      dlg->priv->font_button,  +				      ATK_RELATION_CONTROLLER_FOR);	 + +	editor_font = gedit_prefs_manager_get_system_font (); +	label = g_strdup_printf(_("_Use the system fixed width font (%s)"), +				editor_font); +	gtk_button_set_label (GTK_BUTTON (dlg->priv->default_font_checkbutton), +			      label); +	g_free (editor_font); +	g_free (label); + +	/* read current config and setup initial state */ +	use_default_font = gedit_prefs_manager_get_use_default_font (); +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dlg->priv->default_font_checkbutton), +				      use_default_font); + +	editor_font = gedit_prefs_manager_get_editor_font (); +	if (editor_font != NULL) +	{ +		gtk_font_button_set_font_name (GTK_FONT_BUTTON (dlg->priv->font_button), +					       editor_font); +		g_free (editor_font); +	} + +	/* Connect signals */ +	g_signal_connect (dlg->priv->default_font_checkbutton, +			  "toggled", +			  G_CALLBACK (default_font_font_checkbutton_toggled), +			  dlg); +	g_signal_connect (dlg->priv->font_button, +			  "font_set", +			  G_CALLBACK (editor_font_button_font_set), +			  dlg); + +	/* Set initial widget sensitivity */ +	gtk_widget_set_sensitive (dlg->priv->default_font_checkbutton,  +				  gedit_prefs_manager_use_default_font_can_set ()); + +	if (use_default_font) +		gtk_widget_set_sensitive (dlg->priv->font_hbox, FALSE); +	else +		gtk_widget_set_sensitive (dlg->priv->font_hbox,  +					  gedit_prefs_manager_editor_font_can_set ()); +} + +static void +set_buttons_sensisitivity_according_to_scheme (GeditPreferencesDialog *dlg, +					       const gchar            *scheme_id) +{ +	gboolean editable; +	 +	editable = (scheme_id != NULL) &&  +	           _gedit_style_scheme_manager_scheme_is_gedit_user_scheme ( +						gedit_get_style_scheme_manager (), +						scheme_id); + +	gtk_widget_set_sensitive (dlg->priv->uninstall_scheme_button, +				  editable); +} + +static void +style_scheme_changed (GtkWidget              *treeview, +		      GeditPreferencesDialog *dlg) +{ +	GtkTreePath *path; +	GtkTreeIter iter; +	gchar *id; + +	gtk_tree_view_get_cursor (GTK_TREE_VIEW (dlg->priv->schemes_treeview), &path, NULL); +	gtk_tree_model_get_iter (GTK_TREE_MODEL (dlg->priv->schemes_treeview_model), +				 &iter, path); +	gtk_tree_path_free (path); +	gtk_tree_model_get (GTK_TREE_MODEL (dlg->priv->schemes_treeview_model), +			    &iter, ID_COLUMN, &id, -1); + +	gedit_prefs_manager_set_source_style_scheme (id); + +	set_buttons_sensisitivity_according_to_scheme (dlg, id); + +	g_free (id); +} + +static const gchar * +ensure_color_scheme_id (const gchar *id) +{ +	GtkSourceStyleScheme *scheme = NULL; +	GtkSourceStyleSchemeManager *manager = gedit_get_style_scheme_manager (); + +	if (id == NULL) +	{ +		gchar *pref_id; + +		pref_id = gedit_prefs_manager_get_source_style_scheme (); +		scheme = gtk_source_style_scheme_manager_get_scheme (manager, +								     pref_id); +		g_free (pref_id); +	} +	else +	{ +		scheme = gtk_source_style_scheme_manager_get_scheme (manager, +								     id); +	} + +	if (scheme == NULL) +	{ +		/* Fall-back to classic style scheme */ +		scheme = gtk_source_style_scheme_manager_get_scheme (manager, +								     "classic"); +	} + +	if (scheme == NULL) +	{ +		/* Cannot determine default style scheme -> broken GtkSourceView installation */ +		return NULL; +	} + +	return 	gtk_source_style_scheme_get_id (scheme); +} + +/* If def_id is NULL, use the default scheme as returned by  + * gedit_style_scheme_manager_get_default_scheme. If this one returns NULL + * use the first available scheme as default */ +static const gchar * +populate_color_scheme_list (GeditPreferencesDialog *dlg, const gchar *def_id) +{ +	GSList *schemes; +	GSList *l; +	 +	gtk_list_store_clear (dlg->priv->schemes_treeview_model); +	 +	def_id = ensure_color_scheme_id (def_id); +	if (def_id == NULL)  +	{ +		g_warning ("Cannot build the list of available color schemes.\n" +		           "Please check your GtkSourceView installation."); +		return NULL; +	} +	 +	schemes = gedit_style_scheme_manager_list_schemes_sorted (gedit_get_style_scheme_manager ()); +	l = schemes; +	while (l != NULL) +	{ +		GtkSourceStyleScheme *scheme; +		const gchar *id; +		const gchar *name; +		const gchar *description; +		GtkTreeIter iter; + +		scheme = GTK_SOURCE_STYLE_SCHEME (l->data); +				 +		id = gtk_source_style_scheme_get_id (scheme); +		name = gtk_source_style_scheme_get_name (scheme); +		description = gtk_source_style_scheme_get_description (scheme); + +		gtk_list_store_append (dlg->priv->schemes_treeview_model, &iter); +		gtk_list_store_set (dlg->priv->schemes_treeview_model, +				    &iter, +				    ID_COLUMN, id, +				    NAME_COLUMN, name, +				    DESC_COLUMN, description, +				    -1); + +		g_return_val_if_fail (def_id != NULL, NULL); +		if (strcmp (id, def_id) == 0) +		{ +			GtkTreeSelection *selection; +			 +			selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->schemes_treeview)); +			gtk_tree_selection_select_iter (selection, &iter); +		} +		 +		l = g_slist_next (l); +	} +	 +	g_slist_free (schemes); +	 +	return def_id; +} + +static void +add_scheme_chooser_response_cb (GtkDialog              *chooser,  +				gint                    res_id, +				GeditPreferencesDialog *dlg) +{ +	gchar* filename;  +	const gchar *scheme_id; +	 +	if (res_id != GTK_RESPONSE_ACCEPT) +	{ +		gtk_widget_hide (GTK_WIDGET (chooser)); +		return; +	} +	 +	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); +	if (filename == NULL) +		return; +	 +	gtk_widget_hide (GTK_WIDGET (chooser)); +	 +	scheme_id = _gedit_style_scheme_manager_install_scheme ( +					gedit_get_style_scheme_manager (), +					filename); +	g_free (filename); + +	if (scheme_id == NULL) +	{ +		gedit_warning (GTK_WINDOW (dlg), +			       _("The selected color scheme cannot be installed.")); + +		return; +	} + +	gedit_prefs_manager_set_source_style_scheme (scheme_id); + +	scheme_id = populate_color_scheme_list (dlg, scheme_id); + +	set_buttons_sensisitivity_according_to_scheme (dlg, scheme_id); +} +			  +static void +install_scheme_clicked (GtkButton              *button, +			GeditPreferencesDialog *dlg) +{ +	GtkWidget      *chooser; +	GtkFileFilter  *filter; +	 +	if (dlg->priv->install_scheme_file_schooser != NULL) { +		gtk_window_present (GTK_WINDOW (dlg->priv->install_scheme_file_schooser)); +		gtk_widget_grab_focus (dlg->priv->install_scheme_file_schooser); +		return; +	} + +	chooser = gtk_file_chooser_dialog_new (_("Add Scheme"), +					       GTK_WINDOW (dlg), +					       GTK_FILE_CHOOSER_ACTION_OPEN, +					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, +					       NULL); + +	gedit_dialog_add_button (GTK_DIALOG (chooser),  +				 _("A_dd Scheme"), +				 GTK_STOCK_ADD, +				 GTK_RESPONSE_ACCEPT); + +	gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE); +		 +	/* Filters */ +	filter = gtk_file_filter_new (); +	gtk_file_filter_set_name (filter, _("Color Scheme Files")); +	gtk_file_filter_add_pattern (filter, "*.xml"); +	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter); + +	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), filter); +	 +	filter = gtk_file_filter_new (); +	gtk_file_filter_set_name (filter, _("All Files")); +	gtk_file_filter_add_pattern (filter, "*"); +	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter); + +	gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT); +			 +	g_signal_connect (chooser, +			  "response", +			  G_CALLBACK (add_scheme_chooser_response_cb), +			  dlg); + +	dlg->priv->install_scheme_file_schooser = chooser; +	 +	g_object_add_weak_pointer (G_OBJECT (chooser), +				   (gpointer) &dlg->priv->install_scheme_file_schooser); +				    +	gtk_widget_show (chooser);	 +} + +static void +uninstall_scheme_clicked (GtkButton              *button, +			  GeditPreferencesDialog *dlg) +{ +	GtkTreeSelection *selection; +	GtkTreeModel *model; +	GtkTreeIter iter; + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->schemes_treeview)); +	model = GTK_TREE_MODEL (dlg->priv->schemes_treeview_model); + +	if (gtk_tree_selection_get_selected (selection, +					     &model, +					     &iter)) +	{ +		gchar *id; +		gchar *name; + +		gtk_tree_model_get (model, &iter, +				    ID_COLUMN, &id, +				    NAME_COLUMN, &name, +				    -1); +	 +		if (!_gedit_style_scheme_manager_uninstall_scheme (gedit_get_style_scheme_manager (), id)) +		{ +			gedit_warning (GTK_WINDOW (dlg), +				       _("Could not remove color scheme \"%s\"."), +				       name); +		} +		else +		{	 +			const gchar *real_new_id; +			gchar *new_id = NULL; +			GtkTreePath *path; +			GtkTreeIter new_iter; +			gboolean new_iter_set = FALSE; + +			/* If the removed style scheme is the last of the list, +			 * set as new default style scheme the previous one, +			 * otherwise set the next one. +			 * To make this possible, we need to get the id of the +			 * new default style scheme before re-populating the list. +			 * Fall back to "classic" if it is not possible to get +			 * the id +			 */ +			path = gtk_tree_model_get_path (model, &iter); + +			/* Try to move to the next path */ +			gtk_tree_path_next (path); +			if (!gtk_tree_model_get_iter (model, &new_iter, path)) +			{ +				/* It seems the removed style scheme was the  +				 * last of the list. Try to move to the  +				 * previous one */ +				gtk_tree_path_free (path); + +				path = gtk_tree_model_get_path (model, &iter); +				 +				gtk_tree_path_prev (path); +				if (gtk_tree_model_get_iter (model, &new_iter, path)) +					new_iter_set = TRUE; +			} +			else +				new_iter_set = TRUE; + +			gtk_tree_path_free (path); +						 +			if (new_iter_set) +				gtk_tree_model_get (model, &new_iter, +						    ID_COLUMN, &new_id, +						    -1); +						    			 +			real_new_id = populate_color_scheme_list (dlg, new_id); +			g_free (new_id); +	 +			set_buttons_sensisitivity_according_to_scheme (dlg, real_new_id); +			 +			if (real_new_id != NULL) +				gedit_prefs_manager_set_source_style_scheme (real_new_id); +		} + +		g_free (id); +		g_free (name); +	} +} + +static void +scheme_description_cell_data_func (GtkTreeViewColumn *column, +				   GtkCellRenderer   *renderer, +				   GtkTreeModel      *model, +				   GtkTreeIter       *iter, +				   gpointer           data) +{ +	gchar *name; +	gchar *desc; +	gchar *text; + +	gtk_tree_model_get (model, iter, +			    NAME_COLUMN, &name, +			    DESC_COLUMN, &desc, +			    -1); + +	if (desc != NULL) +	{ +		text = g_markup_printf_escaped ("<b>%s</b> - %s", +						name, +						desc); +	} +	else +	{ +		text = g_markup_printf_escaped ("<b>%s</b>", +						name); +	} + +	g_free (name); +	g_free (desc); + +	g_object_set (G_OBJECT (renderer), +		      "markup", +		      text, +		      NULL); + +	g_free (text); +} + +static void +setup_font_colors_page_style_scheme_section (GeditPreferencesDialog *dlg) +{ +	GtkCellRenderer *renderer; +	GtkTreeViewColumn *column; +	GtkTreeSelection *selection; +	const gchar *def_id; +	 +	gedit_debug (DEBUG_PREFS); +			   +	/* Create GtkListStore for styles & setup treeview. */ +	dlg->priv->schemes_treeview_model = gtk_list_store_new (NUM_COLUMNS, +								G_TYPE_STRING, +								G_TYPE_STRING, +								G_TYPE_STRING, +								G_TYPE_STRING); + +	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dlg->priv->schemes_treeview_model), +					      0, +					      GTK_SORT_ASCENDING); +	gtk_tree_view_set_model (GTK_TREE_VIEW (dlg->priv->schemes_treeview), +				 GTK_TREE_MODEL (dlg->priv->schemes_treeview_model)); + +	column = gtk_tree_view_column_new (); + +	renderer = gtk_cell_renderer_text_new (); +	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +	gtk_tree_view_column_pack_start (column, renderer, TRUE); +	gtk_tree_view_column_set_cell_data_func (column, +						 renderer, +						 scheme_description_cell_data_func, +						 dlg, +						 NULL); + +	gtk_tree_view_append_column (GTK_TREE_VIEW (dlg->priv->schemes_treeview), +				     column); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dlg->priv->schemes_treeview)); +	gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + +	def_id = populate_color_scheme_list (dlg, NULL); +	 +	/* Connect signals */ +	g_signal_connect (dlg->priv->schemes_treeview, +			  "cursor-changed", +			  G_CALLBACK (style_scheme_changed), +			  dlg); +	g_signal_connect (dlg->priv->install_scheme_button, +			  "clicked", +			  G_CALLBACK (install_scheme_clicked), +			  dlg); +	g_signal_connect (dlg->priv->uninstall_scheme_button, +			  "clicked", +			  G_CALLBACK (uninstall_scheme_clicked), +			  dlg); +		 +	/* Set initial widget sensitivity */ +	set_buttons_sensisitivity_according_to_scheme (dlg, def_id); +} + +static void +setup_font_colors_page (GeditPreferencesDialog *dlg) +{ +	setup_font_colors_page_font_section (dlg); +	setup_font_colors_page_style_scheme_section (dlg); +} + +static void +setup_plugins_page (GeditPreferencesDialog *dlg) +{ +	GtkWidget *page_content; + +	gedit_debug (DEBUG_PREFS); + +	page_content = gedit_plugin_manager_new (); +	g_return_if_fail (page_content != NULL); + +	gtk_box_pack_start (GTK_BOX (dlg->priv->plugin_manager_place_holder), +			    page_content, +			    TRUE, +			    TRUE, +			    0); + +	gtk_widget_show_all (page_content); +} + +static void +gedit_preferences_dialog_init (GeditPreferencesDialog *dlg) +{ +	GtkWidget *error_widget; +	gboolean ret; +	gchar *file; +	gchar *root_objects[] = { +		"notebook", +		"adjustment1", +		"adjustment2", +		"adjustment3", +		"install_scheme_image", +		NULL +	}; + +	gedit_debug (DEBUG_PREFS); + +	dlg->priv = GEDIT_PREFERENCES_DIALOG_GET_PRIVATE (dlg); + +	gtk_dialog_add_buttons (GTK_DIALOG (dlg), +				GTK_STOCK_CLOSE, +				GTK_RESPONSE_CLOSE, +				GTK_STOCK_HELP, +				GTK_RESPONSE_HELP, +				NULL); + +	gtk_window_set_title (GTK_WINDOW (dlg), _("gedit Preferences")); +	gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); +	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); +	gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE); + +	/* HIG defaults */ +	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 2); /* 2 * 5 + 2 = 12 */ +	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), 5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), 6); +	 +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (dialog_response_handler), +			  NULL); +	 +	file = gedit_dirs_get_ui_file ("gedit-preferences-dialog.ui"); +	ret = gedit_utils_get_ui_objects (file, +		root_objects, +		&error_widget, + +		"notebook", &dlg->priv->notebook, + +		"display_line_numbers_checkbutton", &dlg->priv->display_line_numbers_checkbutton, +		"highlight_current_line_checkbutton", &dlg->priv->highlight_current_line_checkbutton, +		"bracket_matching_checkbutton", &dlg->priv->bracket_matching_checkbutton, +		"wrap_text_checkbutton", &dlg->priv->wrap_text_checkbutton, +		"split_checkbutton", &dlg->priv->split_checkbutton, + +		"right_margin_checkbutton", &dlg->priv->right_margin_checkbutton, +		"right_margin_position_spinbutton", &dlg->priv->right_margin_position_spinbutton, +		"right_margin_position_hbox", &dlg->priv->right_margin_position_hbox, + +		"tabs_width_spinbutton", &dlg->priv->tabs_width_spinbutton, +		"tabs_width_hbox", &dlg->priv->tabs_width_hbox, +		"insert_spaces_checkbutton", &dlg->priv->insert_spaces_checkbutton, + +		"auto_indent_checkbutton", &dlg->priv->auto_indent_checkbutton, + +		"autosave_hbox", &dlg->priv->autosave_hbox, +		"backup_copy_checkbutton", &dlg->priv->backup_copy_checkbutton, +		"auto_save_checkbutton", &dlg->priv->auto_save_checkbutton, +		"auto_save_spinbutton", &dlg->priv->auto_save_spinbutton, + +		"default_font_checkbutton", &dlg->priv->default_font_checkbutton, +		"font_button", &dlg->priv->font_button, +		"font_hbox", &dlg->priv->font_hbox, + +		"schemes_treeview", &dlg->priv->schemes_treeview, +		"install_scheme_button", &dlg->priv->install_scheme_button, +		"uninstall_scheme_button", &dlg->priv->uninstall_scheme_button, + +		"plugin_manager_place_holder", &dlg->priv->plugin_manager_place_holder, + +		NULL); +	g_free (file); + +	if (!ret) +	{ +		gtk_widget_show (error_widget); +			 +		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +		                    error_widget, +		                    TRUE, TRUE, 0); +		 +		return; +	} + +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			    dlg->priv->notebook, FALSE, FALSE, 0); +	g_object_unref (dlg->priv->notebook); +	gtk_container_set_border_width (GTK_CONTAINER (dlg->priv->notebook), 5); + +	setup_editor_page (dlg); +	setup_view_page (dlg); +	setup_font_colors_page (dlg); +	setup_plugins_page (dlg); +} + +void +gedit_show_preferences_dialog (GeditWindow *parent) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (GEDIT_IS_WINDOW (parent)); + +	if (preferences_dialog == NULL) +	{ +		preferences_dialog = GTK_WIDGET (g_object_new (GEDIT_TYPE_PREFERENCES_DIALOG, NULL)); +		g_signal_connect (preferences_dialog, +				  "destroy", +				  G_CALLBACK (gtk_widget_destroyed), +				  &preferences_dialog); +	} + +	if (GTK_WINDOW (parent) != gtk_window_get_transient_for (GTK_WINDOW (preferences_dialog))) +	{ +		gtk_window_set_transient_for (GTK_WINDOW (preferences_dialog), +					      GTK_WINDOW (parent)); +	} + +	gtk_window_present (GTK_WINDOW (preferences_dialog)); +} diff --git a/gedit/dialogs/gedit-preferences-dialog.h b/gedit/dialogs/gedit-preferences-dialog.h new file mode 100755 index 00000000..bafebb06 --- /dev/null +++ b/gedit/dialogs/gedit-preferences-dialog.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-preferences-dialog.c + * This file is part of gedit + * + * Copyright (C) 2001-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2003. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PREFERENCES_DIALOG_H__ +#define __GEDIT_PREFERENCES_DIALOG_H__ + +#include "gedit-window.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PREFERENCES_DIALOG              (gedit_preferences_dialog_get_type()) +#define GEDIT_PREFERENCES_DIALOG(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PREFERENCES_DIALOG, GeditPreferencesDialog)) +#define GEDIT_PREFERENCES_DIALOG_CONST(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PREFERENCES_DIALOG, GeditPreferencesDialog const)) +#define GEDIT_PREFERENCES_DIALOG_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PREFERENCES_DIALOG, GeditPreferencesDialogClass)) +#define GEDIT_IS_PREFERENCES_DIALOG(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PREFERENCES_DIALOG)) +#define GEDIT_IS_PREFERENCES_DIALOG_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PREFERENCES_DIALOG)) +#define GEDIT_PREFERENCES_DIALOG_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PREFERENCES_DIALOG, GeditPreferencesDialogClass)) + + +/* Private structure type */ +typedef struct _GeditPreferencesDialogPrivate GeditPreferencesDialogPrivate; + +/* + * Main object structure + */ +typedef struct _GeditPreferencesDialog GeditPreferencesDialog; + +struct _GeditPreferencesDialog  +{ +	GtkDialog dialog; +	 +	/*< private > */ +	GeditPreferencesDialogPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditPreferencesDialogClass GeditPreferencesDialogClass; + +struct _GeditPreferencesDialogClass  +{ +	GtkDialogClass parent_class; +}; + +/* + * Public methods + */ +GType		 gedit_preferences_dialog_get_type	(void) G_GNUC_CONST; + +void		 gedit_show_preferences_dialog		(GeditWindow *parent); + +G_END_DECLS + +#endif /* __GEDIT_PREFERENCES_DIALOG_H__ */ + diff --git a/gedit/dialogs/gedit-preferences-dialog.ui b/gedit/dialogs/gedit-preferences-dialog.ui new file mode 100755 index 00000000..42d41e31 --- /dev/null +++ b/gedit/dialogs/gedit-preferences-dialog.ui @@ -0,0 +1,1107 @@ +<?xml version="1.0"?> +<interface> +  <requires lib="gtk+" version="2.16"/> +  <!-- interface-naming-policy toplevel-contextual --> +  <object class="GtkAdjustment" id="adjustment1"> +    <property name="value">80</property> +    <property name="lower">1</property> +    <property name="upper">160</property> +    <property name="step_increment">1</property> +    <property name="page_increment">10</property> +  </object> +  <object class="GtkAdjustment" id="adjustment2"> +    <property name="value">8</property> +    <property name="lower">1</property> +    <property name="upper">24</property> +    <property name="step_increment">1</property> +    <property name="page_increment">4</property> +  </object> +  <object class="GtkAdjustment" id="adjustment3"> +    <property name="value">8</property> +    <property name="lower">1</property> +    <property name="upper">100</property> +    <property name="step_increment">1</property> +    <property name="page_increment">10</property> +  </object> +  <object class="GtkImage" id="install_scheme_image"> +    <property name="stock">gtk-add</property> +  </object> +  <object class="GtkDialog" id="preferences_dialog"> +    <property name="title" translatable="yes">Preferences</property> +    <property name="resizable">False</property> +    <property name="destroy_with_parent">True</property> +    <property name="type_hint">dialog</property> +    <property name="has_separator">False</property> +    <child internal-child="vbox"> +      <object class="GtkVBox" id="dialog-vbox"> +        <property name="visible">True</property> +        <property name="orientation">vertical</property> +        <child> +          <object class="GtkNotebook" id="notebook"> +            <property name="visible">True</property> +            <property name="can_focus">True</property> +            <property name="border_width">6</property> +            <child> +              <object class="GtkVBox" id="vbox228"> +                <property name="visible">True</property> +                <property name="border_width">12</property> +                <property name="orientation">vertical</property> +                <property name="spacing">18</property> +                <child> +                  <object class="GtkVBox" id="vbox226"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label848"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Text Wrapping</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox142"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label849"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="wrap_mode_frame"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="wrap_text_checkbutton"> +                                <property name="label" translatable="yes">Enable text _wrapping</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkCheckButton" id="split_checkbutton"> +                                <property name="label" translatable="yes">Do not _split words over two lines</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox217"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label854"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Line Numbers</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox137"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label843"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox222"> +                            <property name="visible">True</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="display_line_numbers_checkbutton"> +                                <property name="label" translatable="yes">_Display line numbers</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">1</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox244"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label876"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Current Line</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox161"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label877"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox245"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="highlight_current_line_checkbutton"> +                                <property name="label" translatable="yes">Highlight current _line</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="position">2</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox230"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label855"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Right Margin</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox145"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label856"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox231"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="right_margin_checkbutton"> +                                <property name="label" translatable="yes">Display right _margin</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkHBox" id="right_margin_position_hbox"> +                                <property name="visible">True</property> +                                <property name="spacing">6</property> +                                <child> +                                  <object class="GtkLabel" id="label857"> +                                    <property name="visible">True</property> +                                    <property name="xalign">0</property> +                                    <property name="label" translatable="yes">_Right margin at column:</property> +                                    <property name="use_underline">True</property> +                                    <property name="mnemonic_widget">right_margin_position_spinbutton</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">0</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkSpinButton" id="right_margin_position_spinbutton"> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="adjustment">adjustment1</property> +                                    <property name="climb_rate">1</property> +                                    <property name="snap_to_ticks">True</property> +                                    <property name="numeric">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">1</property> +                                  </packing> +                                </child> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">3</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox249"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label881"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Bracket Matching</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox163"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label882"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox250"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="bracket_matching_checkbutton"> +                                <property name="label" translatable="yes">Highlight matching _bracket</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="position">4</property> +                  </packing> +                </child> +              </object> +            </child> +            <child type="tab"> +              <object class="GtkLabel" id="label853"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">View</property> +              </object> +              <packing> +                <property name="tab_fill">False</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox224"> +                <property name="visible">True</property> +                <property name="border_width">12</property> +                <property name="orientation">vertical</property> +                <property name="spacing">18</property> +                <child> +                  <object class="GtkVBox" id="vbox225"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label846"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Tab Stops</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox141"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label847"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox205"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkHBox" id="tabs_width_hbox"> +                                <property name="visible">True</property> +                                <property name="spacing">6</property> +                                <child> +                                  <object class="GtkLabel" id="label98"> +                                    <property name="visible">True</property> +                                    <property name="label" translatable="yes">_Tab width:</property> +                                    <property name="use_underline">True</property> +                                    <property name="justify">center</property> +                                    <property name="mnemonic_widget">tabs_width_spinbutton</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">0</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkSpinButton" id="tabs_width_spinbutton"> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="adjustment">adjustment2</property> +                                    <property name="climb_rate">1</property> +                                    <property name="numeric">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">1</property> +                                  </packing> +                                </child> +                              </object> +                              <packing> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkCheckButton" id="insert_spaces_checkbutton"> +                                <property name="label" translatable="yes">Insert _spaces instead of tabs</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox227"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label851"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Automatic Indentation</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox143"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label852"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkCheckButton" id="auto_indent_checkbutton"> +                            <property name="label" translatable="yes">_Enable automatic indentation</property> +                            <property name="visible">True</property> +                            <property name="can_focus">True</property> +                            <property name="receives_default">False</property> +                            <property name="use_underline">True</property> +                            <property name="draw_indicator">True</property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">1</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox232"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label859"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">File Saving</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox147"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label860"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox187"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="backup_copy_checkbutton"> +                                <property name="label" translatable="yes">Create a _backup copy of files before saving</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkHBox" id="autosave_hbox"> +                                <property name="visible">True</property> +                                <property name="spacing">6</property> +                                <child> +                                  <object class="GtkCheckButton" id="auto_save_checkbutton"> +                                    <property name="label" translatable="yes">_Autosave files every</property> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="receives_default">False</property> +                                    <property name="use_underline">True</property> +                                    <property name="draw_indicator">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">0</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkSpinButton" id="auto_save_spinbutton"> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="adjustment">adjustment3</property> +                                    <property name="climb_rate">1</property> +                                    <property name="numeric">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">1</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkLabel" id="label97"> +                                    <property name="visible">True</property> +                                    <property name="label" translatable="yes">_minutes</property> +                                    <property name="use_underline">True</property> +                                    <property name="justify">center</property> +                                    <property name="mnemonic_widget">auto_save_spinbutton</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">2</property> +                                  </packing> +                                </child> +                              </object> +                              <packing> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">2</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="position">1</property> +              </packing> +            </child> +            <child type="tab"> +              <object class="GtkLabel" id="label829"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">Editor</property> +              </object> +              <packing> +                <property name="position">1</property> +                <property name="tab_fill">False</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox202"> +                <property name="visible">True</property> +                <property name="border_width">12</property> +                <property name="orientation">vertical</property> +                <property name="spacing">18</property> +                <child> +                  <object class="GtkVBox" id="vbox185"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label819"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Font</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox116"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label800"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox183"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkCheckButton" id="default_font_checkbutton"> +                                <property name="label">_Use the system fixed width font (%s)</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkHBox" id="font_hbox"> +                                <property name="visible">True</property> +                                <property name="spacing">12</property> +                                <child> +                                  <object class="GtkLabel" id="font_label"> +                                    <property name="visible">True</property> +                                    <property name="xalign">0</property> +                                    <property name="label" translatable="yes">Editor _font: </property> +                                    <property name="use_underline">True</property> +                                    <property name="justify">center</property> +                                    <property name="mnemonic_widget">font_button</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">0</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkFontButton" id="font_button"> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="receives_default">False</property> +                                    <property name="title" translatable="yes">Pick the editor font</property> +                                    <property name="use_font">True</property> +                                  </object> +                                  <packing> +                                    <property name="position">1</property> +                                  </packing> +                                </child> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkVBox" id="vbox14"> +                    <property name="visible">True</property> +                    <property name="orientation">vertical</property> +                    <property name="spacing">6</property> +                    <child> +                      <object class="GtkLabel" id="label798"> +                        <property name="visible">True</property> +                        <property name="xalign">0</property> +                        <property name="label" translatable="yes">Color Scheme</property> +                        <attributes> +                          <attribute name="weight" value="bold"/> +                        </attributes> +                      </object> +                      <packing> +                        <property name="expand">False</property> +                        <property name="fill">False</property> +                        <property name="position">0</property> +                      </packing> +                    </child> +                    <child> +                      <object class="GtkHBox" id="hbox115"> +                        <property name="visible">True</property> +                        <child> +                          <object class="GtkLabel" id="label797"> +                            <property name="visible">True</property> +                            <property name="label" translatable="yes">    </property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkVBox" id="vbox1"> +                            <property name="visible">True</property> +                            <property name="orientation">vertical</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkScrolledWindow" id="scrolledwindow2"> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="hscrollbar_policy">automatic</property> +                                <property name="vscrollbar_policy">automatic</property> +                                <property name="shadow_type">etched-in</property> +                                <child> +                                  <object class="GtkTreeView" id="schemes_treeview"> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                                    <property name="headers_visible">False</property> +                                    <property name="rules_hint">True</property> +                                  </object> +                                </child> +                              </object> +                              <packing> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkHButtonBox" id="hbuttonbox1"> +                                <property name="visible">True</property> +                                <property name="spacing">6</property> +                                <property name="layout_style">end</property> +                                <child> +                                  <object class="GtkButton" id="install_scheme_button"> +                                    <property name="label" translatable="yes">_Add...</property> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="receives_default">False</property> +                                    <property name="image">install_scheme_image</property> +                                    <property name="use_underline">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">0</property> +                                  </packing> +                                </child> +                                <child> +                                  <object class="GtkButton" id="uninstall_scheme_button"> +                                    <property name="label">gtk-remove</property> +                                    <property name="visible">True</property> +                                    <property name="can_focus">True</property> +                                    <property name="receives_default">False</property> +                                    <property name="use_stock">True</property> +                                  </object> +                                  <packing> +                                    <property name="expand">False</property> +                                    <property name="fill">False</property> +                                    <property name="position">1</property> +                                  </packing> +                                </child> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                      <packing> +                        <property name="position">1</property> +                      </packing> +                    </child> +                  </object> +                  <packing> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="position">2</property> +              </packing> +            </child> +            <child type="tab"> +              <object class="GtkLabel" id="label830"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">Font & Colors</property> +              </object> +              <packing> +                <property name="position">2</property> +                <property name="tab_fill">False</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="plugin_manager_place_holder"> +                <property name="visible">True</property> +                <property name="border_width">12</property> +                <property name="orientation">vertical</property> +                <child> +                  <placeholder/> +                </child> +              </object> +              <packing> +                <property name="position">3</property> +              </packing> +            </child> +            <child type="tab"> +              <object class="GtkLabel" id="label868"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">Plugins</property> +              </object> +              <packing> +                <property name="position">3</property> +                <property name="tab_fill">False</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="position">1</property> +          </packing> +        </child> +        <child internal-child="action_area"> +          <object class="GtkHButtonBox" id="dialog-action_area"> +            <property name="visible">True</property> +            <property name="layout_style">end</property> +            <child> +              <object class="GtkButton" id="helpbutton1"> +                <property name="label">gtk-help</property> +                <property name="visible">True</property> +                <property name="can_focus">True</property> +                <property name="can_default">True</property> +                <property name="receives_default">False</property> +                <property name="use_stock">True</property> +              </object> +              <packing> +                <property name="expand">False</property> +                <property name="fill">False</property> +                <property name="position">0</property> +              </packing> +            </child> +            <child> +              <object class="GtkButton" id="closebutton1"> +                <property name="label">gtk-close</property> +                <property name="visible">True</property> +                <property name="can_focus">True</property> +                <property name="can_default">True</property> +                <property name="receives_default">False</property> +                <property name="use_stock">True</property> +              </object> +              <packing> +                <property name="expand">False</property> +                <property name="fill">False</property> +                <property name="position">1</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="expand">False</property> +            <property name="pack_type">end</property> +            <property name="position">0</property> +          </packing> +        </child> +      </object> +    </child> +    <action-widgets> +      <action-widget response="-11">helpbutton1</action-widget> +      <action-widget response="-7">closebutton1</action-widget> +    </action-widgets> +  </object> +</interface> diff --git a/gedit/dialogs/gedit-search-dialog.c b/gedit/dialogs/gedit-search-dialog.c new file mode 100755 index 00000000..5a059932 --- /dev/null +++ b/gedit/dialogs/gedit-search-dialog.c @@ -0,0 +1,634 @@ +/* + * gedit-search-dialog.c + * This file is part of gedit + * + * Copyright (C) 2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> + +#include "gedit-search-dialog.h" +#include "gedit-history-entry.h" +#include "gedit-utils.h" +#include "gedit-marshal.h" +#include "gedit-dirs.h" + +#define GEDIT_SEARCH_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +						GEDIT_TYPE_SEARCH_DIALOG,              \ +						GeditSearchDialogPrivate)) + +/* Signals */ +enum +{ +	SHOW_REPLACE, +	LAST_SIGNAL +}; + +static guint dialog_signals [LAST_SIGNAL] = { 0 }; + +struct _GeditSearchDialogPrivate  +{ +	gboolean   show_replace; + +	GtkWidget *table; +	GtkWidget *search_label; +	GtkWidget *search_entry; +	GtkWidget *search_text_entry; +	GtkWidget *replace_label; +	GtkWidget *replace_entry; +	GtkWidget *replace_text_entry; +	GtkWidget *match_case_checkbutton; +	GtkWidget *entire_word_checkbutton; +	GtkWidget *backwards_checkbutton; +	GtkWidget *wrap_around_checkbutton; +	GtkWidget *find_button; +	GtkWidget *replace_button; +	GtkWidget *replace_all_button; + +	gboolean   ui_error; +}; + +G_DEFINE_TYPE(GeditSearchDialog, gedit_search_dialog, GTK_TYPE_DIALOG) + +enum +{ +	PROP_0, +	PROP_SHOW_REPLACE +}; + +static void +gedit_search_dialog_set_property (GObject      *object, +				  guint         prop_id, +				  const GValue *value, +				  GParamSpec   *pspec) +{ +	GeditSearchDialog *dlg = GEDIT_SEARCH_DIALOG (object); + +	switch (prop_id) +	{ +		case PROP_SHOW_REPLACE: +			gedit_search_dialog_set_show_replace (dlg, +							      g_value_get_boolean (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_search_dialog_get_property (GObject    *object, +				  guint       prop_id, +				  GValue     *value, +				  GParamSpec *pspec) +{ +	GeditSearchDialog *dlg = GEDIT_SEARCH_DIALOG (object); + +	switch (prop_id) +	{ +		case PROP_SHOW_REPLACE: +			g_value_set_boolean (value, dlg->priv->show_replace); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +void +gedit_search_dialog_present_with_time (GeditSearchDialog *dialog, +				       guint32            timestamp) +{ +	g_return_if_fail (GEDIT_SEARCH_DIALOG (dialog)); + +	gtk_window_present_with_time (GTK_WINDOW (dialog), timestamp); + +	gtk_widget_grab_focus (dialog->priv->search_text_entry);	 +} + +static gboolean +show_replace (GeditSearchDialog *dlg) +{ +	gedit_search_dialog_set_show_replace (dlg, TRUE); +	 +	return TRUE; +} + +static void  +gedit_search_dialog_class_init (GeditSearchDialogClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GtkBindingSet *binding_set; +	 +	object_class->set_property = gedit_search_dialog_set_property; +	object_class->get_property = gedit_search_dialog_get_property; +	 +	klass->show_replace = show_replace; +	 +	dialog_signals[SHOW_REPLACE] = +    		g_signal_new ("show_replace", +		  	      G_TYPE_FROM_CLASS (object_class), +		  	      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +		  	      G_STRUCT_OFFSET (GeditSearchDialogClass, show_replace), +			      NULL, NULL, +			      gedit_marshal_BOOLEAN__NONE, +			      G_TYPE_BOOLEAN, 0); +			       +	g_object_class_install_property (object_class, PROP_SHOW_REPLACE, +					 g_param_spec_boolean ("show-replace", +					 		       "Show Replace", +					 		       "Whether the dialog is used for Search&Replace", +					 		       FALSE, +					 		       G_PARAM_READWRITE)); + +	g_type_class_add_private (object_class, sizeof (GeditSearchDialogPrivate)); + +	binding_set = gtk_binding_set_by_class (klass); +	 +	/* Note: we cannot use the keyval/modifier associated with the  +	 * GTK_STOCK_FIND_AND_REPLACE stock item since MATE HIG suggests Ctrl+h +	 * for Replace while gtk+ uses Ctrl+r */ +	gtk_binding_entry_add_signal (binding_set, GDK_h, GDK_CONTROL_MASK, "show_replace", 0); +	gtk_binding_entry_add_signal (binding_set, GDK_H, GDK_CONTROL_MASK, "show_replace", 0);		 +} + +static void +insert_text_handler (GtkEditable *editable, +		     const gchar *text, +		     gint         length, +		     gint        *position, +		     gpointer     data) +{ +	static gboolean insert_text = FALSE; +	gchar *escaped_text; +	gint new_len; + +	/* To avoid recursive behavior */ +	if (insert_text) +		return; + +	escaped_text = gedit_utils_escape_search_text (text); + +	new_len = strlen (escaped_text); + +	if (new_len == length) +	{ +		g_free (escaped_text); +		return; +	} + +	insert_text = TRUE; + +	g_signal_stop_emission_by_name (editable, "insert_text"); + +	gtk_editable_insert_text (editable, escaped_text, new_len, position); + +	insert_text = FALSE; + +	g_free (escaped_text); +} + +static void +search_text_entry_changed (GtkEditable       *editable, +			   GeditSearchDialog *dialog) +{ +	const gchar *search_string; + +	search_string = gtk_entry_get_text (GTK_ENTRY (editable)); +	g_return_if_fail (search_string != NULL); + +	if (*search_string != '\0') +	{ +		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),  +			GEDIT_SEARCH_DIALOG_FIND_RESPONSE, TRUE); +		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),  +			GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE, TRUE); +	} +	else +	{ +		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),  +			GEDIT_SEARCH_DIALOG_FIND_RESPONSE, FALSE); +		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),  +			GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE, FALSE); +		gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),  +			GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE, FALSE); +	} +} + +static void +response_handler (GeditSearchDialog *dialog, +		  gint               response_id, +		  gpointer           data) +{ +	const gchar *str; + +	switch (response_id) +	{ +		case GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE: +		case GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE: +			str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_text_entry)); +			if (*str != '\0') +			{ +				gchar *text; + +				text = gedit_utils_unescape_search_text (str); +				gedit_history_entry_prepend_text +						(GEDIT_HISTORY_ENTRY (dialog->priv->replace_entry), +						 text); + +				g_free (text); +			} +			/* fall through, so that we also save the find entry */ +		case GEDIT_SEARCH_DIALOG_FIND_RESPONSE: +			str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_text_entry)); +			if (*str != '\0') +			{ +				gchar *text; + +				text = gedit_utils_unescape_search_text (str); +				gedit_history_entry_prepend_text +						(GEDIT_HISTORY_ENTRY (dialog->priv->search_entry), +						 text); + +				g_free (text); +			} +	} +} + +static void +show_replace_widgets (GeditSearchDialog *dlg, +		      gboolean           show_replace) +{ +	if (show_replace) +	{ +		gtk_widget_show (dlg->priv->replace_label); +		gtk_widget_show (dlg->priv->replace_entry); +		gtk_widget_show (dlg->priv->replace_all_button); +		gtk_widget_show (dlg->priv->replace_button); + +		gtk_table_set_row_spacings (GTK_TABLE (dlg->priv->table), 12); + +		gtk_window_set_title (GTK_WINDOW (dlg), _("Replace")); +	} +	else +	{ +		gtk_widget_hide (dlg->priv->replace_label); +		gtk_widget_hide (dlg->priv->replace_entry); +		gtk_widget_hide (dlg->priv->replace_all_button); +		gtk_widget_hide (dlg->priv->replace_button); + +		gtk_table_set_row_spacings (GTK_TABLE (dlg->priv->table), 0); + +		gtk_window_set_title (GTK_WINDOW (dlg), _("Find")); +	} + +	gtk_widget_show (dlg->priv->find_button); +} + +static void +gedit_search_dialog_init (GeditSearchDialog *dlg) +{ +	GtkWidget *content; +	GtkWidget *error_widget; +	gboolean ret; +	gchar *file; +	gchar *root_objects[] = { +		"search_dialog_content", +		NULL +	}; + +	dlg->priv = GEDIT_SEARCH_DIALOG_GET_PRIVATE (dlg); + +	gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); +	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE); +	gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), TRUE); + +	gtk_dialog_add_buttons (GTK_DIALOG (dlg), +				GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, +				NULL); +				 +	/* HIG defaults */ +	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			     2); /* 2 * 5 + 2 = 12 */ +	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), +					5); +	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dlg))), +			     6); + +	file = gedit_dirs_get_ui_file ("gedit-search-dialog.ui"); +	ret = gedit_utils_get_ui_objects (file, +					  root_objects, +					  &error_widget, +					  "search_dialog_content", &content, +					  "table", &dlg->priv->table, +					  "search_label", &dlg->priv->search_label, +					  "replace_with_label", &dlg->priv->replace_label, +					  "match_case_checkbutton", &dlg->priv->match_case_checkbutton, +					  "entire_word_checkbutton", &dlg->priv->entire_word_checkbutton, +					  "search_backwards_checkbutton", &dlg->priv->backwards_checkbutton, +					  "wrap_around_checkbutton", &dlg->priv->wrap_around_checkbutton, +					  NULL); +	g_free (file); + +	if (!ret) +	{ +		gtk_widget_show (error_widget); + +		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +		                    error_widget, +		                    TRUE, TRUE, 0); +		gtk_container_set_border_width (GTK_CONTAINER (error_widget), +						5); + +		dlg->priv->ui_error = TRUE; + +		return; +	} + +	dlg->priv->search_entry = gedit_history_entry_new ("gedit2_search_for_entry", +							   TRUE); +	gtk_widget_set_size_request (dlg->priv->search_entry, 300, -1); +	gedit_history_entry_set_escape_func +			(GEDIT_HISTORY_ENTRY (dlg->priv->search_entry), +			 (GeditHistoryEntryEscapeFunc) gedit_utils_escape_search_text); + +	dlg->priv->search_text_entry = gedit_history_entry_get_entry +			(GEDIT_HISTORY_ENTRY (dlg->priv->search_entry)); +	gtk_entry_set_activates_default (GTK_ENTRY (dlg->priv->search_text_entry), +					 TRUE); +	gtk_widget_show (dlg->priv->search_entry); +	gtk_table_attach_defaults (GTK_TABLE (dlg->priv->table), +				   dlg->priv->search_entry, +				   1, 2, 0, 1); + +	dlg->priv->replace_entry = gedit_history_entry_new ("gedit2_replace_with_entry", +							    TRUE); +	gedit_history_entry_set_escape_func +			(GEDIT_HISTORY_ENTRY (dlg->priv->replace_entry), +			 (GeditHistoryEntryEscapeFunc) gedit_utils_escape_search_text); + +	dlg->priv->replace_text_entry = gedit_history_entry_get_entry +			(GEDIT_HISTORY_ENTRY (dlg->priv->replace_entry)); +	gtk_entry_set_activates_default (GTK_ENTRY (dlg->priv->replace_text_entry), +					 TRUE); +	gtk_widget_show (dlg->priv->replace_entry); +	gtk_table_attach_defaults (GTK_TABLE (dlg->priv->table), +				   dlg->priv->replace_entry, +				   1, 2, 1, 2); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (dlg->priv->search_label), +				       dlg->priv->search_entry); +	gtk_label_set_mnemonic_widget (GTK_LABEL (dlg->priv->replace_label), +				       dlg->priv->replace_entry); + +	dlg->priv->find_button = gtk_button_new_from_stock (GTK_STOCK_FIND); +	dlg->priv->replace_all_button = gtk_button_new_with_mnemonic (_("Replace _All")); +	dlg->priv->replace_button = gedit_gtk_button_new_with_stock_icon (_("_Replace"), +									  GTK_STOCK_FIND_AND_REPLACE); + +	gtk_dialog_add_action_widget (GTK_DIALOG (dlg), +				      dlg->priv->replace_all_button, +				      GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE); +	gtk_dialog_add_action_widget (GTK_DIALOG (dlg), +				      dlg->priv->replace_button, +				      GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE); +	gtk_dialog_add_action_widget (GTK_DIALOG (dlg), +				      dlg->priv->find_button, +				      GEDIT_SEARCH_DIALOG_FIND_RESPONSE); +	g_object_set (G_OBJECT (dlg->priv->find_button), +		      "can-default", TRUE, +		      NULL); + +	gtk_dialog_set_default_response (GTK_DIALOG (dlg), +					 GEDIT_SEARCH_DIALOG_FIND_RESPONSE); + +	/* insensitive by default */ +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dlg), +					   GEDIT_SEARCH_DIALOG_FIND_RESPONSE, +					   FALSE); +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dlg), +					   GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE, +					   FALSE); +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dlg), +					   GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE, +					   FALSE); + +	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), +			    content, FALSE, FALSE, 0); +	g_object_unref (content); +	gtk_container_set_border_width (GTK_CONTAINER (content), 5); + +	g_signal_connect (dlg->priv->search_text_entry, +			  "insert_text", +			  G_CALLBACK (insert_text_handler), +			  NULL); +	g_signal_connect (dlg->priv->replace_text_entry, +			  "insert_text", +			  G_CALLBACK (insert_text_handler), +			  NULL); +	g_signal_connect (dlg->priv->search_text_entry, +			  "changed", +			  G_CALLBACK (search_text_entry_changed), +			  dlg); + +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (response_handler), +			  NULL); +} + +GtkWidget * +gedit_search_dialog_new (GtkWindow *parent, +			 gboolean   show_replace) +{ +	GeditSearchDialog *dlg; + +	dlg = g_object_new (GEDIT_TYPE_SEARCH_DIALOG, +			    "show-replace", show_replace, +			    NULL); + +	if (parent != NULL) +	{ +		gtk_window_set_transient_for (GTK_WINDOW (dlg), +					      parent); +	 +		gtk_window_set_destroy_with_parent (GTK_WINDOW (dlg), +						    TRUE); +	} +	 +	return GTK_WIDGET (dlg); +} + +gboolean +gedit_search_dialog_get_show_replace (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), FALSE); + +	return dialog->priv->show_replace; +} + +void +gedit_search_dialog_set_show_replace (GeditSearchDialog *dialog, +				      gboolean           show_replace) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); + +	if (dialog->priv->ui_error) +		return; + +	dialog->priv->show_replace = show_replace != FALSE; +	show_replace_widgets (dialog, dialog->priv->show_replace); + +	g_object_notify (G_OBJECT (dialog), "show-replace"); +} + +void +gedit_search_dialog_set_search_text (GeditSearchDialog *dialog, +				     const gchar       *text) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); +	g_return_if_fail (text != NULL); + +	gtk_entry_set_text (GTK_ENTRY (dialog->priv->search_text_entry), +			    text); + +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), +					   GEDIT_SEARCH_DIALOG_FIND_RESPONSE, +					   (text != '\0')); +					    +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), +					   GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE, +					   (text != '\0'));					    +} + +/* + * The text must be unescaped before searching. + */ +const gchar * +gedit_search_dialog_get_search_text (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), NULL); + +	return gtk_entry_get_text (GTK_ENTRY (dialog->priv->search_text_entry)); +} + +void +gedit_search_dialog_set_replace_text (GeditSearchDialog *dialog, +				      const gchar       *text) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); +	g_return_if_fail (text != NULL); + +	gtk_entry_set_text (GTK_ENTRY (dialog->priv->replace_text_entry), +			    text); +} + +const gchar * +gedit_search_dialog_get_replace_text (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), NULL); + +	return gtk_entry_get_text (GTK_ENTRY (dialog->priv->replace_text_entry)); +} + +void +gedit_search_dialog_set_match_case (GeditSearchDialog *dialog, +				    gboolean           match_case) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->match_case_checkbutton), +				      match_case); +} + +gboolean +gedit_search_dialog_get_match_case (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), FALSE); + +	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->match_case_checkbutton)); +} + +void +gedit_search_dialog_set_entire_word (GeditSearchDialog *dialog, +				     gboolean           entire_word) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->entire_word_checkbutton), +				      entire_word); +} + +gboolean +gedit_search_dialog_get_entire_word (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), FALSE); + +	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->entire_word_checkbutton)); +} + +void +gedit_search_dialog_set_backwards (GeditSearchDialog *dialog, +				  gboolean           backwards) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards_checkbutton), +				      backwards); +} + +gboolean +gedit_search_dialog_get_backwards (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), FALSE); + +	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->backwards_checkbutton)); +} + +void +gedit_search_dialog_set_wrap_around (GeditSearchDialog *dialog, +				     gboolean           wrap_around) +{ +	g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog)); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap_around_checkbutton), +				      wrap_around); +} + +gboolean +gedit_search_dialog_get_wrap_around (GeditSearchDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_SEARCH_DIALOG (dialog), FALSE); + +	return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->wrap_around_checkbutton)); +} diff --git a/gedit/dialogs/gedit-search-dialog.h b/gedit/dialogs/gedit-search-dialog.h new file mode 100755 index 00000000..a225be14 --- /dev/null +++ b/gedit/dialogs/gedit-search-dialog.h @@ -0,0 +1,128 @@ +/* + * gedit-search-dialog.h + * This file is part of gedit + * + * Copyright (C) 2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_SEARCH_DIALOG_H__ +#define __GEDIT_SEARCH_DIALOG_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_SEARCH_DIALOG              (gedit_search_dialog_get_type()) +#define GEDIT_SEARCH_DIALOG(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_SEARCH_DIALOG, GeditSearchDialog)) +#define GEDIT_SEARCH_DIALOG_CONST(obj)        (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_SEARCH_DIALOG, GeditSearchDialog const)) +#define GEDIT_SEARCH_DIALOG_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_SEARCH_DIALOG, GeditSearchDialogClass)) +#define GEDIT_IS_SEARCH_DIALOG(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_SEARCH_DIALOG)) +#define GEDIT_IS_SEARCH_DIALOG_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_SEARCH_DIALOG)) +#define GEDIT_SEARCH_DIALOG_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_SEARCH_DIALOG, GeditSearchDialogClass)) + +/* Private structure type */ +typedef struct _GeditSearchDialogPrivate GeditSearchDialogPrivate; + +/* + * Main object structure + */ +typedef struct _GeditSearchDialog GeditSearchDialog; + +struct _GeditSearchDialog  +{ +	GtkDialog dialog; + +	/*< private > */ +	GeditSearchDialogPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditSearchDialogClass GeditSearchDialogClass; + +struct _GeditSearchDialogClass  +{ +	GtkDialogClass parent_class; +	 +	/* Key bindings */ +	gboolean (* show_replace) (GeditSearchDialog *dlg); +}; + +enum +{ +	GEDIT_SEARCH_DIALOG_FIND_RESPONSE = 100, +	GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE, +	GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE +}; + +/* + * Public methods + */ +GType 		 gedit_search_dialog_get_type 		(void) G_GNUC_CONST; + +GtkWidget	*gedit_search_dialog_new		(GtkWindow         *parent, +							 gboolean           show_replace); + +void		 gedit_search_dialog_present_with_time	(GeditSearchDialog *dialog, +							 guint32 timestamp); + +gboolean	 gedit_search_dialog_get_show_replace	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_show_replace	(GeditSearchDialog *dialog, +							 gboolean           show_replace); + + +void		 gedit_search_dialog_set_search_text	(GeditSearchDialog *dialog, +							 const gchar       *text); +const gchar	*gedit_search_dialog_get_search_text	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_replace_text	(GeditSearchDialog *dialog, +							 const gchar       *text); +const gchar	*gedit_search_dialog_get_replace_text	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_match_case	(GeditSearchDialog *dialog, +							 gboolean           match_case); +gboolean	 gedit_search_dialog_get_match_case	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_entire_word	(GeditSearchDialog *dialog, +							 gboolean           entire_word); +gboolean	 gedit_search_dialog_get_entire_word	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_backwards	(GeditSearchDialog *dialog, +							 gboolean           backwards); +gboolean	 gedit_search_dialog_get_backwards	(GeditSearchDialog *dialog); + +void		 gedit_search_dialog_set_wrap_around	(GeditSearchDialog *dialog, +							 gboolean           wrap_around); +gboolean	 gedit_search_dialog_get_wrap_around	(GeditSearchDialog *dialog); +    +G_END_DECLS + +#endif  /* __GEDIT_SEARCH_DIALOG_H__  */ diff --git a/gedit/dialogs/gedit-search-dialog.ui b/gedit/dialogs/gedit-search-dialog.ui new file mode 100755 index 00000000..35b6c390 --- /dev/null +++ b/gedit/dialogs/gedit-search-dialog.ui @@ -0,0 +1,255 @@ +<?xml version="1.0"?> +<!--*- mode: xml -*--> +<interface> +  <object class="GtkDialog" id="dialog"> +    <property name="title" translatable="yes">Replace</property> +    <property name="type">GTK_WINDOW_TOPLEVEL</property> +    <property name="window_position">GTK_WIN_POS_NONE</property> +    <property name="modal">False</property> +    <property name="resizable">False</property> +    <property name="destroy_with_parent">False</property> +    <property name="decorated">True</property> +    <property name="skip_taskbar_hint">False</property> +    <property name="skip_pager_hint">False</property> +    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> +    <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> +    <property name="focus_on_map">True</property> +    <property name="urgency_hint">False</property> +    <property name="has_separator">False</property> +    <child internal-child="vbox"> +      <object class="GtkVBox" id="dialog-vbox1"> +        <property name="visible">True</property> +        <property name="homogeneous">False</property> +        <property name="spacing">8</property> +        <child internal-child="action_area"> +          <object class="GtkHButtonBox" id="dialog-action_area1"> +            <property name="visible">True</property> +            <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property> +            <child> +              <object class="GtkButton" id="close_button"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label">gtk-close</property> +                <property name="use_stock">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +                <property name="focus_on_click">True</property> +              </object> +            </child> +            <child> +              <object class="GtkButton" id="replace_all_button"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label" translatable="yes">Replace All</property> +                <property name="use_underline">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +                <property name="focus_on_click">True</property> +              </object> +            </child> +            <child> +              <object class="GtkButton" id="replace_button"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label" translatable="yes">Replace</property> +                <property name="use_underline">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +                <property name="focus_on_click">True</property> +              </object> +            </child> +            <child> +              <object class="GtkButton" id="find_next_button"> +                <property name="visible">True</property> +                <property name="can_default">True</property> +                <property name="can_focus">True</property> +                <property name="label">gtk-find</property> +                <property name="use_stock">True</property> +                <property name="relief">GTK_RELIEF_NORMAL</property> +                <property name="focus_on_click">True</property> +              </object> +            </child> +          </object> +          <packing> +            <property name="padding">0</property> +            <property name="expand">False</property> +            <property name="fill">False</property> +            <property name="pack_type">GTK_PACK_END</property> +          </packing> +        </child> +        <child> +          <object class="GtkVBox" id="search_dialog_content"> +            <property name="border_width">5</property> +            <property name="visible">True</property> +            <property name="homogeneous">False</property> +            <property name="spacing">18</property> +            <child> +              <object class="GtkTable" id="table"> +                <property name="visible">True</property> +                <property name="n_rows">2</property> +                <property name="n_columns">2</property> +                <property name="homogeneous">False</property> +                <property name="row_spacing">12</property> +                <property name="column_spacing">12</property> +                <child> +                  <object class="GtkLabel" id="search_label"> +                    <property name="visible">True</property> +                    <property name="label" translatable="yes">_Search for: </property> +                    <property name="use_underline">True</property> +                    <property name="use_markup">False</property> +                    <property name="justify">GTK_JUSTIFY_LEFT</property> +                    <property name="wrap">False</property> +                    <property name="selectable">False</property> +                    <property name="xalign">0</property> +                    <property name="yalign">0.5</property> +                    <property name="xpad">0</property> +                    <property name="ypad">0</property> +                    <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> +                    <property name="width_chars">-1</property> +                    <property name="single_line_mode">False</property> +                    <property name="angle">0</property> +                  </object> +                  <packing> +                    <property name="left_attach">0</property> +                    <property name="right_attach">1</property> +                    <property name="top_attach">0</property> +                    <property name="bottom_attach">1</property> +                    <property name="x_options">fill</property> +                    <property name="y_options"/> +                  </packing> +                </child> +                <child> +                  <object class="GtkLabel" id="replace_with_label"> +                    <property name="visible">True</property> +                    <property name="label" translatable="yes">Replace _with: </property> +                    <property name="use_underline">True</property> +                    <property name="use_markup">False</property> +                    <property name="justify">GTK_JUSTIFY_LEFT</property> +                    <property name="wrap">False</property> +                    <property name="selectable">False</property> +                    <property name="xalign">0</property> +                    <property name="yalign">0.5</property> +                    <property name="xpad">0</property> +                    <property name="ypad">0</property> +                    <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> +                    <property name="width_chars">-1</property> +                    <property name="single_line_mode">False</property> +                    <property name="angle">0</property> +                  </object> +                  <packing> +                    <property name="left_attach">0</property> +                    <property name="right_attach">1</property> +                    <property name="top_attach">1</property> +                    <property name="bottom_attach">2</property> +                    <property name="x_options">fill</property> +                    <property name="y_options"/> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="padding">0</property> +                <property name="expand">True</property> +                <property name="fill">True</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox3"> +                <property name="visible">True</property> +                <property name="homogeneous">False</property> +                <property name="spacing">12</property> +                <child> +                  <object class="GtkCheckButton" id="match_case_checkbutton"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="label" translatable="yes">_Match case</property> +                    <property name="use_underline">True</property> +                    <property name="relief">GTK_RELIEF_NORMAL</property> +                    <property name="focus_on_click">True</property> +                    <property name="active">False</property> +                    <property name="inconsistent">False</property> +                    <property name="draw_indicator">True</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkCheckButton" id="entire_word_checkbutton"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="label" translatable="yes">Match _entire word only</property> +                    <property name="use_underline">True</property> +                    <property name="relief">GTK_RELIEF_NORMAL</property> +                    <property name="focus_on_click">True</property> +                    <property name="active">False</property> +                    <property name="inconsistent">False</property> +                    <property name="draw_indicator">True</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkCheckButton" id="search_backwards_checkbutton"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="label" translatable="yes">Search _backwards</property> +                    <property name="use_underline">True</property> +                    <property name="relief">GTK_RELIEF_NORMAL</property> +                    <property name="focus_on_click">True</property> +                    <property name="active">False</property> +                    <property name="inconsistent">False</property> +                    <property name="draw_indicator">True</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkCheckButton" id="wrap_around_checkbutton"> +                    <property name="visible">True</property> +                    <property name="can_focus">True</property> +                    <property name="label" translatable="yes">_Wrap around</property> +                    <property name="use_underline">True</property> +                    <property name="relief">GTK_RELIEF_NORMAL</property> +                    <property name="focus_on_click">True</property> +                    <property name="active">True</property> +                    <property name="inconsistent">False</property> +                    <property name="draw_indicator">True</property> +                  </object> +                  <packing> +                    <property name="padding">0</property> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="padding">0</property> +                <property name="expand">True</property> +                <property name="fill">True</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="padding">0</property> +            <property name="expand">True</property> +            <property name="fill">True</property> +          </packing> +        </child> +      </object> +    </child> +    <action-widgets> +      <action-widget response="0">close_button</action-widget> +      <action-widget response="0">replace_all_button</action-widget> +      <action-widget response="0">replace_button</action-widget> +      <action-widget response="0">find_next_button</action-widget> +    </action-widgets> +  </object> +</interface> diff --git a/gedit/gedit-app.c b/gedit/gedit-app.c new file mode 100755 index 00000000..14148fef --- /dev/null +++ b/gedit/gedit-app.c @@ -0,0 +1,911 @@ +/* + * gedit-app.c + * This file is part of gedit + * + * Copyright (C) 2005-2006 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n.h> + +#include "gedit-app.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-commands.h" +#include "gedit-notebook.h" +#include "gedit-debug.h" +#include "gedit-utils.h" +#include "gedit-enum-types.h" +#include "gedit-dirs.h" + +#ifdef OS_OSX +#include <ige-mac-integration.h> +#endif + +#define GEDIT_PAGE_SETUP_FILE		"gedit-page-setup" +#define GEDIT_PRINT_SETTINGS_FILE	"gedit-print-settings" + +#define GEDIT_APP_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_APP, GeditAppPrivate)) + +/* Properties */ +enum +{ +	PROP_0, +	PROP_LOCKDOWN +}; + +struct _GeditAppPrivate +{ +	GList	          *windows; +	GeditWindow       *active_window; + +	GeditLockdownMask  lockdown; + +	GtkPageSetup      *page_setup; +	GtkPrintSettings  *print_settings; +}; + +G_DEFINE_TYPE(GeditApp, gedit_app, G_TYPE_OBJECT) + +static void +gedit_app_finalize (GObject *object) +{ +	GeditApp *app = GEDIT_APP (object); + +	g_list_free (app->priv->windows); + +	if (app->priv->page_setup) +		g_object_unref (app->priv->page_setup); +	if (app->priv->print_settings) +		g_object_unref (app->priv->print_settings); + +	G_OBJECT_CLASS (gedit_app_parent_class)->finalize (object); +} + +static void +gedit_app_get_property (GObject    *object, +			guint       prop_id, +			GValue     *value, +			GParamSpec *pspec) +{ +	GeditApp *app = GEDIT_APP (object); + +	switch (prop_id) +	{ +		case PROP_LOCKDOWN: +			g_value_set_flags (value, gedit_app_get_lockdown (app)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_app_class_init (GeditAppClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_app_finalize; +	object_class->get_property = gedit_app_get_property; + +	g_object_class_install_property (object_class, +					 PROP_LOCKDOWN, +					 g_param_spec_flags ("lockdown", +							     "Lockdown", +							     "The lockdown mask", +							     GEDIT_TYPE_LOCKDOWN_MASK, +							     0, +							     G_PARAM_READABLE | +							     G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof(GeditAppPrivate)); +} + +static gboolean +ensure_user_config_dir (void) +{ +	gchar *config_dir; +	gboolean ret = TRUE; +	gint res; + +	config_dir = gedit_dirs_get_user_config_dir (); +	if (config_dir == NULL) +	{ +		g_warning ("Could not get config directory\n"); +		return FALSE; +	} + +	res = g_mkdir_with_parents (config_dir, 0755); +	if (res < 0) +	{ +		g_warning ("Could not create config directory\n"); +		ret = FALSE; +	} + +	g_free (config_dir); + +	return ret; +} + +static void +load_accels (void) +{ +	gchar *filename; + +	filename = gedit_dirs_get_user_accels_file (); +	if (filename != NULL) +	{ +		gedit_debug_message (DEBUG_APP, "Loading keybindings from %s\n", filename); +		gtk_accel_map_load (filename); +		g_free (filename); +	} +} + +static void +save_accels (void) +{ +	gchar *filename; + +	filename = gedit_dirs_get_user_accels_file (); +	if (filename != NULL) +	{ +		gedit_debug_message (DEBUG_APP, "Saving keybindings in %s\n", filename); +		gtk_accel_map_save (filename); +		g_free (filename); +	} +} + +static gchar * +get_page_setup_file (void) +{ +	gchar *config_dir; +	gchar *setup = NULL; + +	config_dir = gedit_dirs_get_user_config_dir (); + +	if (config_dir != NULL) +	{ +		setup = g_build_filename (config_dir, +					  GEDIT_PAGE_SETUP_FILE, +					  NULL); +		g_free (config_dir); +	} + +	return setup; +} + +static void +load_page_setup (GeditApp *app) +{ +	gchar *filename; +	GError *error = NULL; + +	g_return_if_fail (app->priv->page_setup == NULL); + +	filename = get_page_setup_file (); + +	app->priv->page_setup = gtk_page_setup_new_from_file (filename, +							      &error); +	if (error) +	{ +		/* Ignore file not found error */ +		if (error->domain != G_FILE_ERROR || +		    error->code != G_FILE_ERROR_NOENT) +		{ +			g_warning ("%s", error->message); +		} + +		g_error_free (error); +	} + +	g_free (filename); + +	/* fall back to default settings */ +	if (app->priv->page_setup == NULL) +		app->priv->page_setup = gtk_page_setup_new (); +} + +static void +save_page_setup (GeditApp *app) +{ +	gchar *filename; +	GError *error = NULL; + +	if (app->priv->page_setup == NULL) +		return; + +	filename = get_page_setup_file (); + +	gtk_page_setup_to_file (app->priv->page_setup, +				filename, +				&error); +	if (error) +	{ +		g_warning ("%s", error->message); +		g_error_free (error); +	} + +	g_free (filename); +} + +static gchar * +get_print_settings_file (void) +{ +	gchar *config_dir; +	gchar *settings = NULL; + +	config_dir = gedit_dirs_get_user_config_dir (); + +	if (config_dir != NULL) +	{ +		settings = g_build_filename (config_dir, +					     GEDIT_PRINT_SETTINGS_FILE, +					     NULL); +		g_free (config_dir); +	} + +	return settings; +} + +static void +load_print_settings (GeditApp *app) +{ +	gchar *filename; +	GError *error = NULL; + +	g_return_if_fail (app->priv->print_settings == NULL); + +	filename = get_print_settings_file (); + +	app->priv->print_settings = gtk_print_settings_new_from_file (filename, +								      &error); +	if (error) +	{ +		/* Ignore file not found error */ +		if (error->domain != G_FILE_ERROR || +		    error->code != G_FILE_ERROR_NOENT) +		{ +			g_warning ("%s", error->message); +		} + +		g_error_free (error); +	} + +	g_free (filename); + +	/* fall back to default settings */ +	if (app->priv->print_settings == NULL) +		app->priv->print_settings = gtk_print_settings_new (); +} + +static void +save_print_settings (GeditApp *app) +{ +	gchar *filename; +	GError *error = NULL; + +	if (app->priv->print_settings == NULL) +		return; + +	filename = get_print_settings_file (); + +	gtk_print_settings_to_file (app->priv->print_settings, +				    filename, +				    &error); +	if (error) +	{ +		g_warning ("%s", error->message); +		g_error_free (error); +	} + +	g_free (filename); +} + +static void +gedit_app_init (GeditApp *app) +{ +	app->priv = GEDIT_APP_GET_PRIVATE (app); + +	load_accels (); + +	/* initial lockdown state */ +	app->priv->lockdown = gedit_prefs_manager_get_lockdown (); +} + +static void +app_weak_notify (gpointer data, +		 GObject *where_the_app_was) +{ +	gtk_main_quit (); +} + +/** + * gedit_app_get_default: + * + * Returns the #GeditApp object. This object is a singleton and + * represents the running gedit instance. + * + * Return value: the #GeditApp pointer + */ +GeditApp * +gedit_app_get_default (void) +{ +	static GeditApp *app = NULL; + +	if (app != NULL) +		return app; + +	app = GEDIT_APP (g_object_new (GEDIT_TYPE_APP, NULL)); + +	g_object_add_weak_pointer (G_OBJECT (app), +				   (gpointer) &app); +	g_object_weak_ref (G_OBJECT (app), +			   app_weak_notify, +			   NULL); + +	return app; +} + +static void +set_active_window (GeditApp    *app, +                   GeditWindow *window) +{ +	app->priv->active_window = window; +} + +static gboolean +window_focus_in_event (GeditWindow   *window, +		       GdkEventFocus *event, +		       GeditApp      *app) +{ +	/* updates active_view and active_child when a new toplevel receives focus */ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); + +	set_active_window (app, window); + +	return FALSE; +} + +static gboolean +window_delete_event (GeditWindow *window, +                     GdkEvent    *event, +                     GeditApp    *app) +{ +	GeditWindowState ws; + +	ws = gedit_window_get_state (window); + +	if (ws & +	    (GEDIT_WINDOW_STATE_SAVING | +	     GEDIT_WINDOW_STATE_PRINTING | +	     GEDIT_WINDOW_STATE_SAVING_SESSION)) +	    	return TRUE; + +	_gedit_cmd_file_quit (NULL, window); + +	/* Do not destroy the window */ +	return TRUE; +} + +static void +window_destroy (GeditWindow *window, +		GeditApp    *app) +{ +	app->priv->windows = g_list_remove (app->priv->windows, +					    window); + +	if (window == app->priv->active_window) +	{ +		set_active_window (app, app->priv->windows != NULL ? app->priv->windows->data : NULL); +	} + +/* CHECK: I don't think we have to disconnect this function, since windows +   is being destroyed */ +/* +	g_signal_handlers_disconnect_by_func (window, +					      G_CALLBACK (window_focus_in_event), +					      app); +	g_signal_handlers_disconnect_by_func (window, +					      G_CALLBACK (window_destroy), +					      app); +*/ +	if (app->priv->windows == NULL) +	{ +#ifdef OS_OSX +		if (!GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "gedit-is-quitting-all"))) +		{ +			/* Create hidden proxy window on OS X to handle the menu */ +			gedit_app_create_window (app, NULL); +			return; +		} +#endif +		/* Last window is gone... save some settings and exit */ +		ensure_user_config_dir (); + +		save_accels (); +		save_page_setup (app); +		save_print_settings (app); + +		g_object_unref (app); +	} +} + +/* Generates a unique string for a window role */ +static gchar * +gen_role (void) +{ +	GTimeVal result; +	static gint serial; + +	g_get_current_time (&result); + +	return g_strdup_printf ("gedit-window-%ld-%ld-%d-%s", +				result.tv_sec, +				result.tv_usec, +				serial++, +				g_get_host_name ()); +} + +static GeditWindow * +gedit_app_create_window_real (GeditApp    *app, +			      gboolean     set_geometry, +			      const gchar *role) +{ +	GeditWindow *window; + +	gedit_debug (DEBUG_APP); + +	/* +	 * We need to be careful here, there is a race condition: +	 * when another gedit is launched it checks active_window, +	 * so we must do our best to ensure that active_window +	 * is never NULL when at least a window exists. +	 */ +	if (app->priv->windows == NULL) +	{ +		window = g_object_new (GEDIT_TYPE_WINDOW, NULL); +		set_active_window (app, window); +	} +	else +	{ +		window = g_object_new (GEDIT_TYPE_WINDOW, NULL); +	} + +	app->priv->windows = g_list_prepend (app->priv->windows, +					     window); + +	gedit_debug_message (DEBUG_APP, "Window created"); + +	if (role != NULL) +	{ +		gtk_window_set_role (GTK_WINDOW (window), role); +	} +	else +	{ +		gchar *newrole; + +		newrole = gen_role (); +		gtk_window_set_role (GTK_WINDOW (window), newrole); +		g_free (newrole); +	} + +	if (set_geometry) +	{ +		GdkWindowState state; +		gint w, h; + +		state = gedit_prefs_manager_get_window_state (); + +		if ((state & GDK_WINDOW_STATE_MAXIMIZED) != 0) +		{ +			gedit_prefs_manager_get_default_window_size (&w, &h); +			gtk_window_set_default_size (GTK_WINDOW (window), w, h); +			gtk_window_maximize (GTK_WINDOW (window)); +		} +		else +		{ +			gedit_prefs_manager_get_window_size (&w, &h); +			gtk_window_set_default_size (GTK_WINDOW (window), w, h); +			gtk_window_unmaximize (GTK_WINDOW (window)); +		} + +		if ((state & GDK_WINDOW_STATE_STICKY ) != 0) +			gtk_window_stick (GTK_WINDOW (window)); +		else +			gtk_window_unstick (GTK_WINDOW (window)); +	} + +	g_signal_connect (window, +			  "focus_in_event", +			  G_CALLBACK (window_focus_in_event), +			  app); +	g_signal_connect (window, +			  "delete_event", +			  G_CALLBACK (window_delete_event), +			  app); +	g_signal_connect (window, +			  "destroy", +			  G_CALLBACK (window_destroy), +			  app); + +	return window; +} + +/** + * gedit_app_create_window: + * @app: the #GeditApp + * + * Create a new #GeditWindow part of @app. + * + * Return value: the new #GeditWindow + */ +GeditWindow * +gedit_app_create_window (GeditApp  *app, +			 GdkScreen *screen) +{ +	GeditWindow *window; + +	window = gedit_app_create_window_real (app, TRUE, NULL); + +	if (screen != NULL) +		gtk_window_set_screen (GTK_WINDOW (window), screen); + +	return window; +} + +/* + * Same as _create_window, but doesn't set the geometry. + * The session manager takes care of it. Used in mate-session. + */ +GeditWindow * +_gedit_app_restore_window (GeditApp    *app, +			   const gchar *role) +{ +	GeditWindow *window; + +	window = gedit_app_create_window_real (app, FALSE, role); + +	return window; +} + +/** + * gedit_app_get_windows: + * @app: the #GeditApp + * + * Returns all the windows currently present in #GeditApp. + * + * Return value: the list of #GeditWindows objects. The list + * should not be freed + */ +const GList * +gedit_app_get_windows (GeditApp *app) +{ +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	return app->priv->windows; +} + +/** + * gedit_app_get_active_window: + * @app: the #GeditApp + * + * Retrives the #GeditWindow currently active. + * + * Return value: the active #GeditWindow + */ +GeditWindow * +gedit_app_get_active_window (GeditApp *app) +{ +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	/* make sure our active window is always realized: +	 * this is needed on startup if we launch two gedit fast +	 * enough that the second instance comes up before the +	 * first one shows its window. +	 */ +	if (!GTK_WIDGET_REALIZED (GTK_WIDGET (app->priv->active_window))) +		gtk_widget_realize (GTK_WIDGET (app->priv->active_window)); + +	return app->priv->active_window; +} + +static gboolean +is_in_viewport (GeditWindow  *window, +		GdkScreen    *screen, +		gint          workspace, +		gint          viewport_x, +		gint          viewport_y) +{ +	GdkScreen *s; +	GdkDisplay *display; +	GdkWindow *gdkwindow; +	const gchar *cur_name; +	const gchar *name; +	gint cur_n; +	gint n; +	gint ws; +	gint sc_width, sc_height; +	gint x, y, width, height; +	gint vp_x, vp_y; + +	/* Check for screen and display match */ +	display = gdk_screen_get_display (screen); +	cur_name = gdk_display_get_name (display); +	cur_n = gdk_screen_get_number (screen); + +	s = gtk_window_get_screen (GTK_WINDOW (window)); +	display = gdk_screen_get_display (s); +	name = gdk_display_get_name (display); +	n = gdk_screen_get_number (s); + +	if (strcmp (cur_name, name) != 0 || cur_n != n) +		return FALSE; + +	/* Check for workspace match */ +	ws = gedit_utils_get_window_workspace (GTK_WINDOW (window)); +	if (ws != workspace && ws != GEDIT_ALL_WORKSPACES) +		return FALSE; + +	/* Check for viewport match */ +	gdkwindow = gtk_widget_get_window (GTK_WIDGET (window)); +	gdk_window_get_position (gdkwindow, &x, &y); + +	#if GTK_CHECK_VERSION(3, 0, 0) +		width = gdk_window_get_width(gdkwindow); +		height = gdk_window_get_height(gdkwindow); +	#else +		gdk_drawable_get_size(gdkwindow, &width, &height); +	#endif + +	gedit_utils_get_current_viewport (screen, &vp_x, &vp_y); +	x += vp_x; +	y += vp_y; + +	sc_width = gdk_screen_get_width (screen); +	sc_height = gdk_screen_get_height (screen); + +	return x + width * .25 >= viewport_x && +	       x + width * .75 <= viewport_x + sc_width && +	       y  >= viewport_y && +	       y + height <= viewport_y + sc_height; +} + +/** + * _gedit_app_get_window_in_viewport + * @app: the #GeditApp + * @screen: the #GdkScreen + * @workspace: the workspace number + * @viewport_x: the viewport horizontal origin + * @viewport_y: the viewport vertical origin + * + * Since a workspace can be larger than the screen, it is divided into several + * equal parts called viewports. This function retrives the #GeditWindow in + * the given viewport of the given workspace. + * + * Return value: the #GeditWindow in the given viewport of the given workspace. + */ +GeditWindow * +_gedit_app_get_window_in_viewport (GeditApp  *app, +				   GdkScreen *screen, +				   gint       workspace, +				   gint       viewport_x, +				   gint       viewport_y) +{ +	GeditWindow *window; + +	GList *l; + +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	/* first try if the active window */ +	window = app->priv->active_window; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	if (is_in_viewport (window, screen, workspace, viewport_x, viewport_y)) +		return window; + +	/* otherwise try to see if there is a window on this workspace */ +	for (l = app->priv->windows; l != NULL; l = l->next) +	{ +		window = l->data; + +		if (is_in_viewport (window, screen, workspace, viewport_x, viewport_y)) +			return window; +	} + +	/* no window on this workspace... create a new one */ +	return gedit_app_create_window (app, screen); +} + +/** + * gedit_app_get_documents: + * @app: the #GeditApp + * + * Returns all the documents currently open in #GeditApp. + * + * Return value: a newly allocated list of #GeditDocument objects + */ +GList * +gedit_app_get_documents	(GeditApp *app) +{ +	GList *res = NULL; +	GList *windows; + +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	windows = app->priv->windows; + +	while (windows != NULL) +	{ +		res = g_list_concat (res, +				     gedit_window_get_documents (GEDIT_WINDOW (windows->data))); + +		windows = g_list_next (windows); +	} + +	return res; +} + +/** + * gedit_app_get_views: + * @app: the #GeditApp + * + * Returns all the views currently present in #GeditApp. + * + * Return value: a newly allocated list of #GeditView objects + */ +GList * +gedit_app_get_views (GeditApp *app) +{ +	GList *res = NULL; +	GList *windows; + +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	windows = app->priv->windows; + +	while (windows != NULL) +	{ +		res = g_list_concat (res, +				     gedit_window_get_views (GEDIT_WINDOW (windows->data))); + +		windows = g_list_next (windows); +	} + +	return res; +} + +/** + * gedit_app_get_lockdown: + * @app: a #GeditApp + * + * Gets the lockdown mask (see #GeditLockdownMask) for the application. + * The lockdown mask determines which functions are locked down using + * the MATE-wise lockdown MateConf keys. + **/ +GeditLockdownMask +gedit_app_get_lockdown (GeditApp *app) +{ +	g_return_val_if_fail (GEDIT_IS_APP (app), GEDIT_LOCKDOWN_ALL); + +	return app->priv->lockdown; +} + +static void +app_lockdown_changed (GeditApp *app) +{ +	GList *l; + +	for (l = app->priv->windows; l != NULL; l = l->next) +		_gedit_window_set_lockdown (GEDIT_WINDOW (l->data), +					    app->priv->lockdown); + +	g_object_notify (G_OBJECT (app), "lockdown"); +} + +void +_gedit_app_set_lockdown (GeditApp          *app, +			 GeditLockdownMask  lockdown) +{ +	g_return_if_fail (GEDIT_IS_APP (app)); + +	app->priv->lockdown = lockdown; + +	app_lockdown_changed (app); +} + +void +_gedit_app_set_lockdown_bit (GeditApp          *app, +			     GeditLockdownMask  bit, +			     gboolean           value) +{ +	g_return_if_fail (GEDIT_IS_APP (app)); + +	if (value) +		app->priv->lockdown |= bit; +	else +		app->priv->lockdown &= ~bit; + +	app_lockdown_changed (app); +} + +/* Returns a copy */ +GtkPageSetup * +_gedit_app_get_default_page_setup (GeditApp *app) +{ +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	if (app->priv->page_setup == NULL) +		load_page_setup (app); + +	return gtk_page_setup_copy (app->priv->page_setup); +} + +void +_gedit_app_set_default_page_setup (GeditApp     *app, +				   GtkPageSetup *page_setup) +{ +	g_return_if_fail (GEDIT_IS_APP (app)); +	g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup)); + +	if (app->priv->page_setup != NULL) +		g_object_unref (app->priv->page_setup); + +	app->priv->page_setup = g_object_ref (page_setup); +} + +/* Returns a copy */ +GtkPrintSettings * +_gedit_app_get_default_print_settings (GeditApp *app) +{ +	g_return_val_if_fail (GEDIT_IS_APP (app), NULL); + +	if (app->priv->print_settings == NULL) +		load_print_settings (app); + +	return gtk_print_settings_copy (app->priv->print_settings); +} + +void +_gedit_app_set_default_print_settings (GeditApp         *app, +				       GtkPrintSettings *settings) +{ +	g_return_if_fail (GEDIT_IS_APP (app)); +	g_return_if_fail (GTK_IS_PRINT_SETTINGS (settings)); + +	if (app->priv->print_settings != NULL) +		g_object_unref (app->priv->print_settings); + +	app->priv->print_settings = g_object_ref (settings); +} + diff --git a/gedit/gedit-app.h b/gedit/gedit-app.h new file mode 100755 index 00000000..5311930a --- /dev/null +++ b/gedit/gedit-app.h @@ -0,0 +1,142 @@ +/* + * gedit-app.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_APP_H__ +#define __GEDIT_APP_H__ + +#include <gtk/gtk.h> + +#include <gedit/gedit-window.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_APP              (gedit_app_get_type()) +#define GEDIT_APP(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_APP, GeditApp)) +#define GEDIT_APP_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_APP, GeditAppClass)) +#define GEDIT_IS_APP(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_APP)) +#define GEDIT_IS_APP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_APP)) +#define GEDIT_APP_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_APP, GeditAppClass)) + +/* Private structure type */ +typedef struct _GeditAppPrivate GeditAppPrivate; + +/* + * Main object structure + */ +typedef struct _GeditApp GeditApp; + +struct _GeditApp  +{ +	GObject object; + +	/*< private > */ +	GeditAppPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditAppClass GeditAppClass; + +struct _GeditAppClass  +{ +	GObjectClass parent_class; +}; + +/* + * Lockdown mask definition + */ +typedef enum +{ +	GEDIT_LOCKDOWN_COMMAND_LINE	= 1 << 0, +	GEDIT_LOCKDOWN_PRINTING		= 1 << 1, +	GEDIT_LOCKDOWN_PRINT_SETUP	= 1 << 2, +	GEDIT_LOCKDOWN_SAVE_TO_DISK	= 1 << 3, +	GEDIT_LOCKDOWN_ALL		= 0xF +} GeditLockdownMask; + +/* + * Public methods + */ +GType 		 gedit_app_get_type 			(void) G_GNUC_CONST; + +GeditApp 	*gedit_app_get_default			(void); + +GeditWindow	*gedit_app_create_window		(GeditApp  *app, +							 GdkScreen *screen); + +const GList	*gedit_app_get_windows			(GeditApp *app); +GeditWindow	*gedit_app_get_active_window		(GeditApp *app); + +/* Returns a newly allocated list with all the documents */ +GList		*gedit_app_get_documents		(GeditApp *app); + +/* Returns a newly allocated list with all the views */ +GList		*gedit_app_get_views			(GeditApp *app); + +/* Lockdown state */ +GeditLockdownMask gedit_app_get_lockdown		(GeditApp *app); + +/* + * Non exported functions + */ +GeditWindow	*_gedit_app_restore_window		(GeditApp    *app, +							 const gchar *role); +GeditWindow	*_gedit_app_get_window_in_viewport	(GeditApp     *app, +							 GdkScreen    *screen, +							 gint          workspace, +							 gint          viewport_x, +							 gint          viewport_y); +void		 _gedit_app_set_lockdown		(GeditApp          *app, +							 GeditLockdownMask  lockdown); +void		 _gedit_app_set_lockdown_bit		(GeditApp          *app, +							 GeditLockdownMask  bit, +							 gboolean           value); +/* + * This one is a gedit-window function, but we declare it here to avoid + * #include headaches since it needs the GeditLockdownMask declaration. + */ +void		 _gedit_window_set_lockdown		(GeditWindow         *window, +							 GeditLockdownMask    lockdown); + +/* global print config */ +GtkPageSetup		*_gedit_app_get_default_page_setup	(GeditApp         *app); +void			 _gedit_app_set_default_page_setup	(GeditApp         *app, +								 GtkPageSetup     *page_setup); +GtkPrintSettings	*_gedit_app_get_default_print_settings	(GeditApp         *app); +void			 _gedit_app_set_default_print_settings	(GeditApp         *app, +								 GtkPrintSettings *settings); + +G_END_DECLS + +#endif  /* __GEDIT_APP_H__  */ diff --git a/gedit/gedit-close-button.c b/gedit/gedit-close-button.c new file mode 100755 index 00000000..0e9f157e --- /dev/null +++ b/gedit/gedit-close-button.c @@ -0,0 +1,80 @@ +/* + * gedit-close-button.c + * This file is part of gedit + * + * Copyright (C) 2010 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ + +#include "gedit-close-button.h" + +G_DEFINE_TYPE (GeditCloseButton, gedit_close_button, GTK_TYPE_BUTTON) + +static void +gedit_close_button_style_set (GtkWidget *button, +			      GtkStyle *previous_style) +{ +	gint h, w; + +	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button), +					   GTK_ICON_SIZE_MENU, &w, &h); + +	gtk_widget_set_size_request (button, w + 2, h + 2); + +	GTK_WIDGET_CLASS (gedit_close_button_parent_class)->style_set (button, previous_style); +} + +static void +gedit_close_button_class_init (GeditCloseButtonClass *klass) +{ +	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + +	widget_class->style_set = gedit_close_button_style_set; +} + +static void +gedit_close_button_init (GeditCloseButton *button) +{ +	GtkRcStyle *rcstyle; +	GtkWidget *image; + +	/* make it as small as possible */ +	rcstyle = gtk_rc_style_new (); +	rcstyle->xthickness = rcstyle->ythickness = 0; +	gtk_widget_modify_style (GTK_WIDGET (button), rcstyle); +	g_object_unref (rcstyle); + +	image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, +					  GTK_ICON_SIZE_MENU); +	gtk_widget_show (image); + +	gtk_container_add (GTK_CONTAINER (button), image); +} + +GtkWidget * +gedit_close_button_new () +{ +	GeditCloseButton *button; + +	button = g_object_new (GEDIT_TYPE_CLOSE_BUTTON, +			       "relief", GTK_RELIEF_NONE, +			       "focus-on-click", FALSE, +			       NULL); + +	return GTK_WIDGET (button); +} + diff --git a/gedit/gedit-close-button.h b/gedit/gedit-close-button.h new file mode 100755 index 00000000..4f631380 --- /dev/null +++ b/gedit/gedit-close-button.h @@ -0,0 +1,56 @@ +/* + * gedit-close-button.h + * This file is part of gedit + * + * Copyright (C) 2010 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GEDIT_CLOSE_BUTTON_H__ +#define __GEDIT_CLOSE_BUTTON_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_CLOSE_BUTTON			(gedit_close_button_get_type ()) +#define GEDIT_CLOSE_BUTTON(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_CLOSE_BUTTON, GeditCloseButton)) +#define GEDIT_CLOSE_BUTTON_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_CLOSE_BUTTON, GeditCloseButton const)) +#define GEDIT_CLOSE_BUTTON_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_CLOSE_BUTTON, GeditCloseButtonClass)) +#define GEDIT_IS_CLOSE_BUTTON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_CLOSE_BUTTON)) +#define GEDIT_IS_CLOSE_BUTTON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_CLOSE_BUTTON)) +#define GEDIT_CLOSE_BUTTON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_CLOSE_BUTTON, GeditCloseButtonClass)) + +typedef struct _GeditCloseButton	GeditCloseButton; +typedef struct _GeditCloseButtonClass	GeditCloseButtonClass; +typedef struct _GeditCloseButtonPrivate	GeditCloseButtonPrivate; + +struct _GeditCloseButton { +	GtkButton parent; +}; + +struct _GeditCloseButtonClass { +	GtkButtonClass parent_class; +}; + +GType		  gedit_close_button_get_type (void) G_GNUC_CONST; + +GtkWidget	 *gedit_close_button_new (void); + +G_END_DECLS + +#endif /* __GEDIT_CLOSE_BUTTON_H__ */ diff --git a/gedit/gedit-commands-documents.c b/gedit/gedit-commands-documents.c new file mode 100755 index 00000000..016ebbe8 --- /dev/null +++ b/gedit/gedit-commands-documents.c @@ -0,0 +1,87 @@ +/* + * gedit-documents-commands.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-window.h" +#include "gedit-notebook.h" +#include "gedit-debug.h" + +void +_gedit_cmd_documents_previous_document (GtkAction   *action, +				       GeditWindow *window) +{ +	GtkNotebook *notebook; + +	gedit_debug (DEBUG_COMMANDS); + +	notebook = GTK_NOTEBOOK (_gedit_window_get_notebook (window)); +	gtk_notebook_prev_page (notebook); +} + +void +_gedit_cmd_documents_next_document (GtkAction   *action, +				   GeditWindow *window) +{ +	GtkNotebook *notebook; + +	gedit_debug (DEBUG_COMMANDS); + +	notebook = GTK_NOTEBOOK (_gedit_window_get_notebook (window)); +	gtk_notebook_next_page (notebook); +} + +void +_gedit_cmd_documents_move_to_new_window (GtkAction   *action, +					GeditWindow *window) +{ +	GeditNotebook *old_notebook; +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); + +	if (tab == NULL) +		return; + +	old_notebook = GEDIT_NOTEBOOK (_gedit_window_get_notebook (window)); + +	g_return_if_fail (gtk_notebook_get_n_pages (GTK_NOTEBOOK (old_notebook)) > 1); + +	_gedit_window_move_tab_to_new_window (window, tab); +} diff --git a/gedit/gedit-commands-edit.c b/gedit/gedit-commands-edit.c new file mode 100755 index 00000000..e2cedeb6 --- /dev/null +++ b/gedit/gedit-commands-edit.c @@ -0,0 +1,174 @@ +/* + * gedit-commands-edit.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-window.h" +#include "gedit-debug.h" +#include "gedit-view.h" +#include "dialogs/gedit-preferences-dialog.h" + +void +_gedit_cmd_edit_undo (GtkAction   *action, +		     GeditWindow *window) +{ +	GeditView *active_view; +	GtkSourceBuffer *active_document; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	active_document = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + +	gtk_source_buffer_undo (active_document); + +	gedit_view_scroll_to_cursor (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_redo (GtkAction   *action, +		     GeditWindow *window) +{ +	GeditView *active_view; +	GtkSourceBuffer *active_document; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	active_document = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + +	gtk_source_buffer_redo (active_document); + +	gedit_view_scroll_to_cursor (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_cut (GtkAction   *action, +		    GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	gedit_view_cut_clipboard (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_copy (GtkAction   *action, +		     GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	gedit_view_copy_clipboard (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_paste (GtkAction   *action, +		      GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	gedit_view_paste_clipboard (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_delete (GtkAction   *action, +		       GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	gedit_view_delete_selection (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_select_all (GtkAction   *action, +			   GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view); + +	gedit_view_select_all (active_view); + +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +} + +void +_gedit_cmd_edit_preferences (GtkAction   *action, +			    GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	gedit_show_preferences_dialog (window); +} diff --git a/gedit/gedit-commands-file-print.c b/gedit/gedit-commands-file-print.c new file mode 100755 index 00000000..455e2e21 --- /dev/null +++ b/gedit/gedit-commands-file-print.c @@ -0,0 +1,91 @@ +/* + * gedit-commands-file-print.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-window.h" +#include "gedit-tab.h" +#include "gedit-debug.h" + +#if !GTK_CHECK_VERSION (2, 17, 4) +void +_gedit_cmd_file_page_setup (GtkAction   *action, +			    GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	_gedit_tab_page_setup (tab); +} +#endif + +void +_gedit_cmd_file_print_preview (GtkAction   *action, +			       GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	_gedit_tab_print_preview (tab); +} + +void +_gedit_cmd_file_print (GtkAction   *action, +		       GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	_gedit_tab_print (tab); +} + diff --git a/gedit/gedit-commands-file.c b/gedit/gedit-commands-file.c new file mode 100755 index 00000000..985b0417 --- /dev/null +++ b/gedit/gedit-commands-file.c @@ -0,0 +1,1885 @@ +/* + * gedit-commands-file.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> /* For strlen and strcmp */ + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-window.h" +#include "gedit-window-private.h" +#include "gedit-statusbar.h" +#include "gedit-debug.h" +#include "gedit-utils.h" +#include "gedit-file-chooser-dialog.h" +#include "dialogs/gedit-close-confirmation-dialog.h" + + +/* Defined constants */ +#define GEDIT_OPEN_DIALOG_KEY 		"gedit-open-dialog-key" +#define GEDIT_TAB_TO_SAVE_AS  		"gedit-tab-to-save-as" +#define GEDIT_LIST_OF_TABS_TO_SAVE_AS   "gedit-list-of-tabs-to-save-as" +#define GEDIT_IS_CLOSING_ALL            "gedit-is-closing-all" +#define GEDIT_IS_QUITTING 	        "gedit-is-quitting" +#define GEDIT_IS_CLOSING_TAB		"gedit-is-closing-tab" +#define GEDIT_IS_QUITTING_ALL		"gedit-is-quitting-all" + +static void tab_state_changed_while_saving (GeditTab    *tab, +					    GParamSpec  *pspec, +					    GeditWindow *window); + +void +_gedit_cmd_file_new (GtkAction   *action, +		     GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	gedit_window_create_tab (window, TRUE); +} + +static GeditTab * +get_tab_from_file (GList *docs, GFile *file) +{ +	GeditTab *tab = NULL; + +	while (docs != NULL) +	{ +		GeditDocument *d; +		GFile *l; + +		d = GEDIT_DOCUMENT (docs->data); + +		l = gedit_document_get_location (d); +		if (l != NULL) +		{ +			if (g_file_equal (l, file)) +			{ +				tab = gedit_tab_get_from_document (d); +				g_object_unref (l); +				break; +			} + +			g_object_unref (l); +		} + +		docs = g_list_next (docs); +	} + +	return tab; +} + +static gboolean +is_duplicated_file (GSList *files, GFile *file) +{ +	while (files != NULL) +	{ +		if (g_file_equal (files->data, file)) +			return TRUE; + +		files = g_slist_next (files); +	} + +	return FALSE; +} + +/* File loading */ +static gint +load_file_list (GeditWindow         *window, +		GSList              *files, +		const GeditEncoding *encoding, +		gint                 line_pos, +		gboolean             create) +{ +	GeditTab      *tab; +	gint           loaded_files = 0; /* Number of files to load */ +	gboolean       jump_to = TRUE; /* Whether to jump to the new tab */ +	GList         *win_docs; +	GSList        *files_to_load = NULL; +	GSList        *l; + +	gedit_debug (DEBUG_COMMANDS); + +	win_docs = gedit_window_get_documents (window); + +	/* Remove the uris corresponding to documents already open +	 * in "window" and remove duplicates from "uris" list */ +	for (l = files; l != NULL; l = l->next) +	{ +		if (!is_duplicated_file (files_to_load, l->data)) +		{ +			tab = get_tab_from_file (win_docs, l->data); +			if (tab != NULL) +			{ +				if (l == files) +				{ +					gedit_window_set_active_tab (window, tab); +					jump_to = FALSE; + +					if (line_pos > 0) +					{ +						GeditDocument *doc; +						GeditView *view; + +						doc = gedit_tab_get_document (tab); +						view = gedit_tab_get_view (tab); + +						/* document counts lines starting from 0 */ +						gedit_document_goto_line (doc, line_pos - 1); +						gedit_view_scroll_to_cursor (view); +					} +				} + +				++loaded_files; +			} +			else +			{ +				files_to_load = g_slist_prepend (files_to_load,  +								 l->data); +			} +		} +	} + +	g_list_free (win_docs); + +	if (files_to_load == NULL) +		return loaded_files; +	 +	files_to_load = g_slist_reverse (files_to_load); +	l = files_to_load; + +	tab = gedit_window_get_active_tab (window); +	if (tab != NULL) +	{ +		GeditDocument *doc; + +		doc = gedit_tab_get_document (tab); + +		if (gedit_document_is_untouched (doc) && +		    (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_NORMAL)) +		{ +			gchar *uri; + +			// FIXME: pass the GFile to tab when api is there +			uri = g_file_get_uri (l->data); +			_gedit_tab_load (tab, +					 uri, +					 encoding, +					 line_pos, +					 create); +			g_free (uri); + +			l = g_slist_next (l); +			jump_to = FALSE; + +			++loaded_files; +		} +	} + +	while (l != NULL) +	{ +		gchar *uri; + +		g_return_val_if_fail (l->data != NULL, 0); + +		// FIXME: pass the GFile to tab when api is there +		uri = g_file_get_uri (l->data); +		tab = gedit_window_create_tab_from_uri (window, +							uri, +							encoding, +							line_pos, +							create, +							jump_to); +		g_free (uri); + +		if (tab != NULL) +		{ +			jump_to = FALSE; +			++loaded_files; +		} + +		l = g_slist_next (l); +	} + +	if (loaded_files == 1) +	{ +		GeditDocument *doc; +		gchar *uri_for_display; + +		g_return_val_if_fail (tab != NULL, loaded_files); + +		doc = gedit_tab_get_document (tab); +		uri_for_display = gedit_document_get_uri_for_display (doc); + +		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +					       window->priv->generic_message_cid, +					       _("Loading file '%s'\342\200\246"), +					       uri_for_display); + +		g_free (uri_for_display); +	} +	else +	{ +		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +					       window->priv->generic_message_cid, +					       ngettext("Loading %d file\342\200\246", +							"Loading %d files\342\200\246", +							loaded_files), +					       loaded_files); +	} + +	/* Free uris_to_load. Note that l points to the first element of uris_to_load */ +	g_slist_free (files_to_load); + +	return loaded_files; +} + + +// FIXME: we should expose API with GFile and just make the uri +// variants backward compat wrappers + +static gint +load_uri_list (GeditWindow         *window, +	       const GSList        *uris, +	       const GeditEncoding *encoding, +	       gint                 line_pos, +	       gboolean             create) +{ +	GSList *files = NULL; +	const GSList *u; +	gint ret; + +	for (u = uris; u != NULL; u = u->next) +	{ +		gchar *uri = u->data; + +		if (gedit_utils_is_valid_uri (uri)) +			files = g_slist_prepend (files, g_file_new_for_uri (uri)); +		else +			g_warning ("invalid uri: %s", uri); +	} +	files = g_slist_reverse (files); + +	ret = load_file_list (window, files, encoding, line_pos, create); + +	g_slist_foreach (files, (GFunc) g_object_unref, NULL); +	g_slist_free (files); + +	return ret; +} + +/** + * gedit_commands_load_uri: + * + * Do nothing if URI does not exist + */ +void +gedit_commands_load_uri (GeditWindow         *window, +			 const gchar         *uri, +			 const GeditEncoding *encoding, +			 gint                 line_pos) +{ +	GSList *uris = NULL; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (uri != NULL); +	g_return_if_fail (gedit_utils_is_valid_uri (uri)); + +	gedit_debug_message (DEBUG_COMMANDS, "Loading URI '%s'", uri); + +	uris = g_slist_prepend (uris, (gchar *)uri); + +	load_uri_list (window, uris, encoding, line_pos, FALSE); + +	g_slist_free (uris); +} + +/** + * gedit_commands_load_uris: + * + * Ignore non-existing URIs  + */ +gint +gedit_commands_load_uris (GeditWindow         *window, +			  const GSList        *uris, +			  const GeditEncoding *encoding, +			  gint                 line_pos) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), 0); +	g_return_val_if_fail ((uris != NULL) && (uris->data != NULL), 0); + +	gedit_debug (DEBUG_COMMANDS); + +	return load_uri_list (window, uris, encoding, line_pos, FALSE); +} + +/* + * This should become public once we convert all api to GFile: + */ +static gint +gedit_commands_load_files (GeditWindow         *window, +			   GSList              *files, +			   const GeditEncoding *encoding, +			   gint                 line_pos) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), 0); +	g_return_val_if_fail ((files != NULL) && (files->data != NULL), 0); + +	gedit_debug (DEBUG_COMMANDS); + +	return load_file_list (window, files, encoding, line_pos, FALSE); +} + +/* + * From the command line we can specify a line position for the + * first doc. Beside specifying a not existing uri creates a + * titled document. + */ +gint +_gedit_cmd_load_files_from_prompt (GeditWindow         *window, +				   GSList              *files, +				   const GeditEncoding *encoding, +				   gint                 line_pos) +{ +	gedit_debug (DEBUG_COMMANDS); + +	return load_file_list (window, files, encoding, line_pos, TRUE); +} + +static void +open_dialog_destroyed (GeditWindow            *window, +		       GeditFileChooserDialog *dialog) +{ +	gedit_debug (DEBUG_COMMANDS); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_OPEN_DIALOG_KEY, +			   NULL); +} + +static void +open_dialog_response_cb (GeditFileChooserDialog *dialog, +                         gint                    response_id, +                         GeditWindow            *window) +{ +	GSList *files; +	const GeditEncoding *encoding; + +	gedit_debug (DEBUG_COMMANDS); + +	if (response_id != GTK_RESPONSE_OK) +	{ +		gtk_widget_destroy (GTK_WIDGET (dialog)); + +		return; +	} + +	files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog)); +	g_return_if_fail (files != NULL); + +	encoding = gedit_file_chooser_dialog_get_encoding (dialog); + +	gtk_widget_destroy (GTK_WIDGET (dialog)); + +	/* Remember the folder we navigated to */ +	 _gedit_window_set_default_location (window, files->data); + +	gedit_commands_load_files (window, +				   files, +				   encoding, +				   0); + +	g_slist_foreach (files, (GFunc) g_object_unref, NULL); +	g_slist_free (files); +} + +void +_gedit_cmd_file_open (GtkAction   *action, +		      GeditWindow *window) +{ +	GtkWidget *open_dialog; +	gpointer data; +	GeditDocument *doc; +	GFile *default_path = NULL; + +	gedit_debug (DEBUG_COMMANDS); + +	data = g_object_get_data (G_OBJECT (window), GEDIT_OPEN_DIALOG_KEY); + +	if (data != NULL) +	{ +		g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (data)); + +		gtk_window_present (GTK_WINDOW (data)); + +		return; +	} + +	/* Translators: "Open Files" is the title of the file chooser window */  +	open_dialog = gedit_file_chooser_dialog_new (_("Open Files"), +						     GTK_WINDOW (window), +						     GTK_FILE_CHOOSER_ACTION_OPEN, +						     NULL, +						     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, +						     GTK_STOCK_OPEN, GTK_RESPONSE_OK, +						     NULL); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_OPEN_DIALOG_KEY, +			   open_dialog); + +	g_object_weak_ref (G_OBJECT (open_dialog), +			   (GWeakNotify) open_dialog_destroyed, +			   window); + +	/* Set the curret folder uri */ +	doc = gedit_window_get_active_document (window); +	if (doc != NULL) +	{ +		GFile *file; + +		file = gedit_document_get_location (doc); + +		if (file != NULL) +		{ +			default_path = g_file_get_parent (file); +			g_object_unref (file); +		} +	} + +	if (default_path == NULL) +		default_path = _gedit_window_get_default_location (window); + +	if (default_path != NULL) +	{ +		gchar *uri; + +		uri = g_file_get_uri (default_path); +		gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (open_dialog), +							 uri); + +		g_free (uri); +		g_object_unref (default_path); +	} + +	g_signal_connect (open_dialog, +			  "response", +			  G_CALLBACK (open_dialog_response_cb), +			  window); + +	gtk_widget_show (open_dialog); +} + +/* File saving */ +static void file_save_as (GeditTab *tab, GeditWindow *window); + +static gboolean +is_read_only (GFile *location) +{ +	gboolean ret = TRUE; /* default to read only */ +	GFileInfo *info; + +	gedit_debug (DEBUG_COMMANDS); + +	info = g_file_query_info (location, +				  G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, +				  G_FILE_QUERY_INFO_NONE, +				  NULL, +				  NULL); + +	if (info != NULL) +	{ +		if (g_file_info_has_attribute (info, +					       G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) +		{ +			ret = !g_file_info_get_attribute_boolean (info, +								  G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); +		} + +		g_object_unref (info); +	} + +	return ret; +} + +/* FIXME: modify this dialog to be similar to the one provided by gtk+ for + * already existing files - Paolo (Oct. 11, 2005) */ +static gboolean +replace_read_only_file (GtkWindow *parent, GFile *file) +{ +	GtkWidget *dialog; +	gint ret; +	gchar *parse_name; +	gchar *name_for_display; + +	gedit_debug (DEBUG_COMMANDS); + +	parse_name = g_file_get_parse_name (file); + +	/* Truncate the name so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the name doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	name_for_display = gedit_utils_str_middle_truncate (parse_name, 50); +	g_free (parse_name); + +	dialog = gtk_message_dialog_new (parent, +					 GTK_DIALOG_DESTROY_WITH_PARENT, +					 GTK_MESSAGE_QUESTION, +					 GTK_BUTTONS_NONE, +					 _("The file \"%s\" is read-only."), +				         name_for_display); +	g_free (name_for_display); + +	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), +						  _("Do you want to try to replace it " +						    "with the one you are saving?")); + +	gtk_dialog_add_button (GTK_DIALOG (dialog), +			       GTK_STOCK_CANCEL, +			       GTK_RESPONSE_CANCEL); + +	gedit_dialog_add_button (GTK_DIALOG (dialog), +				 _("_Replace"), +			  	 GTK_STOCK_SAVE_AS, +			  	 GTK_RESPONSE_YES); + +	gtk_dialog_set_default_response	(GTK_DIALOG (dialog), +					 GTK_RESPONSE_CANCEL); + +	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + +	ret = gtk_dialog_run (GTK_DIALOG (dialog)); + +	gtk_widget_destroy (dialog); + +	return (ret == GTK_RESPONSE_YES); +} + +static void +save_dialog_response_cb (GeditFileChooserDialog *dialog, +                         gint                    response_id, +                         GeditWindow            *window) +{ +	GFile *file; +	const GeditEncoding *encoding; +	GeditTab *tab; +	gpointer data; +	GSList *tabs_to_save_as; +	GeditDocumentNewlineType newline_type; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = GEDIT_TAB (g_object_get_data (G_OBJECT (dialog), +					    GEDIT_TAB_TO_SAVE_AS)); + +	if (response_id != GTK_RESPONSE_OK) +	{ +		gtk_widget_destroy (GTK_WIDGET (dialog)); + +		goto save_next_tab; +	} + +	file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); +	g_return_if_fail (file != NULL); + +	encoding = gedit_file_chooser_dialog_get_encoding (dialog); +	newline_type = gedit_file_chooser_dialog_get_newline_type (dialog); + +	gtk_widget_destroy (GTK_WIDGET (dialog)); + +	if (tab != NULL) +	{ +		GeditDocument *doc; +		gchar *parse_name; +		gchar *uri; + +		doc = gedit_tab_get_document (tab); +		g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +		parse_name = g_file_get_parse_name (file); + +		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +					        window->priv->generic_message_cid, +					       _("Saving file '%s'\342\200\246"), +					       parse_name); + +		g_free (parse_name); + +		/* let's remember the dir we navigated too, +		 * even if the saving fails... */ +		 _gedit_window_set_default_location (window, file); + +		// FIXME: pass the GFile to tab when api is there +		uri = g_file_get_uri (file); +		_gedit_tab_save_as (tab, uri, encoding, newline_type); +		g_free (uri); +	} + +	g_object_unref (file); + +save_next_tab: + +	data = g_object_get_data (G_OBJECT (window), +				  GEDIT_LIST_OF_TABS_TO_SAVE_AS); +	if (data == NULL) +		return; + +	/* Save As the next tab of the list (we are Saving All files) */ +	tabs_to_save_as = (GSList *)data; +	g_return_if_fail (tab == GEDIT_TAB (tabs_to_save_as->data)); + +	/* Remove the first item of the list */ +	tabs_to_save_as = g_slist_delete_link (tabs_to_save_as, +					       tabs_to_save_as); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_LIST_OF_TABS_TO_SAVE_AS, +			   tabs_to_save_as); + +	if (tabs_to_save_as != NULL) +	{ +		tab = GEDIT_TAB (tabs_to_save_as->data); + +		if (GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (tab), +							    GEDIT_IS_CLOSING_TAB)) == TRUE) +		{ +			g_object_set_data (G_OBJECT (tab), +					   GEDIT_IS_CLOSING_TAB, +					   NULL); + +			/* Trace tab state changes */ +			g_signal_connect (tab, +					  "notify::state", +					  G_CALLBACK (tab_state_changed_while_saving), +					  window); +		} + +		gedit_window_set_active_tab (window, tab); +		file_save_as (tab, window); +	} +} + +static GtkFileChooserConfirmation +confirm_overwrite_callback (GtkFileChooser *dialog, +			    gpointer        data) +{ +	gchar *uri; +	GFile *file; +	GtkFileChooserConfirmation res; + +	gedit_debug (DEBUG_COMMANDS); + +	uri = gtk_file_chooser_get_uri (dialog); +	file = g_file_new_for_uri (uri); +	g_free (uri); + +	if (is_read_only (file)) +	{ +		if (replace_read_only_file (GTK_WINDOW (dialog), file)) +			res = GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME; +		else +			res = GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN; +	} +	else +	{ +		/* fall back to the default confirmation dialog */ +		res = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM; +	} + +	g_object_unref (file); + +	return res; +} + +static void +file_save_as (GeditTab    *tab, +	      GeditWindow *window) +{ +	GtkWidget *save_dialog; +	GtkWindowGroup *wg; +	GeditDocument *doc; +	GFile *file; +	gboolean uri_set = FALSE; +	const GeditEncoding *encoding; +	GeditDocumentNewlineType newline_type; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	gedit_debug (DEBUG_COMMANDS); + +	save_dialog = gedit_file_chooser_dialog_new (_("Save As\342\200\246"), +						     GTK_WINDOW (window), +						     GTK_FILE_CHOOSER_ACTION_SAVE, +						     NULL, +						     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, +						     GTK_STOCK_SAVE, GTK_RESPONSE_OK, +						     NULL); + +	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (save_dialog), +							TRUE); +	g_signal_connect (save_dialog, +			  "confirm-overwrite", +			  G_CALLBACK (confirm_overwrite_callback), +			  NULL); + +	wg = gedit_window_get_group (window); + +	gtk_window_group_add_window (wg, +				     GTK_WINDOW (save_dialog)); + +	/* Save As dialog is modal to its main window */ +	gtk_window_set_modal (GTK_WINDOW (save_dialog), TRUE); + +	/* Set the suggested file name */ +	doc = gedit_tab_get_document (tab); +	file = gedit_document_get_location (doc); + +	if (file != NULL) +	{ +		uri_set = gtk_file_chooser_set_file (GTK_FILE_CHOOSER (save_dialog), +						     file, +						     NULL); + +		g_object_unref (file); +	} + + +	if (!uri_set) +	{ +		GFile *default_path; +		gchar *docname; + +		default_path = _gedit_window_get_default_location (window); +		docname = gedit_document_get_short_name_for_display (doc); + +		if (default_path != NULL) +		{ +			gchar *uri; + +			uri = g_file_get_uri (default_path); +			gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), +								 uri); + +			g_free (uri); +			g_object_unref (default_path); +		} + +		gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), +						   docname); + +		g_free (docname); +	} + +	/* Set suggested encoding */ +	encoding = gedit_document_get_encoding (doc); +	g_return_if_fail (encoding != NULL); + +	newline_type = gedit_document_get_newline_type (doc); + +	gedit_file_chooser_dialog_set_encoding (GEDIT_FILE_CHOOSER_DIALOG (save_dialog), +						encoding); + +	gedit_file_chooser_dialog_set_newline_type (GEDIT_FILE_CHOOSER_DIALOG (save_dialog), +	                                            newline_type); + +	g_object_set_data (G_OBJECT (save_dialog), +			   GEDIT_TAB_TO_SAVE_AS, +			   tab); + +	g_signal_connect (save_dialog, +			  "response", +			  G_CALLBACK (save_dialog_response_cb), +			  window); + +	gtk_widget_show (save_dialog); +} + +static void +file_save (GeditTab    *tab, +	   GeditWindow *window) +{ +	GeditDocument *doc; +	gchar *uri_for_display; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	if (gedit_document_is_untitled (doc) ||  +	    gedit_document_get_readonly (doc)) +	{ +		gedit_debug_message (DEBUG_COMMANDS, "Untitled or Readonly"); + +		file_save_as (tab, window); +		 +		return; +	} + +	uri_for_display = gedit_document_get_uri_for_display (doc); +	gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +				        window->priv->generic_message_cid, +				       _("Saving file '%s'\342\200\246"), +				       uri_for_display); + +	g_free (uri_for_display); + +	_gedit_tab_save (tab); +} + +void +_gedit_cmd_file_save (GtkAction   *action, +		     GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	file_save (tab, window); +} + +void +_gedit_cmd_file_save_as (GtkAction   *action, +			GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	file_save_as (tab, window); +} + +static gboolean +document_needs_saving (GeditDocument *doc) +{ +	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) +		return TRUE; + +	/* we check if it was deleted only for local files +	 * since for remote files it may hang */ +	if (gedit_document_is_local (doc) && gedit_document_get_deleted (doc)) +		return TRUE; + +	return FALSE; +} + +/* + * The docs in the list must belong to the same GeditWindow. + */ +void +_gedit_cmd_file_save_documents_list (GeditWindow *window, +				     GList       *docs) +{ +	GList *l; +	GSList *tabs_to_save_as = NULL; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (!(gedit_window_get_state (window) &  +			    (GEDIT_WINDOW_STATE_PRINTING | +			     GEDIT_WINDOW_STATE_SAVING_SESSION))); + +	l = docs; +	while (l != NULL) +	{ +		GeditDocument *doc; +		GeditTab *t; +		GeditTabState state; + +		g_return_if_fail (GEDIT_IS_DOCUMENT (l->data)); +  +		doc = GEDIT_DOCUMENT (l->data); +		t = gedit_tab_get_from_document (doc); +		state = gedit_tab_get_state (t); + +		g_return_if_fail (state != GEDIT_TAB_STATE_PRINTING); +		g_return_if_fail (state != GEDIT_TAB_STATE_PRINT_PREVIEWING); +		g_return_if_fail (state != GEDIT_TAB_STATE_CLOSING); + +		if ((state == GEDIT_TAB_STATE_NORMAL) || +		    (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) || +		    (state == GEDIT_TAB_STATE_GENERIC_NOT_EDITABLE)) +		{ +			/* FIXME: manage the case of local readonly files owned by the +			   user is running gedit - Paolo (Dec. 8, 2005) */ +			if (gedit_document_is_untitled (doc) ||  +			    gedit_document_get_readonly (doc)) +			{ +				if (document_needs_saving (doc)) +			     	{ +				     	tabs_to_save_as = g_slist_prepend (tabs_to_save_as, +									   t); +			     	} +			} +			else +			{ +				file_save (t, window);			 +			} +		} +		else +		{ +			/* If the state is: +			   - GEDIT_TAB_STATE_LOADING: we do not save since we are sure the file is unmodified +			   - GEDIT_TAB_STATE_REVERTING: we do not save since the user wants +			     to return back to the version of the file she previously saved +			   - GEDIT_TAB_STATE_SAVING: well, we are already saving (no need to save again) +			   - GEDIT_TAB_STATE_PRINTING, GEDIT_TAB_STATE_PRINT_PREVIEWING: there is not a +			     real reason for not saving in this case, we do not save to avoid to run +			     two operations using the message area at the same time (may be we can remove +			     this limitation in the future). Note that SaveAll, ClosAll +			     and Quit are unsensitive if the window state is PRINTING. +			   - GEDIT_TAB_STATE_GENERIC_ERROR: we do not save since the document contains +			     errors (I don't think this is a very frequent case, we should probably remove +			     this state) +			   - GEDIT_TAB_STATE_LOADING_ERROR: there is nothing to save +			   - GEDIT_TAB_STATE_REVERTING_ERROR: there is nothing to save and saving the current +			     document will overwrite the copy of the file the user wants to go back to +			   - GEDIT_TAB_STATE_SAVING_ERROR: we do not save since we just failed to save, so there is +			     no reason to automatically retry... we wait for user intervention +			   - GEDIT_TAB_STATE_CLOSING: this state is invalid in this case +			*/ + +			gchar *uri_for_display; + +			uri_for_display = gedit_document_get_uri_for_display (doc); +			gedit_debug_message (DEBUG_COMMANDS, +					     "File '%s' not saved. State: %d", +					     uri_for_display, +					     state); +			g_free (uri_for_display); +		} + +		l = g_list_next (l); +	} + +	if (tabs_to_save_as != NULL) +	{ +		GeditTab *tab; + +		tabs_to_save_as = g_slist_reverse (tabs_to_save_as ); + +		g_return_if_fail (g_object_get_data (G_OBJECT (window), +						     GEDIT_LIST_OF_TABS_TO_SAVE_AS) == NULL); + +		g_object_set_data (G_OBJECT (window), +				   GEDIT_LIST_OF_TABS_TO_SAVE_AS, +				   tabs_to_save_as); + +		tab = GEDIT_TAB (tabs_to_save_as->data); + +		gedit_window_set_active_tab (window, tab); +		file_save_as (tab, window); +	} +} + +void +gedit_commands_save_all_documents (GeditWindow *window) +{ +	GList *docs; +	 +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	gedit_debug (DEBUG_COMMANDS); + +	docs = gedit_window_get_documents (window); + +	_gedit_cmd_file_save_documents_list (window, docs); + +	g_list_free (docs); +} + +void +_gedit_cmd_file_save_all (GtkAction   *action, +			 GeditWindow *window) +{ +	gedit_commands_save_all_documents (window); +} + +void +gedit_commands_save_document (GeditWindow   *window, +                              GeditDocument *document) +{ +	GeditTab *tab; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (GEDIT_IS_DOCUMENT (document)); +	 +	gedit_debug (DEBUG_COMMANDS); +	 +	tab = gedit_tab_get_from_document (document); +	file_save (tab, window); +} + +/* File revert */ +static void +do_revert (GeditWindow *window, +	   GeditTab    *tab) +{ +	GeditDocument *doc; +	gchar *docname; + +	gedit_debug (DEBUG_COMMANDS); + +	doc = gedit_tab_get_document (tab); +	docname = gedit_document_get_short_name_for_display (doc); + +	gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +				        window->priv->generic_message_cid, +				       _("Reverting the document '%s'\342\200\246"), +				       docname); + +	g_free (docname); + +	_gedit_tab_revert (tab); +} + +static void +revert_dialog_response_cb (GtkDialog   *dialog, +			   gint         response_id, +			   GeditWindow *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	/* FIXME: we are relying on the fact that the dialog is +	   modal so the active tab can't be changed... +	   not very nice - Paolo (Oct 11, 2005) */ +	tab = gedit_window_get_active_tab (window); +	if (tab == NULL) +		return; + +	gtk_widget_destroy (GTK_WIDGET (dialog)); + +	if (response_id == GTK_RESPONSE_OK) +	{ +		do_revert (window, tab); +	} +} + +static GtkWidget * +revert_dialog (GeditWindow   *window, +	       GeditDocument *doc) +{ +	GtkWidget *dialog; +	gchar *docname; +	gchar *primary_msg; +	gchar *secondary_msg; +	glong seconds; + +	gedit_debug (DEBUG_COMMANDS); + +	docname = gedit_document_get_short_name_for_display (doc); +	primary_msg = g_strdup_printf (_("Revert unsaved changes to document '%s'?"), +	                               docname); +	g_free (docname); + +	seconds = MAX (1, _gedit_document_get_seconds_since_last_save_or_load (doc)); + +	if (seconds < 55) +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("Changes made to the document in the last %ld second " +					    	  "will be permanently lost.", +						  "Changes made to the document in the last %ld seconds " +					    	  "will be permanently lost.", +						  seconds), +					seconds); +	} +	else if (seconds < 75) /* 55 <= seconds < 75 */ +	{ +		secondary_msg = g_strdup (_("Changes made to the document in the last minute " +					    "will be permanently lost.")); +	} +	else if (seconds < 110) /* 75 <= seconds < 110 */ +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("Changes made to the document in the last minute and " +						  "%ld second will be permanently lost.", +						  "Changes made to the document in the last minute and " +						  "%ld seconds will be permanently lost.", +						  seconds - 60 ), +					seconds - 60); +	} +	else if (seconds < 3600) +	{ +		secondary_msg = g_strdup_printf ( +					ngettext ("Changes made to the document in the last %ld minute " +					    	  "will be permanently lost.", +						  "Changes made to the document in the last %ld minutes " +					    	  "will be permanently lost.", +						  seconds / 60), +					seconds / 60); +	} +	else if (seconds < 7200) +	{ +		gint minutes; +		seconds -= 3600; + +		minutes = seconds / 60; +		if (minutes < 5) +		{ +			secondary_msg = g_strdup (_("Changes made to the document in the last hour " +						    "will be permanently lost.")); +		} +		else +		{ +			secondary_msg = g_strdup_printf ( +					ngettext ("Changes made to the document in the last hour and " +						  "%d minute will be permanently lost.", +						  "Changes made to the document in the last hour and " +						  "%d minutes will be permanently lost.", +						  minutes), +					minutes); +		} +	} +	else +	{ +		gint hours; + +		hours = seconds / 3600; + +		secondary_msg = g_strdup_printf ( +					ngettext ("Changes made to the document in the last %d hour " +					    	  "will be permanently lost.", +						  "Changes made to the document in the last %d hours " +					    	  "will be permanently lost.", +						  hours), +					hours); +	} + +	dialog = gtk_message_dialog_new (GTK_WINDOW (window), +					 GTK_DIALOG_DESTROY_WITH_PARENT, +					 GTK_MESSAGE_QUESTION, +					 GTK_BUTTONS_NONE, +					 "%s", primary_msg); + +	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), +						  "%s", secondary_msg); +	g_free (primary_msg); +	g_free (secondary_msg); + +	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + +	gtk_dialog_add_button (GTK_DIALOG (dialog), +			       GTK_STOCK_CANCEL, +			       GTK_RESPONSE_CANCEL); + +	gedit_dialog_add_button (GTK_DIALOG (dialog), +				 _("_Revert"), +				 GTK_STOCK_REVERT_TO_SAVED, +				 GTK_RESPONSE_OK); + +	gtk_dialog_set_default_response	(GTK_DIALOG (dialog), +					 GTK_RESPONSE_CANCEL); + +	return dialog; +} + +void +_gedit_cmd_file_revert (GtkAction   *action, +		       GeditWindow *window) +{ +	GeditTab       *tab; +	GeditDocument  *doc; +	GtkWidget      *dialog; +	GtkWindowGroup *wg; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_window_get_active_tab (window); +	g_return_if_fail (tab != NULL); + +	/* If we are already displaying a notification +	 * reverting will drop local modifications, do +	 * not bug the user further */ +	if (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) +	{ +		do_revert (window, tab); +		return; +	} + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (doc != NULL); +	g_return_if_fail (!gedit_document_is_untitled (doc)); + +	dialog = revert_dialog (window, doc); + +	wg = gedit_window_get_group (window); + +	gtk_window_group_add_window (wg, GTK_WINDOW (dialog)); + +	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + +	g_signal_connect (dialog, +			  "response", +			  G_CALLBACK (revert_dialog_response_cb), +			  window); + +	gtk_widget_show (dialog); +} + +/* Close tab */ +static gboolean +really_close_tab (GeditTab *tab) +{ +	GtkWidget *toplevel; +	GeditWindow *window; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_val_if_fail (gedit_tab_get_state (tab) == GEDIT_TAB_STATE_CLOSING, +			      FALSE); + +	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tab)); +	g_return_val_if_fail (GEDIT_IS_WINDOW (toplevel), FALSE); + +	window = GEDIT_WINDOW (toplevel); + +	gedit_window_close_tab (window, tab); + +	if (gedit_window_get_active_tab (window) == NULL) +	{ +		gboolean is_quitting; + +		is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window), +								      GEDIT_IS_QUITTING)); + +		if (is_quitting) +			gtk_widget_destroy (GTK_WIDGET (window)); +	} + +	return FALSE; +} + +static void +tab_state_changed_while_saving (GeditTab    *tab, +				GParamSpec  *pspec, +				GeditWindow *window) +{ +	GeditTabState ts; + +	ts = gedit_tab_get_state (tab); + +	gedit_debug_message (DEBUG_COMMANDS, "State while saving: %d\n", ts); + +	/* When the state become NORMAL, it means the saving operation is +	   finished */ +	if (ts == GEDIT_TAB_STATE_NORMAL) +	{ +		GeditDocument *doc; + +		g_signal_handlers_disconnect_by_func (tab, +						      G_CALLBACK (tab_state_changed_while_saving), +					      	      window); + +		doc = gedit_tab_get_document (tab); +		g_return_if_fail (doc != NULL); + +		/* If the saving operation failed or was interrupted, then the +		   document is still "modified" -> do not close the tab */ +		if (document_needs_saving (doc)) +			return; + +		/* Close the document only if it has been succesfully saved. +		   Tab state is set to CLOSING (it is a state without exiting +		   transitions) and the tab is closed in a idle handler */ +		_gedit_tab_mark_for_closing (tab); + +		g_idle_add_full (G_PRIORITY_HIGH_IDLE, +				 (GSourceFunc)really_close_tab, +				 tab, +				 NULL); +	} +} + +static void +save_and_close (GeditTab    *tab, +		GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	/* Trace tab state changes */ +	g_signal_connect (tab, +			  "notify::state", +			  G_CALLBACK (tab_state_changed_while_saving), +			  window); + +	file_save (tab, window); +} + +static void +save_as_and_close (GeditTab    *tab, +		   GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	g_object_set_data (G_OBJECT (tab), +			   GEDIT_IS_CLOSING_TAB, +			   NULL); + +	/* Trace tab state changes */ +	g_signal_connect (tab, +			  "notify::state", +			  G_CALLBACK (tab_state_changed_while_saving), +			  window); + +	gedit_window_set_active_tab (window, tab); +	file_save_as (tab, window); +} + +static void +save_and_close_all_documents (const GList  *docs, +			      GeditWindow  *window) +{ +	GList  *tabs; +	GList  *l; +	GSList *sl; +	GSList *tabs_to_save_as; +	GSList *tabs_to_save_and_close; +	GList  *tabs_to_close; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (!(gedit_window_get_state (window) & GEDIT_WINDOW_STATE_PRINTING)); + +	tabs = gtk_container_get_children ( +			GTK_CONTAINER (_gedit_window_get_notebook (window))); + +	tabs_to_save_as = NULL; +	tabs_to_save_and_close = NULL; +	tabs_to_close = NULL; + +	l = tabs; +	while (l != NULL) +	{ +		GeditTab *t; +		GeditTabState state; +		GeditDocument *doc; + +		t = GEDIT_TAB (l->data); + +		state = gedit_tab_get_state (t); +		doc = gedit_tab_get_document (t); + +		/* If the state is: ([*] invalid states) +		   - GEDIT_TAB_STATE_NORMAL: close (and if needed save) +		   - GEDIT_TAB_STATE_LOADING: close, we are sure the file is unmodified +		   - GEDIT_TAB_STATE_REVERTING: since the user wants +		     to return back to the version of the file she previously saved, we can close +		     without saving (CHECK: are we sure this is the right behavior, suppose the case  +		     the original file has been deleted) +		   - [*] GEDIT_TAB_STATE_SAVING: invalid, ClosAll +		     and Quit are unsensitive if the window state is SAVING. +		   - [*] GEDIT_TAB_STATE_PRINTING, GEDIT_TAB_STATE_PRINT_PREVIEWING: there is not a +		     real reason for not closing in this case, we do not save to avoid to run +		     two operations using the message area at the same time (may be we can remove +		     this limitation in the future). Note that ClosAll +		     and Quit are unsensitive if the window state is PRINTING. +		   - GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW: close (and if needed save) +		   - GEDIT_TAB_STATE_LOADING_ERROR: close without saving (if the state is LOADING_ERROR then the +		     document is not modified) +		   - GEDIT_TAB_STATE_REVERTING_ERROR: we do not close since the document contains errors +		   - GEDIT_TAB_STATE_SAVING_ERROR: we do not close since the document contains errors +		   - GEDIT_TAB_STATE_GENERIC_ERROR: we do not close since the document contains +		     errors (CHECK: we should problably remove this state) +		   - [*] GEDIT_TAB_STATE_CLOSING: this state is invalid in this case +		*/ + +		g_return_if_fail (state != GEDIT_TAB_STATE_PRINTING); +		g_return_if_fail (state != GEDIT_TAB_STATE_PRINT_PREVIEWING); +		g_return_if_fail (state != GEDIT_TAB_STATE_CLOSING); +		g_return_if_fail (state != GEDIT_TAB_STATE_SAVING); + +		if ((state != GEDIT_TAB_STATE_SAVING_ERROR) && +		    (state != GEDIT_TAB_STATE_GENERIC_ERROR) && +		    (state != GEDIT_TAB_STATE_REVERTING_ERROR)) +		{ +			if ((g_list_index ((GList *)docs, doc) >= 0) && +			    (state != GEDIT_TAB_STATE_LOADING) && +			    (state != GEDIT_TAB_STATE_LOADING_ERROR) &&			     +			    (state != GEDIT_TAB_STATE_REVERTING)) /* CHECK: is this the right behavior with REVERTING ?*/ +			{			 +				/* The document must be saved before closing */ +				g_return_if_fail (document_needs_saving (doc)); +				 +				/* FIXME: manage the case of local readonly files owned by the +				   user is running gedit - Paolo (Dec. 8, 2005) */ +				if (gedit_document_is_untitled (doc) || +				    gedit_document_get_readonly (doc)) +				{ +					g_object_set_data (G_OBJECT (t), +							   GEDIT_IS_CLOSING_TAB, +							   GBOOLEAN_TO_POINTER (TRUE)); + +					tabs_to_save_as = g_slist_prepend (tabs_to_save_as, +									   t); +				} +				else +				{ +					tabs_to_save_and_close = g_slist_prepend (tabs_to_save_and_close, +										  t); +				} +			} +			else +			{ +				/* The document must be closed without saving */ +				tabs_to_close = g_list_prepend (tabs_to_close, +								t); +			} +		} + +		l = g_list_next (l); +	} + +	g_list_free (tabs); + +	/* Close all tabs to close (in a sync way) */ +	gedit_window_close_tabs (window, tabs_to_close); +	g_list_free (tabs_to_close); + +	/* Save and close all the files in tabs_to_save_and_close */ +	sl = tabs_to_save_and_close; +	while (sl != NULL) +	{ +		save_and_close (GEDIT_TAB (sl->data), +				window); +		sl = g_slist_next (sl); +	} +	g_slist_free (tabs_to_save_and_close); + +	/* Save As and close all the files in tabs_to_save_as  */ +	if (tabs_to_save_as != NULL) +	{ +		GeditTab *tab; + +		tabs_to_save_as = g_slist_reverse (tabs_to_save_as ); + +		g_return_if_fail (g_object_get_data (G_OBJECT (window), +						     GEDIT_LIST_OF_TABS_TO_SAVE_AS) == NULL); + +		g_object_set_data (G_OBJECT (window), +				   GEDIT_LIST_OF_TABS_TO_SAVE_AS, +				   tabs_to_save_as); + +		tab = GEDIT_TAB (tabs_to_save_as->data); + +		save_as_and_close (tab, window); +	} +} + +static void +save_and_close_document (const GList  *docs, +			 GeditWindow  *window) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (docs->next == NULL); + +	tab = gedit_tab_get_from_document (GEDIT_DOCUMENT (docs->data)); +	g_return_if_fail (tab != NULL); + +	save_and_close (tab, window); +} + +static void +close_all_tabs (GeditWindow *window) +{ +	gboolean is_quitting; + +	gedit_debug (DEBUG_COMMANDS); + +	/* There is no document to save -> close all tabs */ +	gedit_window_close_all_tabs (window); + +	is_quitting = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window), +							      GEDIT_IS_QUITTING)); + +	if (is_quitting) +		gtk_widget_destroy (GTK_WIDGET (window)); + +	return; +} + +static void +close_document (GeditWindow   *window, +		GeditDocument *doc) +{ +	GeditTab *tab; + +	gedit_debug (DEBUG_COMMANDS); + +	tab = gedit_tab_get_from_document (doc); +	g_return_if_fail (tab != NULL); + +	gedit_window_close_tab (window, tab); +} + +static void +close_confirmation_dialog_response_handler (GeditCloseConfirmationDialog *dlg, +					    gint                          response_id, +					    GeditWindow                  *window) +{ +	GList *selected_documents; +	gboolean is_closing_all; + +	gedit_debug (DEBUG_COMMANDS); + +	is_closing_all = GPOINTER_TO_BOOLEAN (g_object_get_data (G_OBJECT (window), +					    			 GEDIT_IS_CLOSING_ALL)); + +	gtk_widget_hide (GTK_WIDGET (dlg)); + +	switch (response_id) +	{ +		case GTK_RESPONSE_YES: /* Save and Close */ +			selected_documents = gedit_close_confirmation_dialog_get_selected_documents (dlg); +			if (selected_documents == NULL) +			{ +				if (is_closing_all) +				{ +					/* There is no document to save -> close all tabs */ +					/* We call gtk_widget_destroy before close_all_tabs +					 * because close_all_tabs could destroy the gedit window */ +					gtk_widget_destroy (GTK_WIDGET (dlg)); + +					close_all_tabs (window); + +					return; +				} +				else +					g_return_if_reached (); +			} +			else +			{ +				if (is_closing_all) +				{ +					save_and_close_all_documents (selected_documents, +								      window); +				} +				else +				{ +					save_and_close_document (selected_documents, +								 window); +				} +			} + +			g_list_free (selected_documents); + +			break; + +		case GTK_RESPONSE_NO: /* Close without Saving */ +			if (is_closing_all) +			{ +				/* We call gtk_widget_destroy before close_all_tabs +				 * because close_all_tabs could destroy the gedit window */ +				gtk_widget_destroy (GTK_WIDGET (dlg)); + +				close_all_tabs (window); + +				return; +			} +			else +			{ +				const GList *unsaved_documents; + +				unsaved_documents = gedit_close_confirmation_dialog_get_unsaved_documents (dlg); +				g_return_if_fail (unsaved_documents->next == NULL); + +				close_document (window, +						GEDIT_DOCUMENT (unsaved_documents->data)); +			} + +			break; +		default: /* Do not close */ + +			/* Reset is_quitting flag */ +			g_object_set_data (G_OBJECT (window), +					   GEDIT_IS_QUITTING, +					   GBOOLEAN_TO_POINTER (FALSE)); + +#ifdef OS_OSX +			g_object_set_data (G_OBJECT (window), +			                   GEDIT_IS_QUITTING_ALL, +			                   GINT_TO_POINTER (FALSE)); +#endif +			break; +	} + +	gtk_widget_destroy (GTK_WIDGET (dlg)); +} + +/* Returns TRUE if the tab can be immediately closed */ +static gboolean +tab_can_close (GeditTab  *tab, +	       GtkWindow *window) +{ +	GeditDocument *doc; + +	gedit_debug (DEBUG_COMMANDS); + +	doc = gedit_tab_get_document (tab); + +	if (!_gedit_tab_can_close (tab)) +	{ +		GtkWidget     *dlg; + +		dlg = gedit_close_confirmation_dialog_new_single ( +						window, +						doc, +						FALSE); + +		g_signal_connect (dlg, +				  "response", +				  G_CALLBACK (close_confirmation_dialog_response_handler), +				  window); + +		gtk_widget_show (dlg); + +		return FALSE; +	} + +	return TRUE; +} + +/* CHECK: we probably need this one public for plugins... + * maybe even a _list variant. Or maybe it's better make + * gedit_window_close_tab always run the confirm dialog? + * we should not allow closing a tab without resetting the + * GEDIT_IS_CLOSING_ALL flag! + */ +void +_gedit_cmd_file_close_tab (GeditTab    *tab, +			   GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (GTK_WIDGET (window) == gtk_widget_get_toplevel (GTK_WIDGET (tab))); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_IS_CLOSING_ALL, +			   GBOOLEAN_TO_POINTER (FALSE)); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_IS_QUITTING, +			   GBOOLEAN_TO_POINTER (FALSE)); + +	g_object_set_data (G_OBJECT (window),  +	                   GEDIT_IS_QUITTING_ALL,  +	                   GINT_TO_POINTER (FALSE)); + + +	if (tab_can_close (tab, GTK_WINDOW (window))) +		gedit_window_close_tab (window, tab); +} + +void +_gedit_cmd_file_close (GtkAction   *action, +		      GeditWindow *window) +{ +	GeditTab *active_tab; + +	gedit_debug (DEBUG_COMMANDS); + +	active_tab = gedit_window_get_active_tab (window); + +	if (active_tab == NULL) +	{ +#ifdef OS_OSX +		/* Close the window on OS X */ +		gtk_widget_destroy (GTK_WIDGET (window)); +#endif +		return; +	} + +	_gedit_cmd_file_close_tab (active_tab, window); +} + +/* Close all tabs */ +static void +file_close_all (GeditWindow *window, +		gboolean     is_quitting) +{ +	GList     *unsaved_docs; +	GtkWidget *dlg; + +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (!(gedit_window_get_state (window) & +	                    (GEDIT_WINDOW_STATE_SAVING | +	                     GEDIT_WINDOW_STATE_PRINTING | +	                     GEDIT_WINDOW_STATE_SAVING_SESSION))); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_IS_CLOSING_ALL, +			   GBOOLEAN_TO_POINTER (TRUE)); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_IS_QUITTING, +			   GBOOLEAN_TO_POINTER (is_quitting)); +			    +	unsaved_docs = gedit_window_get_unsaved_documents (window); + +	if (unsaved_docs == NULL) +	{ +		/* There is no document to save -> close all tabs */ +		gedit_window_close_all_tabs (window); + +		if (is_quitting) +			gtk_widget_destroy (GTK_WIDGET (window)); + +		return; +	} + +	if (unsaved_docs->next == NULL) +	{ +		/* There is only one unsaved document */ +		GeditTab      *tab; +		GeditDocument *doc; + +		doc = GEDIT_DOCUMENT (unsaved_docs->data); + +		tab = gedit_tab_get_from_document (doc); +		g_return_if_fail (tab != NULL); + +		gedit_window_set_active_tab (window, tab); + +		dlg = gedit_close_confirmation_dialog_new_single ( +						GTK_WINDOW (window), +						doc, +						FALSE); +	} +	else +	{ +		dlg = gedit_close_confirmation_dialog_new (GTK_WINDOW (window), +							   unsaved_docs, +							   FALSE); +	} + +	g_list_free (unsaved_docs); + +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (close_confirmation_dialog_response_handler), +			  window); + +	gtk_widget_show (dlg); +} + +void +_gedit_cmd_file_close_all (GtkAction   *action, +			  GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	g_return_if_fail (!(gedit_window_get_state (window) & +	                    (GEDIT_WINDOW_STATE_SAVING | +	                    GEDIT_WINDOW_STATE_PRINTING | +	                    GEDIT_WINDOW_STATE_SAVING_SESSION))); + +	file_close_all (window, FALSE); +} + +/* Quit */ +#ifdef OS_OSX +static void +quit_all () +{ +	GList *windows; +	GList *item; +	GeditApp *app; + +	app = gedit_app_get_default (); +	windows = g_list_copy ((GList *)gedit_app_get_windows (app)); + +	for (item = windows; item; item = g_list_next (item)) +	{ +		GeditWindow *window = GEDIT_WINDOW (item->data); +	 +		g_object_set_data (G_OBJECT (window), +		                   GEDIT_IS_QUITTING_ALL, +		                   GINT_TO_POINTER (TRUE)); + +		if (!(gedit_window_get_state (window) & +		                    (GEDIT_WINDOW_STATE_SAVING | +		                     GEDIT_WINDOW_STATE_PRINTING | +		                     GEDIT_WINDOW_STATE_SAVING_SESSION))) +		{ +			file_close_all (window, TRUE); +		} +	} + +	g_list_free (windows); +} +#endif + +void +_gedit_cmd_file_quit (GtkAction   *action, +		     GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +#ifdef OS_OSX +	if (action != NULL) +	{ +		quit_all (); +		return; +	} +#endif + +	g_return_if_fail (!(gedit_window_get_state (window) & +	                    (GEDIT_WINDOW_STATE_SAVING | +	                     GEDIT_WINDOW_STATE_PRINTING | +	                     GEDIT_WINDOW_STATE_SAVING_SESSION))); + +	file_close_all (window, TRUE); +} diff --git a/gedit/gedit-commands-help.c b/gedit/gedit-commands-help.c new file mode 100755 index 00000000..3b71106e --- /dev/null +++ b/gedit/gedit-commands-help.c @@ -0,0 +1,115 @@ +/* + * gedit-help-commands.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-help.h" +#include "gedit-dirs.h" + +void +_gedit_cmd_help_contents (GtkAction   *action, +			  GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	gedit_help_display (GTK_WINDOW (window), NULL, NULL); +} + +void +_gedit_cmd_help_about (GtkAction   *action, +		       GeditWindow *window) +{ +	static const gchar * const authors[] = { +		"Paolo Maggi <[email protected]>", +		"Paolo Borelli <[email protected]>", +		"Steve Fr\303\251cinaux  <[email protected]>", +		"Jesse van den Kieboom  <[email protected]>", +		"Ignacio Casal Quinteiro <[email protected]>", +		"James Willcox <[email protected]>", +		"Chema Celorio", +		"Federico Mena Quintero <[email protected]>", +		NULL +	}; + +	static const gchar * const documenters[] = { +		"Sun MATE Documentation Team <[email protected]>", +		"Eric Baudais <[email protected]>", +		NULL +	}; + +	static const gchar copyright[] = \ +		"Copyright \xc2\xa9 1998-2000 Evan Lawrence, Alex Robert\n" +		"Copyright \xc2\xa9 2000-2002 Chema Celorio, Paolo Maggi\n" +		"Copyright \xc2\xa9 2003-2006 Paolo Maggi\n" +		"Copyright \xc2\xa9 2004-2010 Paolo Borelli, Jesse van den Kieboom\nSteve Fr\303\251cinaux, Ignacio Casal Quinteiro"; + +	static const gchar comments[] = \ +		N_("gedit is a small and lightweight text editor for the " +		   "MATE Desktop"); + +	GdkPixbuf *logo; +	gchar *data_dir; +	gchar *logo_file; + +	gedit_debug (DEBUG_COMMANDS); + +	data_dir = gedit_dirs_get_gedit_data_dir (); +	logo_file = g_build_filename (data_dir, +				      "logo", +				      "gedit-logo.png", +				      NULL); +	g_free (data_dir); +	logo = gdk_pixbuf_new_from_file (logo_file, NULL); +	g_free (logo_file); + +	gtk_show_about_dialog (GTK_WINDOW (window), +			       "program-name", "gedit", +			       "authors", authors, +			       "comments", _(comments), +			       "copyright", copyright, +			       "documenters", documenters, +			       "logo", logo, +			       "translator-credits", _("translator-credits"), +			       "version", VERSION, +			       "website", "http://www.gedit.org", +			       NULL); + +	if (logo) +		g_object_unref (logo); +} diff --git a/gedit/gedit-commands-search.c b/gedit/gedit-commands-search.c new file mode 100755 index 00000000..3a44a218 --- /dev/null +++ b/gedit/gedit-commands-search.c @@ -0,0 +1,716 @@ +/* + * gedit-search-commands.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2006 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2006. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> + +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-statusbar.h" +#include "gedit-window.h" +#include "gedit-window-private.h" +#include "gedit-utils.h" +#include "dialogs/gedit-search-dialog.h" + +#define GEDIT_SEARCH_DIALOG_KEY		"gedit-search-dialog-key" +#define GEDIT_LAST_SEARCH_DATA_KEY	"gedit-last-search-data-key" + +typedef struct _LastSearchData LastSearchData; +struct _LastSearchData +{ +	gint x; +	gint y; +}; + +static void +last_search_data_free (LastSearchData *data) +{ +	g_slice_free (LastSearchData, data); +} + +static void +last_search_data_restore_position (GeditSearchDialog *dlg) +{ +	LastSearchData *data; + +	data = g_object_get_data (G_OBJECT (dlg), GEDIT_LAST_SEARCH_DATA_KEY); + +	if (data != NULL) +	{ +		gtk_window_move (GTK_WINDOW (dlg), +				 data->x, +				 data->y); +	} +} + +static void +last_search_data_store_position (GeditSearchDialog *dlg) +{ +	LastSearchData *data; +	 +	data = g_object_get_data (G_OBJECT (dlg), GEDIT_LAST_SEARCH_DATA_KEY); +	 +	if (data == NULL) +	{ +		data = g_slice_new (LastSearchData); +		 +		g_object_set_data_full (G_OBJECT (dlg), +					GEDIT_LAST_SEARCH_DATA_KEY, +					data, +					(GDestroyNotify) last_search_data_free); +	} +	 +	gtk_window_get_position (GTK_WINDOW (dlg), +				 &data->x, +				 &data->y); +} + +/* Use occurences only for Replace All */ +static void +text_found (GeditWindow *window, +	    gint         occurrences) +{ +	if (occurrences > 1) +	{ +		gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +					       window->priv->generic_message_cid, +					       ngettext("Found and replaced %d occurrence", +					     	        "Found and replaced %d occurrences", +					     	        occurrences), +					       occurrences); +	} +	else +	{ +		if (occurrences == 1) +			gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +						       window->priv->generic_message_cid, +						       _("Found and replaced one occurrence")); +		else +			gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +						       window->priv->generic_message_cid, +						       " "); +	} +} + +#define MAX_MSG_LENGTH 40 +static void +text_not_found (GeditWindow *window, +		const gchar *text) +{ +	gchar *searched; +	 +	searched = gedit_utils_str_end_truncate (text, MAX_MSG_LENGTH); + +	gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), +				       window->priv->generic_message_cid, +				       /* Translators: %s is replaced by the text +				          entered by the user in the search box */ +				       _("\"%s\" not found"), searched); +	g_free (searched); +} + +static gboolean +run_search (GeditView   *view, +	    gboolean     wrap_around, +	    gboolean     search_backwards) +{ +	GeditDocument *doc; +	GtkTextIter start_iter; +	GtkTextIter match_start; +	GtkTextIter match_end; +	gboolean found = FALSE; + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); + +	if (!search_backwards) +	{ +		gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), +						      NULL, +						      &start_iter); + +		found = gedit_document_search_forward (doc, +						       &start_iter, +						       NULL, +						       &match_start, +						       &match_end); +	} +	else +	{ +		gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), +						      &start_iter, +						      NULL); + +		found = gedit_document_search_backward (doc, +						        NULL, +						        &start_iter, +						        &match_start, +						        &match_end); +	} + +	if (!found && wrap_around) +	{ +		if (!search_backwards) +			found = gedit_document_search_forward (doc, +							       NULL, +							       NULL, /* FIXME: set the end_inter */ +							       &match_start, +							       &match_end); +		else +			found = gedit_document_search_backward (doc, +							        NULL, /* FIXME: set the start_inter */ +							        NULL,  +							        &match_start, +							        &match_end); +	} +	 +	if (found) +	{ +		gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), +					      &match_start); + +		gtk_text_buffer_move_mark_by_name (GTK_TEXT_BUFFER (doc), +						   "selection_bound", +						   &match_end); + +		gedit_view_scroll_to_cursor (view); +	} +	else +	{ +		gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), +					      &start_iter); +	} + +	return found; +} + +static void +do_find (GeditSearchDialog *dialog, +	 GeditWindow       *window) +{ +	GeditView *active_view; +	GeditDocument *doc; +	gchar *search_text; +	const gchar *entry_text; +	gboolean match_case; +	gboolean entire_word; +	gboolean wrap_around; +	gboolean search_backwards; +	guint flags = 0; +	guint old_flags = 0; +	gboolean found; + +	/* TODO: make the dialog insensitive when all the tabs are closed +	 * and assert here that the view is not NULL */ +	active_view = gedit_window_get_active_view (window); +	if (active_view == NULL) +		return; + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + +	entry_text = gedit_search_dialog_get_search_text (dialog); + +	match_case = gedit_search_dialog_get_match_case (dialog); +	entire_word = gedit_search_dialog_get_entire_word (dialog); +	search_backwards = gedit_search_dialog_get_backwards (dialog); +	wrap_around = gedit_search_dialog_get_wrap_around (dialog); + +	GEDIT_SEARCH_SET_CASE_SENSITIVE (flags, match_case); +	GEDIT_SEARCH_SET_ENTIRE_WORD (flags, entire_word); + +	search_text = gedit_document_get_search_text (doc, &old_flags); + +	if ((search_text == NULL) || +	    (strcmp (search_text, entry_text) != 0) || +	    (flags != old_flags)) +	{ +		gedit_document_set_search_text (doc, entry_text, flags); +	} + +	g_free (search_text); +	 +	found = run_search (active_view, +			    wrap_around, +			    search_backwards); + +	if (found) +		text_found (window, 0); +	else +		text_not_found (window, entry_text); + +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), +					   GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE, +					   found); +} + +/* FIXME: move in gedit-document.c and share it with gedit-view */ +static gboolean +get_selected_text (GtkTextBuffer  *doc, +		   gchar         **selected_text, +		   gint           *len) +{ +	GtkTextIter start, end; + +	g_return_val_if_fail (selected_text != NULL, FALSE); +	g_return_val_if_fail (*selected_text == NULL, FALSE); + +	if (!gtk_text_buffer_get_selection_bounds (doc, &start, &end)) +	{ +		if (len != NULL) +			len = 0; + +		return FALSE; +	} + +	*selected_text = gtk_text_buffer_get_slice (doc, &start, &end, TRUE); + +	if (len != NULL) +		*len = g_utf8_strlen (*selected_text, -1); + +	return TRUE; +} + +static void +replace_selected_text (GtkTextBuffer *buffer, +		       const gchar   *replace) +{ +	g_return_if_fail (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)); +	g_return_if_fail (replace != NULL); + +	gtk_text_buffer_begin_user_action (buffer); + +	gtk_text_buffer_delete_selection (buffer, FALSE, TRUE); + +	gtk_text_buffer_insert_at_cursor (buffer, replace, strlen (replace)); + +	gtk_text_buffer_end_user_action (buffer); +} + +static void +do_replace (GeditSearchDialog *dialog, +	    GeditWindow       *window) +{ +	GeditDocument *doc; +	const gchar *search_entry_text; +	const gchar *replace_entry_text; +	gchar *unescaped_search_text; +	gchar *unescaped_replace_text; +	gchar *selected_text = NULL; +	gboolean match_case; + +	doc = gedit_window_get_active_document (window); +	if (doc == NULL) +		return; + +	search_entry_text = gedit_search_dialog_get_search_text (dialog); +	g_return_if_fail ((search_entry_text) != NULL); +	g_return_if_fail ((*search_entry_text) != '\0'); + +	/* replace text may be "", we just delete */ +	replace_entry_text = gedit_search_dialog_get_replace_text (dialog); +	g_return_if_fail ((replace_entry_text) != NULL); + +	unescaped_search_text = gedit_utils_unescape_search_text (search_entry_text); + +	get_selected_text (GTK_TEXT_BUFFER (doc),  +			   &selected_text,  +			   NULL); + +	match_case = gedit_search_dialog_get_match_case (dialog); + +	if ((selected_text == NULL) || +	    (match_case && (strcmp (selected_text, unescaped_search_text) != 0)) ||  +	    (!match_case && !g_utf8_caselessnmatch (selected_text, +						    unescaped_search_text,  +						    strlen (selected_text),  +						    strlen (unescaped_search_text)) != 0)) +	{ +		do_find (dialog, window); +		g_free (unescaped_search_text); +		g_free (selected_text);	 + +		return; +	} + +	unescaped_replace_text = gedit_utils_unescape_search_text (replace_entry_text);	 +	replace_selected_text (GTK_TEXT_BUFFER (doc), unescaped_replace_text); + +	g_free (unescaped_search_text); +	g_free (selected_text); +	g_free (unescaped_replace_text); +	 +	do_find (dialog, window); +} + +static void +do_replace_all (GeditSearchDialog *dialog, +		GeditWindow       *window) +{ +	GeditView *active_view; +	GeditDocument *doc; +	const gchar *search_entry_text; +	const gchar *replace_entry_text; +	gboolean match_case; +	gboolean entire_word; +	guint flags = 0; +	gint count; + +	active_view = gedit_window_get_active_view (window); +	if (active_view == NULL) +		return; + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); + +	search_entry_text = gedit_search_dialog_get_search_text (dialog); +	g_return_if_fail ((search_entry_text) != NULL); +	g_return_if_fail ((*search_entry_text) != '\0'); + +	/* replace text may be "", we just delete all occurrencies */ +	replace_entry_text = gedit_search_dialog_get_replace_text (dialog); +	g_return_if_fail ((replace_entry_text) != NULL); + +	match_case = gedit_search_dialog_get_match_case (dialog); +	entire_word = gedit_search_dialog_get_entire_word (dialog); + +	GEDIT_SEARCH_SET_CASE_SENSITIVE (flags, match_case); +	GEDIT_SEARCH_SET_ENTIRE_WORD (flags, entire_word); + +	count = gedit_document_replace_all (doc,  +					    search_entry_text, +					    replace_entry_text, +					    flags); + +	if (count > 0) +	{ +		text_found (window, count); +	} +	else +	{ +		text_not_found (window, search_entry_text); +	} + +	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), +					   GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE, +					   FALSE); +} + +static void +search_dialog_response_cb (GeditSearchDialog *dialog, +			   gint               response_id, +			   GeditWindow       *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	switch (response_id) +	{ +		case GEDIT_SEARCH_DIALOG_FIND_RESPONSE: +			do_find (dialog, window); +			break; +		case GEDIT_SEARCH_DIALOG_REPLACE_RESPONSE: +			do_replace (dialog, window); +			break; +		case GEDIT_SEARCH_DIALOG_REPLACE_ALL_RESPONSE: +			do_replace_all (dialog, window); +			break; +		default: +			last_search_data_store_position (dialog); +			gtk_widget_hide (GTK_WIDGET (dialog)); +	} +} + +static gboolean +search_dialog_delete_event_cb (GtkWidget   *widget, +			       GdkEventAny *event, +			       gpointer     user_data) +{ +	gedit_debug (DEBUG_COMMANDS); + +	/* prevent destruction */ +	return TRUE; +} + +static void +search_dialog_destroyed (GeditWindow       *window, +			 GeditSearchDialog *dialog) +{ +	gedit_debug (DEBUG_COMMANDS); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_SEARCH_DIALOG_KEY, +			   NULL); +	g_object_set_data (G_OBJECT (dialog), +			   GEDIT_LAST_SEARCH_DATA_KEY, +			   NULL); +} + +static GtkWidget * +create_dialog (GeditWindow *window, gboolean show_replace) +{ +	GtkWidget *dialog; + +	dialog = gedit_search_dialog_new (GTK_WINDOW (window), show_replace); + +	g_signal_connect (dialog, +			  "response", +			  G_CALLBACK (search_dialog_response_cb), +			  window); +	g_signal_connect (dialog, +			 "delete-event", +			 G_CALLBACK (search_dialog_delete_event_cb), +			 NULL); + +	g_object_set_data (G_OBJECT (window), +			   GEDIT_SEARCH_DIALOG_KEY, +			   dialog); + +	g_object_weak_ref (G_OBJECT (dialog), +			   (GWeakNotify) search_dialog_destroyed, +			   window); + +	return dialog; +} + +void +_gedit_cmd_search_find (GtkAction   *action, +			GeditWindow *window) +{ +	gpointer data; +	GtkWidget *search_dialog; +	GeditDocument *doc; +	gboolean selection_exists; +	gchar *find_text = NULL; +	gint sel_len; + +	gedit_debug (DEBUG_COMMANDS); + +	data = g_object_get_data (G_OBJECT (window), GEDIT_SEARCH_DIALOG_KEY); + +	if (data == NULL) +	{ +		search_dialog = create_dialog (window, FALSE); +	} +	else +	{ +		g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (data)); +		 +		search_dialog = GTK_WIDGET (data); +		 +		/* turn the dialog into a find dialog if needed */ +		if (gedit_search_dialog_get_show_replace (GEDIT_SEARCH_DIALOG (search_dialog))) +			gedit_search_dialog_set_show_replace (GEDIT_SEARCH_DIALOG (search_dialog), +							      FALSE); +	} + +	doc = gedit_window_get_active_document (window); +	g_return_if_fail (doc != NULL); + +	selection_exists = get_selected_text (GTK_TEXT_BUFFER (doc), +					      &find_text, +					      &sel_len); + +	if (selection_exists && find_text != NULL && sel_len < 80) +	{ +		gedit_search_dialog_set_search_text (GEDIT_SEARCH_DIALOG (search_dialog), +						     find_text); +		g_free (find_text); +	} +	else +	{ +		g_free (find_text); +	} + +	gtk_widget_show (search_dialog); +	last_search_data_restore_position (GEDIT_SEARCH_DIALOG (search_dialog)); +	gedit_search_dialog_present_with_time (GEDIT_SEARCH_DIALOG (search_dialog), +					       GDK_CURRENT_TIME); +} + +void +_gedit_cmd_search_replace (GtkAction   *action, +			   GeditWindow *window) +{ +	gpointer data; +	GtkWidget *replace_dialog; +	GeditDocument *doc; +	gboolean selection_exists; +	gchar *find_text = NULL; +	gint sel_len; + +	gedit_debug (DEBUG_COMMANDS); + +	data = g_object_get_data (G_OBJECT (window), GEDIT_SEARCH_DIALOG_KEY); + +	if (data == NULL) +	{ +		replace_dialog = create_dialog (window, TRUE); +	} +	else +	{ +		g_return_if_fail (GEDIT_IS_SEARCH_DIALOG (data)); +		 +		replace_dialog = GTK_WIDGET (data); +		 +		/* turn the dialog into a find dialog if needed */ +		if (!gedit_search_dialog_get_show_replace (GEDIT_SEARCH_DIALOG (replace_dialog))) +			gedit_search_dialog_set_show_replace (GEDIT_SEARCH_DIALOG (replace_dialog), +							      TRUE); +	} + +	doc = gedit_window_get_active_document (window); +	g_return_if_fail (doc != NULL); + +	selection_exists = get_selected_text (GTK_TEXT_BUFFER (doc), +					      &find_text, +					      &sel_len); + +	if (selection_exists && find_text != NULL && sel_len < 80) +	{ +		gedit_search_dialog_set_search_text (GEDIT_SEARCH_DIALOG (replace_dialog), +						     find_text); +		g_free (find_text); +	} +	else +	{ +		g_free (find_text); +	} + +	gtk_widget_show (replace_dialog); +	last_search_data_restore_position (GEDIT_SEARCH_DIALOG (replace_dialog)); +	gedit_search_dialog_present_with_time (GEDIT_SEARCH_DIALOG (replace_dialog), +					       GDK_CURRENT_TIME); +} + +static void +do_find_again (GeditWindow *window, +	       gboolean     backward) +{ +	GeditView *active_view; +	gboolean wrap_around = TRUE; +	gpointer data; +	 +	active_view = gedit_window_get_active_view (window); +	g_return_if_fail (active_view != NULL); + +	data = g_object_get_data (G_OBJECT (window), GEDIT_SEARCH_DIALOG_KEY); +	 +	if (data != NULL) +		wrap_around = gedit_search_dialog_get_wrap_around (GEDIT_SEARCH_DIALOG (data)); +	 +	run_search (active_view, +		    wrap_around, +		    backward); +} + +void +_gedit_cmd_search_find_next (GtkAction   *action, +			     GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	do_find_again (window, FALSE); +} + +void +_gedit_cmd_search_find_prev (GtkAction   *action, +			     GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	do_find_again (window, TRUE); +} + +void +_gedit_cmd_search_clear_highlight (GtkAction   *action, +				   GeditWindow *window) +{ +	GeditDocument *doc; + +	gedit_debug (DEBUG_COMMANDS); + +	doc = gedit_window_get_active_document (window); +	gedit_document_set_search_text (GEDIT_DOCUMENT (doc), +					"", +					GEDIT_SEARCH_DONT_SET_FLAGS); +} + +void +_gedit_cmd_search_goto_line (GtkAction   *action, +			     GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	if (active_view == NULL) +		return; + +	/* Focus the view if needed: we need to focus the view otherwise  +	   activating the binding for goto line has no effect */ +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); + + +	/* goto line is builtin in GeditView, just activate +	 * the corrisponding binding. +	 */ +	gtk_bindings_activate (GTK_OBJECT (active_view), +			       GDK_i, +			       GDK_CONTROL_MASK); +} + +void +_gedit_cmd_search_incremental_search (GtkAction   *action, +				      GeditWindow *window) +{ +	GeditView *active_view; + +	gedit_debug (DEBUG_COMMANDS); + +	active_view = gedit_window_get_active_view (window); +	if (active_view == NULL) +		return; + +	/* Focus the view if needed: we need to focus the view otherwise  +	   activating the binding for incremental search has no effect */ +	gtk_widget_grab_focus (GTK_WIDGET (active_view)); +	 +	/* incremental search is builtin in GeditView, just activate +	 * the corrisponding binding. +	 */ +	gtk_bindings_activate (GTK_OBJECT (active_view), +			       GDK_k, +			       GDK_CONTROL_MASK); +} diff --git a/gedit/gedit-commands-view.c b/gedit/gedit-commands-view.c new file mode 100755 index 00000000..2366e49c --- /dev/null +++ b/gedit/gedit-commands-view.c @@ -0,0 +1,154 @@ +/* + * gedit-view-commands.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> + +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-window.h" +#include "gedit-window-private.h" + + +void +_gedit_cmd_view_show_toolbar (GtkAction   *action, +			     GeditWindow *window) +{ +	gboolean visible; + +	gedit_debug (DEBUG_COMMANDS); + +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + +	if (visible) +		gtk_widget_show (window->priv->toolbar); +	else +		gtk_widget_hide (window->priv->toolbar); +} + +void +_gedit_cmd_view_show_statusbar (GtkAction   *action, +			       GeditWindow *window) +{ +	gboolean visible; + +	gedit_debug (DEBUG_COMMANDS); + +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + +	if (visible) +		gtk_widget_show (window->priv->statusbar); +	else +		gtk_widget_hide (window->priv->statusbar); +} + +void +_gedit_cmd_view_show_side_pane (GtkAction   *action, +			       GeditWindow *window) +{ +	gboolean visible; +	GeditPanel *panel; + +	gedit_debug (DEBUG_COMMANDS); + +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + +	panel = gedit_window_get_side_panel (window); + +	if (visible) +	{ +		gtk_widget_show (GTK_WIDGET (panel)); +		gtk_widget_grab_focus (GTK_WIDGET (panel)); +	} +	else +	{ +		gtk_widget_hide (GTK_WIDGET (panel)); +	} +} + +void +_gedit_cmd_view_show_bottom_pane (GtkAction   *action, +				 GeditWindow *window) +{ +	gboolean visible; +	GeditPanel *panel; + +	gedit_debug (DEBUG_COMMANDS); + +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + +	panel = gedit_window_get_bottom_panel (window); + +	if (visible) +	{ +		gtk_widget_show (GTK_WIDGET (panel)); +		gtk_widget_grab_focus (GTK_WIDGET (panel)); +	} +	else +	{ +		gtk_widget_hide (GTK_WIDGET (panel)); +	} +} + +void +_gedit_cmd_view_toggle_fullscreen_mode (GtkAction *action, +					GeditWindow *window) +{ +	gedit_debug (DEBUG_COMMANDS); + +	if (_gedit_window_is_fullscreen (window)) +		_gedit_window_unfullscreen (window); +	else +		_gedit_window_fullscreen (window); +} + +void +_gedit_cmd_view_leave_fullscreen_mode (GtkAction *action, +				       GeditWindow *window) +{ +	GtkAction *view_action; + +	view_action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +						   "ViewFullscreen"); +	g_signal_handlers_block_by_func +		(view_action, G_CALLBACK (_gedit_cmd_view_toggle_fullscreen_mode), +		 window); +	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (view_action), +				      FALSE); +	_gedit_window_unfullscreen (window); +	g_signal_handlers_unblock_by_func +		(view_action, G_CALLBACK (_gedit_cmd_view_toggle_fullscreen_mode), +		 window); +} diff --git a/gedit/gedit-commands.h b/gedit/gedit-commands.h new file mode 100755 index 00000000..08e70885 --- /dev/null +++ b/gedit/gedit-commands.h @@ -0,0 +1,166 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-commands.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi  + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_COMMANDS_H__ +#define __GEDIT_COMMANDS_H__ + +#include <gtk/gtk.h> +#include <gedit/gedit-window.h> + +G_BEGIN_DECLS + +/* Do nothing if URI does not exist */ +void		 gedit_commands_load_uri		(GeditWindow         *window, +							 const gchar         *uri, +							 const GeditEncoding *encoding, +							 gint                 line_pos); + +/* Ignore non-existing URIs */ +gint		 gedit_commands_load_uris		(GeditWindow         *window, +							 const GSList        *uris, +							 const GeditEncoding *encoding, +							 gint                 line_pos); + +void		 gedit_commands_save_document		(GeditWindow         *window, +                                                         GeditDocument       *document); + +void		 gedit_commands_save_all_documents 	(GeditWindow         *window); + +/* + * Non-exported functions + */ + +/* Create titled documens for non-existing URIs */ +gint		_gedit_cmd_load_files_from_prompt	(GeditWindow         *window, +							 GSList              *files, +							 const GeditEncoding *encoding, +							 gint                 line_pos); + +void		_gedit_cmd_file_new			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_open			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_save			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_save_as			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_save_all		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_revert			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_open_uri		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_print_preview		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_print			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_close			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_close_all		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_file_quit			(GtkAction   *action, +							 GeditWindow *window); + +void		_gedit_cmd_edit_undo			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_redo			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_cut			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_copy			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_paste			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_delete			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_select_all		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_edit_preferences		(GtkAction   *action, +							 GeditWindow *window); + +void		_gedit_cmd_view_show_toolbar		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_view_show_statusbar		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_view_show_side_pane		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_view_show_bottom_pane	(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_view_toggle_fullscreen_mode	(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_view_leave_fullscreen_mode	(GtkAction   *action, +							 GeditWindow *window); + +void		_gedit_cmd_search_find			(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_find_next		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_find_prev		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_replace		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_clear_highlight	(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_goto_line		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_search_incremental_search	(GtkAction   *action, +							 GeditWindow *window);							  +							  +void		_gedit_cmd_documents_previous_document	(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_documents_next_document	(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_documents_move_to_new_window	(GtkAction   *action, +							 GeditWindow *window); + +void		_gedit_cmd_help_contents		(GtkAction   *action, +							 GeditWindow *window); +void		_gedit_cmd_help_about			(GtkAction   *action, +							 GeditWindow *window); + +void		_gedit_cmd_file_close_tab 		(GeditTab    *tab, +							 GeditWindow *window); + +void		_gedit_cmd_file_save_documents_list	(GeditWindow *window, +							 GList       *docs); + + +#if !GTK_CHECK_VERSION (2, 17, 4) +void		_gedit_cmd_file_page_setup		(GtkAction   *action, +							 GeditWindow *window); +#endif + + +G_END_DECLS + +#endif /* __GEDIT_COMMANDS_H__ */  diff --git a/gedit/gedit-debug.c b/gedit/gedit-debug.c new file mode 100755 index 00000000..163a5a54 --- /dev/null +++ b/gedit/gedit-debug.c @@ -0,0 +1,159 @@ +/* + * gedit-debug.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002 - 2005 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "gedit-debug.h" + +#define ENABLE_PROFILING + +#ifdef ENABLE_PROFILING +static GTimer *timer = NULL; +static gdouble last = 0.0; +#endif + +static GeditDebugSection debug = GEDIT_NO_DEBUG; + +void +gedit_debug_init (void) +{ +	if (g_getenv ("GEDIT_DEBUG") != NULL) +	{ +		/* enable all debugging */ +		debug = ~GEDIT_NO_DEBUG; +		goto out; +	} + +	if (g_getenv ("GEDIT_DEBUG_VIEW") != NULL) +		debug = debug | GEDIT_DEBUG_VIEW; +	if (g_getenv ("GEDIT_DEBUG_SEARCH") != NULL) +		debug = debug | GEDIT_DEBUG_SEARCH; +	if (g_getenv ("GEDIT_DEBUG_PREFS") != NULL) +		debug = debug | GEDIT_DEBUG_PREFS; +	if (g_getenv ("GEDIT_DEBUG_PRINT") != NULL) +		debug = debug | GEDIT_DEBUG_PRINT; +	if (g_getenv ("GEDIT_DEBUG_PLUGINS") != NULL) +		debug = debug | GEDIT_DEBUG_PLUGINS; +	if (g_getenv ("GEDIT_DEBUG_TAB") != NULL) +		debug = debug | GEDIT_DEBUG_TAB; +	if (g_getenv ("GEDIT_DEBUG_DOCUMENT") != NULL) +		debug = debug | GEDIT_DEBUG_DOCUMENT; +	if (g_getenv ("GEDIT_DEBUG_COMMANDS") != NULL) +		debug = debug | GEDIT_DEBUG_COMMANDS; +	if (g_getenv ("GEDIT_DEBUG_APP") != NULL) +		debug = debug | GEDIT_DEBUG_APP; +	if (g_getenv ("GEDIT_DEBUG_SESSION") != NULL) +		debug = debug | GEDIT_DEBUG_SESSION; +	if (g_getenv ("GEDIT_DEBUG_UTILS") != NULL) +		debug = debug | GEDIT_DEBUG_UTILS; +	if (g_getenv ("GEDIT_DEBUG_METADATA") != NULL) +		debug = debug | GEDIT_DEBUG_METADATA; +	if (g_getenv ("GEDIT_DEBUG_WINDOW") != NULL) +		debug = debug | GEDIT_DEBUG_WINDOW; +	if (g_getenv ("GEDIT_DEBUG_LOADER") != NULL) +		debug = debug | GEDIT_DEBUG_LOADER; +	if (g_getenv ("GEDIT_DEBUG_SAVER") != NULL) +		debug = debug | GEDIT_DEBUG_SAVER; + +out:		 + +#ifdef ENABLE_PROFILING +	if (debug != GEDIT_NO_DEBUG) +		timer = g_timer_new (); +#endif +	return; +} + +void +gedit_debug_message (GeditDebugSection  section, +		     const gchar       *file, +		     gint               line, +		     const gchar       *function, +		     const gchar       *format, ...) +{ +	if (G_UNLIKELY (debug & section)) +	{	 +#ifdef ENABLE_PROFILING +		gdouble seconds; +#endif + +		va_list args; +		gchar *msg; + +		g_return_if_fail (format != NULL); + +		va_start (args, format); +		msg = g_strdup_vprintf (format, args); +		va_end (args); + +#ifdef ENABLE_PROFILING +		g_return_if_fail (timer != NULL); + +		seconds = g_timer_elapsed (timer, NULL); +		g_print ("[%f (%f)] %s:%d (%s) %s\n",  +			 seconds, seconds - last,  file, line, function, msg); +		last = seconds;			  +#else +		g_print ("%s:%d (%s) %s\n", file, line, function, msg);	 +#endif + +		fflush (stdout); + +		g_free (msg); +	} +} + +void gedit_debug (GeditDebugSection  section, +		  const gchar       *file, +		  gint               line, +		  const gchar       *function) +{ +	if (G_UNLIKELY (debug & section)) +	{ +#ifdef ENABLE_PROFILING +		gdouble seconds; + +		g_return_if_fail (timer != NULL); + +		seconds = g_timer_elapsed (timer, NULL);		 +		g_print ("[%f (%f)] %s:%d (%s)\n",  +			 seconds, seconds - last, file, line, function); +		last = seconds; +#else +		g_print ("%s:%d (%s)\n", file, line, function); +#endif		 +		fflush (stdout); +	} +} diff --git a/gedit/gedit-debug.h b/gedit/gedit-debug.h new file mode 100755 index 00000000..af7d7370 --- /dev/null +++ b/gedit/gedit-debug.h @@ -0,0 +1,93 @@ +/* + * gedit-debug.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002 - 2005 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_DEBUG_H__ +#define __GEDIT_DEBUG_H__ + +#include <glib.h> + +/* + * Set an environmental var of the same name to turn on + * debugging output. Setting GEDIT_DEBUG will turn on all + * sections. + */ +typedef enum { +	GEDIT_NO_DEBUG       = 0, +	GEDIT_DEBUG_VIEW     = 1 << 0, +	GEDIT_DEBUG_SEARCH   = 1 << 1, +	GEDIT_DEBUG_PRINT    = 1 << 2, +	GEDIT_DEBUG_PREFS    = 1 << 3, +	GEDIT_DEBUG_PLUGINS  = 1 << 4, +	GEDIT_DEBUG_TAB      = 1 << 5, +	GEDIT_DEBUG_DOCUMENT = 1 << 6, +	GEDIT_DEBUG_COMMANDS = 1 << 7, +	GEDIT_DEBUG_APP      = 1 << 8, +	GEDIT_DEBUG_SESSION  = 1 << 9, +	GEDIT_DEBUG_UTILS    = 1 << 10, +	GEDIT_DEBUG_METADATA = 1 << 11, +	GEDIT_DEBUG_WINDOW   = 1 << 12, +	GEDIT_DEBUG_LOADER   = 1 << 13, +	GEDIT_DEBUG_SAVER    = 1 << 14 +} GeditDebugSection; + + +#define	DEBUG_VIEW	GEDIT_DEBUG_VIEW,    __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_SEARCH	GEDIT_DEBUG_SEARCH,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PRINT	GEDIT_DEBUG_PRINT,   __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PREFS	GEDIT_DEBUG_PREFS,   __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_PLUGINS	GEDIT_DEBUG_PLUGINS, __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_TAB	GEDIT_DEBUG_TAB,     __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_DOCUMENT	GEDIT_DEBUG_DOCUMENT,__FILE__, __LINE__, G_STRFUNC +#define	DEBUG_COMMANDS	GEDIT_DEBUG_COMMANDS,__FILE__, __LINE__, G_STRFUNC +#define	DEBUG_APP	GEDIT_DEBUG_APP,     __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_SESSION	GEDIT_DEBUG_SESSION, __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_UTILS	GEDIT_DEBUG_UTILS,   __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_METADATA	GEDIT_DEBUG_METADATA,__FILE__, __LINE__, G_STRFUNC +#define	DEBUG_WINDOW	GEDIT_DEBUG_WINDOW,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_LOADER	GEDIT_DEBUG_LOADER,  __FILE__, __LINE__, G_STRFUNC +#define	DEBUG_SAVER	GEDIT_DEBUG_SAVER,   __FILE__, __LINE__, G_STRFUNC + +void gedit_debug_init (void); + +void gedit_debug (GeditDebugSection  section, +		  const gchar       *file, +		  gint               line, +		  const gchar       *function); + +void gedit_debug_message (GeditDebugSection  section, +			  const gchar       *file, +			  gint               line, +			  const gchar       *function, +			  const gchar       *format, ...) G_GNUC_PRINTF(5, 6); + + +#endif /* __GEDIT_DEBUG_H__ */ diff --git a/gedit/gedit-dirs.c b/gedit/gedit-dirs.c new file mode 100755 index 00000000..a1467f61 --- /dev/null +++ b/gedit/gedit-dirs.c @@ -0,0 +1,320 @@ +/* + * gedit-dirs.c + * This file is part of gedit + * + * Copyright (C) 2008 Ignacio Casal Quinteiro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-dirs.h" + +#ifdef OS_OSX +#include <ige-mac-bundle.h> +#endif + +gchar * +gedit_dirs_get_user_config_dir (void) +{ +	gchar *config_dir = NULL; + +#ifndef G_OS_WIN32 +	const gchar *envvar; +	const gchar *home; + +	/* Support old libmate env var */ +	envvar = g_getenv ("MATE22_USER_DIR"); +	if (envvar != NULL) +	{ +		config_dir = g_build_filename (envvar, +					       "gedit", +					       NULL); + +	} +	else +	{ +		home = g_get_home_dir (); + +		if (home != NULL) +		{ +			config_dir = g_build_filename (home, +						       ".mate2", +						       "gedit", +						       NULL); +		} +	} +#else +	config_dir = g_build_filename (g_get_user_config_dir (), +				       "gedit", +				       NULL); +#endif + +	return config_dir; +} + +gchar * +gedit_dirs_get_user_cache_dir (void) +{ +	const gchar *cache_dir; + +	cache_dir = g_get_user_cache_dir (); + +	return g_build_filename (cache_dir, +				 "gedit", +				 NULL); +} + +gchar * +gedit_dirs_get_user_plugins_dir (void) +{ +	gchar *config_dir; +	gchar *plugin_dir; + +	config_dir = gedit_dirs_get_user_config_dir (); + +	plugin_dir = g_build_filename (config_dir, +				       "plugins", +				       NULL); +	g_free (config_dir); +	 +	return plugin_dir; +} + +gchar * +gedit_dirs_get_user_accels_file (void) +{ +	gchar *accels = NULL; + +#ifndef G_OS_WIN32 +	const gchar *envvar; +	const gchar *home; + +	/* on linux accels are stored in .mate2/accels +	 * for historic reasons (backward compat with the +	 * old libmate that took care of saving them */ + +	/* Support old libmate env var */ +	envvar = g_getenv ("MATE22_USER_DIR"); +	if (envvar != NULL) +	{ +		accels = g_build_filename (envvar, +					   "accels", +					   "gedit", +					   NULL); +	} +	else +	{ +		home = g_get_home_dir (); + +		if (home != NULL) +		{ +			accels = g_build_filename (home, +						   ".mate2", +						   "accels", +						   "gedit", +						   NULL); +		} +	} +#else +	{ +		gchar *config_dir = NULL; + +		config_dir = gedit_dirs_get_user_config_dir (); +		accels = g_build_filename (config_dir, +					   "accels", +					   "gedit", +					   NULL); + +		g_free (config_dir); +	} +#endif + +	return accels; +} + +gchar * +gedit_dirs_get_gedit_data_dir (void) +{ +	gchar *data_dir; + +#ifdef G_OS_WIN32 +	gchar *win32_dir; +	 +	win32_dir = g_win32_get_package_installation_directory_of_module (NULL); + +	data_dir = g_build_filename (win32_dir, +				     "share", +				     "gedit-2", +				     NULL); +	 +	g_free (win32_dir); +#elif defined (OS_OSX) +	IgeMacBundle *bundle = ige_mac_bundle_get_default (); + +	if (ige_mac_bundle_get_is_app_bundle (bundle)) +	{ +		const gchar *bundle_data_dir = ige_mac_bundle_get_datadir (bundle); + +		data_dir = g_build_filename (bundle_data_dir, +		                             "gedit-2", +	                                     NULL); +	} +	else +	{ +		data_dir = g_build_filename (DATADIR, "gedit-2", NULL); +	} +#else +	data_dir = g_build_filename (DATADIR, +	                             "gedit-2", +	                             NULL); +#endif + +	return data_dir; +} + +gchar * +gedit_dirs_get_gedit_locale_dir (void) +{ +	gchar *locale_dir; + +#ifdef G_OS_WIN32 +	gchar *win32_dir; +	 +	win32_dir = g_win32_get_package_installation_directory_of_module (NULL); + +	locale_dir = g_build_filename (win32_dir, +				       "share", +				       "locale", +				       NULL); +	 +	g_free (win32_dir); +#elif defined (OS_OSX) +	IgeMacBundle *bundle = ige_mac_bundle_get_default (); + +	if (ige_mac_bundle_get_is_app_bundle (bundle)) +	{ +		locale_dir = g_strdup (ige_mac_bundle_get_localedir (bundle)); +	} +	else +	{ +		locale_dir = g_build_filename (DATADIR, +		                               "locale", +		                               NULL); +	} +#else +	locale_dir = g_build_filename (DATADIR, +				       "locale", +				       NULL); +#endif + +	return locale_dir; +} + +gchar * +gedit_dirs_get_gedit_lib_dir (void) +{ +	gchar *lib_dir; + +#ifdef G_OS_WIN32 +	gchar *win32_dir; +	 +	win32_dir = g_win32_get_package_installation_directory_of_module (NULL); + +	lib_dir = g_build_filename (win32_dir, +				    "lib", +				    "gedit-2", +				    NULL); +	 +	g_free (win32_dir); +#elif defined (OS_OSX) +	IgeMacBundle *bundle = ige_mac_bundle_get_default (); + +	if (ige_mac_bundle_get_is_app_bundle (bundle)) +	{ + 		const gchar *path = ige_mac_bundle_get_resourcesdir (bundle); +		lib_dir = g_build_filename (path, +	                            	"lib", +	                            	"gedit-2", +	                            	NULL); +	} +	else +	{ +		lib_dir = g_build_filename (LIBDIR, +					    "gedit-2", +					    NULL); +	} +#else +	lib_dir = g_build_filename (LIBDIR, +				    "gedit-2", +				    NULL); +#endif + +	return lib_dir; +} + +gchar * +gedit_dirs_get_gedit_plugins_dir (void) +{ +	gchar *lib_dir; +	gchar *plugin_dir; +	 +	lib_dir = gedit_dirs_get_gedit_lib_dir (); +	 +	plugin_dir = g_build_filename (lib_dir, +				       "plugins", +				       NULL); +	g_free (lib_dir); +	 +	return plugin_dir; +} + +gchar * +gedit_dirs_get_gedit_plugin_loaders_dir (void) +{ +	gchar *lib_dir; +	gchar *loader_dir; +	 +	lib_dir = gedit_dirs_get_gedit_lib_dir (); +	 +	loader_dir = g_build_filename (lib_dir, +				       "plugin-loaders", +				       NULL); +	g_free (lib_dir); +	 +	return loader_dir; +} + +gchar * +gedit_dirs_get_ui_file (const gchar *file) +{ +	gchar *datadir; +	gchar *ui_file; + +	g_return_val_if_fail (file != NULL, NULL); +	 +	datadir = gedit_dirs_get_gedit_data_dir (); +	ui_file = g_build_filename (datadir, +				    "ui", +				    file, +				    NULL); +	g_free (datadir); +	 +	return ui_file; +} diff --git a/gedit/gedit-dirs.h b/gedit/gedit-dirs.h new file mode 100755 index 00000000..eddd00d0 --- /dev/null +++ b/gedit/gedit-dirs.h @@ -0,0 +1,54 @@ +/* + * gedit-dirs.h + * This file is part of gedit + * + * Copyright (C) 2008 Ignacio Casal Quinteiro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + + +#ifndef __GEDIT_DIRS_H__ +#define __GEDIT_DIRS_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +gchar		*gedit_dirs_get_user_config_dir		(void); + +gchar		*gedit_dirs_get_user_cache_dir		(void); + +gchar		*gedit_dirs_get_user_plugins_dir	(void); + +gchar		*gedit_dirs_get_user_accels_file	(void); + +gchar		*gedit_dirs_get_gedit_data_dir		(void); + +gchar		*gedit_dirs_get_gedit_locale_dir	(void); + +gchar		*gedit_dirs_get_gedit_lib_dir		(void); + +gchar		*gedit_dirs_get_gedit_plugins_dir	(void); + +gchar		*gedit_dirs_get_gedit_plugin_loaders_dir +							(void); + +gchar		*gedit_dirs_get_ui_file			(const gchar *file); + +G_END_DECLS + +#endif /* __GEDIT_DIRS_H__ */ diff --git a/gedit/gedit-document-input-stream.c b/gedit/gedit-document-input-stream.c new file mode 100755 index 00000000..5a1f9614 --- /dev/null +++ b/gedit/gedit-document-input-stream.c @@ -0,0 +1,479 @@ +/* + * gedit-document-input-stream.c + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + +#include "config.h" + +#include <glib.h> +#include <gio/gio.h> +#include <string.h> +#include "gedit-document-input-stream.h" +#include "gedit-enum-types.h" + +/* NOTE: never use async methods on this stream, the stream is just + * a wrapper around GtkTextBuffer api so that we can use GIO Stream + * methods, but the undelying code operates on a GtkTextBuffer, so + * there is no I/O involved and should be accessed only by the main + * thread */ + + +G_DEFINE_TYPE (GeditDocumentInputStream, gedit_document_input_stream, G_TYPE_INPUT_STREAM); + +struct _GeditDocumentInputStreamPrivate +{ +	GtkTextBuffer *buffer; +	GtkTextMark   *pos; +	gint           bytes_partial; + +	GeditDocumentNewlineType newline_type; + +	guint newline_added : 1; +	guint is_initialized : 1; +}; + +enum +{ +	PROP_0, +	PROP_BUFFER, +	PROP_NEWLINE_TYPE +}; + +static gssize     gedit_document_input_stream_read     (GInputStream      *stream, +							void              *buffer, +							gsize              count, +							GCancellable      *cancellable, +							GError           **error); +static gboolean   gedit_document_input_stream_close    (GInputStream      *stream, +							GCancellable      *cancellable, +							GError           **error); + +static void +gedit_document_input_stream_set_property (GObject      *object, +					  guint         prop_id, +					  const GValue *value, +					  GParamSpec   *pspec) +{ +	GeditDocumentInputStream *stream = GEDIT_DOCUMENT_INPUT_STREAM (object); + +	switch (prop_id) +	{ +		case PROP_BUFFER: +			stream->priv->buffer = GTK_TEXT_BUFFER (g_value_get_object (value)); +			break; + +		case PROP_NEWLINE_TYPE: +			stream->priv->newline_type = g_value_get_enum (value); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_input_stream_get_property (GObject    *object, +					  guint       prop_id, +					  GValue     *value, +					  GParamSpec *pspec) +{ +	GeditDocumentInputStream *stream = GEDIT_DOCUMENT_INPUT_STREAM (object); + +	switch (prop_id) +	{ +		case PROP_BUFFER: +			g_value_set_object (value, stream->priv->buffer); +			break; + +		case PROP_NEWLINE_TYPE: +			g_value_set_enum (value, stream->priv->newline_type); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_input_stream_class_init (GeditDocumentInputStreamClass *klass) +{ +	GObjectClass *gobject_class = G_OBJECT_CLASS (klass); +	GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); + +	g_type_class_add_private (klass, sizeof (GeditDocumentInputStreamPrivate)); + +	gobject_class->get_property = gedit_document_input_stream_get_property; +	gobject_class->set_property = gedit_document_input_stream_set_property; + +	stream_class->read_fn = gedit_document_input_stream_read; +	stream_class->close_fn = gedit_document_input_stream_close; + +	g_object_class_install_property (gobject_class, +					 PROP_BUFFER, +					 g_param_spec_object ("buffer", +							      "Buffer", +							      "The buffer which is read", +							      GTK_TYPE_TEXT_BUFFER, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	/** +	 * GeditDocumentInputStream:newline-type: +	 * +	 * The :newline-type property determines what is considered +	 * as a line ending when reading complete lines from the stream. +	 */  +	g_object_class_install_property (gobject_class, +					 PROP_NEWLINE_TYPE, +					 g_param_spec_enum ("newline-type", +							    "Newline type", +							    "The accepted types of line ending", +							    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE, +							    GEDIT_DOCUMENT_NEWLINE_TYPE_LF, +							    G_PARAM_READWRITE | +							    G_PARAM_STATIC_NAME | +							    G_PARAM_STATIC_BLURB | +							    G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gedit_document_input_stream_init (GeditDocumentInputStream *stream) +{ +	stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, +						    GEDIT_TYPE_DOCUMENT_INPUT_STREAM, +						    GeditDocumentInputStreamPrivate); +} + +static gsize +get_new_line_size (GeditDocumentInputStream *stream) +{ +	gsize ret; + +	switch (stream->priv->newline_type) +	{ +		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR: +		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF: +			ret = 1; +			break; + +		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF: +			ret = 2; +			break; + +		default: +			g_warn_if_reached (); +			ret = 1; +			break; +	} + +	return ret; +} + +/** + * gedit_document_input_stream_new: + * @buffer: a #GtkTextBuffer + * + * Reads the data from @buffer. + * + * Returns: a new #GInputStream to read @buffer + */ +GInputStream * +gedit_document_input_stream_new (GtkTextBuffer           *buffer, +				 GeditDocumentNewlineType type) +{ +	GeditDocumentInputStream *stream; + +	g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); + +	stream = g_object_new (GEDIT_TYPE_DOCUMENT_INPUT_STREAM, +			       "buffer", buffer, +			       "newline-type", type, +			       NULL); + +	return G_INPUT_STREAM (stream); +} + +gsize +gedit_document_input_stream_get_total_size (GeditDocumentInputStream *stream) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_INPUT_STREAM (stream), 0); + +	return gtk_text_buffer_get_char_count (stream->priv->buffer); +} + +gsize +gedit_document_input_stream_tell (GeditDocumentInputStream *stream) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_INPUT_STREAM (stream), 0); + +	/* FIXME: is this potentially inefficient? If yes, we could keep +	   track of the offset internally, assuming the mark doesn't move +	   during the operation */ +	if (!stream->priv->is_initialized) +	{ +		return 0; +	} +	else +	{ +		GtkTextIter iter; + +		gtk_text_buffer_get_iter_at_mark (stream->priv->buffer, +						  &iter, +						  stream->priv->pos); +		return gtk_text_iter_get_offset (&iter); +	} +} + +static const gchar * +get_new_line (GeditDocumentInputStream *stream) +{ +	const gchar *ret; + +	switch (stream->priv->newline_type) +	{ +		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR: +			ret = "\r"; +			break; + +		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF: +			ret = "\n"; +			break; + +		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF: +			ret = "\r\n"; +			break; + +		default: +			g_warn_if_reached (); +			ret = "\n"; +			break; +	} + +	return ret; +} + +static gsize +read_line (GeditDocumentInputStream *stream, +	   gchar                    *outbuf, +	   gsize                     space_left) +{ +	GtkTextIter start, next, end; +	gchar *buf; +	gint bytes; /* int since it's what iter_get_offset returns */ +	gsize bytes_to_write, newline_size, read; +	const gchar *newline; +	gboolean is_last; + +	gtk_text_buffer_get_iter_at_mark (stream->priv->buffer, +					  &start, +					  stream->priv->pos); + +	if (gtk_text_iter_is_end (&start)) +		return 0; + +	end = next = start; +	newline = get_new_line (stream); + +	/* Check needed for empty lines */ +	if (!gtk_text_iter_ends_line (&end)) +		gtk_text_iter_forward_to_line_end (&end); + +	gtk_text_iter_forward_line (&next); + +	buf = gtk_text_iter_get_slice (&start, &end); + +	/* the bytes of a line includes also the newline, so with the +	   offsets we remove the newline and we add the new newline size */ +	bytes = gtk_text_iter_get_bytes_in_line (&start) - stream->priv->bytes_partial; + +	/* bytes_in_line includes the newlines, so we remove that assuming that +	   they are single byte characters */ +	bytes = bytes - (gtk_text_iter_get_offset (&next) - gtk_text_iter_get_offset (&end)); +	is_last = gtk_text_iter_is_end (&end); + +	/* bytes_to_write contains the amount of bytes we would like to write. +	   This means its the amount of bytes in the line (without the newline +	   in the buffer) + the amount of bytes for the newline we want to +	   write (newline_size) */ +	bytes_to_write = bytes; + +	/* do not add the new newline_size for the last line */ +	newline_size = get_new_line_size (stream); +	if (!is_last) +		bytes_to_write += newline_size; + +	if (bytes_to_write > space_left) +	{ +		gchar *ptr; +		gint char_offset; +		gint written; +		gsize to_write; + +		/* Here the line does not fit in the buffer, we thus write +		   the amount of bytes we can still fit, storing the position +		   for the next read with the mark. Do not try to write the +		   new newline in this case, it will be handled in the next +		   iteration */ +		to_write = MIN (space_left, bytes); +		ptr = buf; +		written = 0; +		char_offset = 0; + +		while (written < to_write) +		{ +			gint w; + +			ptr = g_utf8_next_char (ptr); +			w = (ptr - buf); +			if (w > to_write) +			{ +				break; +			} +			else +			{ +				written = w; +				++char_offset; +			} +		} + +		memcpy (outbuf, buf, written); + +		/* Note: offset is one past what we wrote */ +		gtk_text_iter_forward_chars (&start, char_offset); +		stream->priv->bytes_partial += written; +		read = written; +	} +	else +	{ +		/* First just copy the bytes without the newline */ +		memcpy (outbuf, buf, bytes); + +		/* Then add the newline, but not for the last line */ +		if (!is_last) +		{ +			memcpy (outbuf + bytes, newline, newline_size); +		} + +		start = next; +		stream->priv->bytes_partial = 0; +		read = bytes_to_write; +	} + +	gtk_text_buffer_move_mark (stream->priv->buffer, +				   stream->priv->pos, +				   &start); + +	g_free (buf); +	return read; +} + +static gssize +gedit_document_input_stream_read (GInputStream  *stream, +				  void          *buffer, +				  gsize          count, +				  GCancellable  *cancellable, +				  GError       **error) +{ +	GeditDocumentInputStream *dstream; +	GtkTextIter iter; +	gssize space_left, read, n; + +	dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream); + +	if (count < 6) +	{ +		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, +				     "Not enougth space in destination"); +		return -1; +	} + +	if (g_cancellable_set_error_if_cancelled (cancellable, error)) +		return -1; + +	/* Initialize the mark to the first char in the text buffer */ +	if (!dstream->priv->is_initialized) +	{ +		gtk_text_buffer_get_start_iter (dstream->priv->buffer, &iter); +		dstream->priv->pos = gtk_text_buffer_create_mark (dstream->priv->buffer, +								 NULL, +								 &iter, +								 FALSE); + +		dstream->priv->is_initialized = TRUE; +	} + +	space_left = count; +	read = 0; + +	do +	{ +		n = read_line (dstream, buffer + read, space_left); +		read += n; +		space_left -= n; +	} while (space_left > 0 && n != 0 && dstream->priv->bytes_partial == 0); + +	/* Make sure that non-empty files are always terminated with \n (see bug #95676). +	 * Note that we strip the trailing \n when loading the file */ +	gtk_text_buffer_get_iter_at_mark (dstream->priv->buffer, +					  &iter, +					  dstream->priv->pos); + +	if (gtk_text_iter_is_end (&iter) && +	    !gtk_text_iter_is_start (&iter)) +	{ +		gssize newline_size; + +		newline_size = get_new_line_size (dstream); + +		if (space_left >= newline_size && +		    !dstream->priv->newline_added) +		{ +			const gchar *newline; + +			newline = get_new_line (dstream); + +			memcpy (buffer + read, newline, newline_size); + +			read += newline_size; +			dstream->priv->newline_added = TRUE; +		} +	} + +	return read; +} + +static gboolean +gedit_document_input_stream_close (GInputStream  *stream, +				   GCancellable  *cancellable, +				   GError       **error) +{ +	GeditDocumentInputStream *dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream); + +	dstream->priv->newline_added = FALSE; + +	if (dstream->priv->is_initialized) +	{ +		gtk_text_buffer_delete_mark (dstream->priv->buffer, dstream->priv->pos); +	} + +	return TRUE; +} diff --git a/gedit/gedit-document-input-stream.h b/gedit/gedit-document-input-stream.h new file mode 100755 index 00000000..01f6a491 --- /dev/null +++ b/gedit/gedit-document-input-stream.h @@ -0,0 +1,68 @@ +/* + * gedit-document-input-stream.h + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + +#ifndef __GEDIT_DOCUMENT_INPUT_STREAM_H__ +#define __GEDIT_DOCUMENT_INPUT_STREAM_H__ + +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include "gedit-document.h" + +G_BEGIN_DECLS + +#define GEDIT_TYPE_DOCUMENT_INPUT_STREAM		(gedit_document_input_stream_get_type ()) +#define GEDIT_DOCUMENT_INPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStream)) +#define GEDIT_DOCUMENT_INPUT_STREAM_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStream const)) +#define GEDIT_DOCUMENT_INPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStreamClass)) +#define GEDIT_IS_DOCUMENT_INPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM)) +#define GEDIT_IS_DOCUMENT_INPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_INPUT_STREAM)) +#define GEDIT_DOCUMENT_INPUT_STREAM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStreamClass)) + +typedef struct _GeditDocumentInputStream	GeditDocumentInputStream; +typedef struct _GeditDocumentInputStreamClass	GeditDocumentInputStreamClass; +typedef struct _GeditDocumentInputStreamPrivate	GeditDocumentInputStreamPrivate; + +struct _GeditDocumentInputStream +{ +	GInputStream parent; +	 +	GeditDocumentInputStreamPrivate *priv; +}; + +struct _GeditDocumentInputStreamClass +{ +	GInputStreamClass parent_class; +}; + +GType				 gedit_document_input_stream_get_type		(void) G_GNUC_CONST; + +GInputStream			*gedit_document_input_stream_new		(GtkTextBuffer           *buffer, +										 GeditDocumentNewlineType type); + +gsize				 gedit_document_input_stream_get_total_size	(GeditDocumentInputStream *stream); + +gsize				 gedit_document_input_stream_tell		(GeditDocumentInputStream *stream); + +G_END_DECLS + +#endif /* __GEDIT_DOCUMENT_INPUT_STREAM_H__ */ diff --git a/gedit/gedit-document-loader.c b/gedit/gedit-document-loader.c new file mode 100755 index 00000000..05368c8b --- /dev/null +++ b/gedit/gedit-document-loader.c @@ -0,0 +1,357 @@ +/* + * gedit-document-loader.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "gedit-document-loader.h" +#include "gedit-debug.h" +#include "gedit-metadata-manager.h" +#include "gedit-utils.h" +#include "gedit-marshal.h" +#include "gedit-enum-types.h" + +/* Those are for the the gedit_document_loader_new() factory */ +#include "gedit-gio-document-loader.h" + +G_DEFINE_ABSTRACT_TYPE(GeditDocumentLoader, gedit_document_loader, G_TYPE_OBJECT) + +/* Signals */ + +enum { +	LOADING, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* Properties */ + +enum +{ +	PROP_0, +	PROP_DOCUMENT, +	PROP_URI, +	PROP_ENCODING, +	PROP_NEWLINE_TYPE +}; + +static void +gedit_document_loader_set_property (GObject      *object, +				    guint         prop_id, +				    const GValue *value, +				    GParamSpec   *pspec) +{ +	GeditDocumentLoader *loader = GEDIT_DOCUMENT_LOADER (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			g_return_if_fail (loader->document == NULL); +			loader->document = g_value_get_object (value); +			break; +		case PROP_URI: +			g_return_if_fail (loader->uri == NULL); +			loader->uri = g_value_dup_string (value); +			break; +		case PROP_ENCODING: +			g_return_if_fail (loader->encoding == NULL); +			loader->encoding = g_value_get_boxed (value); +			break; +		case PROP_NEWLINE_TYPE: +			loader->auto_detected_newline_type = g_value_get_enum (value); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_loader_get_property (GObject    *object, +				    guint       prop_id, +				    GValue     *value, +				    GParamSpec *pspec) +{ +	GeditDocumentLoader *loader = GEDIT_DOCUMENT_LOADER (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			g_value_set_object (value, loader->document); +			break; +		case PROP_URI: +			g_value_set_string (value, loader->uri); +			break; +		case PROP_ENCODING: +			g_value_set_boxed (value, gedit_document_loader_get_encoding (loader)); +			break; +		case PROP_NEWLINE_TYPE: +			g_value_set_enum (value, loader->auto_detected_newline_type); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_loader_finalize (GObject *object) +{ +	GeditDocumentLoader *loader = GEDIT_DOCUMENT_LOADER (object); + +	g_free (loader->uri); + +	if (loader->info) +		g_object_unref (loader->info); + +	G_OBJECT_CLASS (gedit_document_loader_parent_class)->finalize (object); +} + +static void +gedit_document_loader_dispose (GObject *object) +{ +	GeditDocumentLoader *loader = GEDIT_DOCUMENT_LOADER (object); + +	if (loader->info != NULL) +	{ +		g_object_unref (loader->info); +		loader->info = NULL; +	} + +	G_OBJECT_CLASS (gedit_document_loader_parent_class)->dispose (object); +} + +static void +gedit_document_loader_class_init (GeditDocumentLoaderClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_document_loader_finalize; +	object_class->dispose = gedit_document_loader_dispose; +	object_class->get_property = gedit_document_loader_get_property; +	object_class->set_property = gedit_document_loader_set_property; + +	g_object_class_install_property (object_class, +					 PROP_DOCUMENT, +					 g_param_spec_object ("document", +							      "Document", +							      "The GeditDocument this GeditDocumentLoader is associated with", +							      GEDIT_TYPE_DOCUMENT, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_URI, +					 g_param_spec_string ("uri", +							      "URI", +							      "The URI this GeditDocumentLoader loads the document from", +							      "", +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	g_object_class_install_property (object_class, +					 PROP_ENCODING, +					 g_param_spec_boxed ("encoding", +							     "Encoding", +							     "The encoding of the saved file", +							     GEDIT_TYPE_ENCODING, +							     G_PARAM_READWRITE | +							     G_PARAM_CONSTRUCT_ONLY | +							     G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_NEWLINE_TYPE, +	                                 g_param_spec_enum ("newline-type", +	                                                    "Newline type", +	                                                    "The accepted types of line ending", +	                                                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE, +	                                                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF, +	                                                    G_PARAM_READWRITE | +	                                                    G_PARAM_STATIC_NAME | +	                                                    G_PARAM_STATIC_BLURB)); + +	signals[LOADING] = +		g_signal_new ("loading", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentLoaderClass, loading), +			      NULL, NULL, +			      gedit_marshal_VOID__BOOLEAN_POINTER, +			      G_TYPE_NONE, +			      2, +			      G_TYPE_BOOLEAN, +			      G_TYPE_POINTER); +} + +static void +gedit_document_loader_init (GeditDocumentLoader *loader) +{ +	loader->used = FALSE; +	loader->auto_detected_newline_type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT; +} + +void +gedit_document_loader_loading (GeditDocumentLoader *loader, +			       gboolean             completed, +			       GError              *error) +{ +	/* the object will be unrefed in the callback of the loading signal +	 * (when completed == TRUE), so we need to prevent finalization. +	 */ +	if (completed) +	{ +		g_object_ref (loader); +	} + +	g_signal_emit (loader, signals[LOADING], 0, completed, error); + +	if (completed) +	{ +		if (error == NULL) +			gedit_debug_message (DEBUG_LOADER, "load completed"); +		else +			gedit_debug_message (DEBUG_LOADER, "load failed"); + +		g_object_unref (loader); +	} +} + +/* This is a factory method that returns an appopriate loader + * for the given uri. + */ +GeditDocumentLoader * +gedit_document_loader_new (GeditDocument       *doc, +			   const gchar         *uri, +			   const GeditEncoding *encoding) +{ +	GeditDocumentLoader *loader; +	GType loader_type; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	/* At the moment we just use gio loader in all cases... +	 * In the future it would be great to have a PolicyKit +	 * loader to get permission to save systen files etc */ +	loader_type = GEDIT_TYPE_GIO_DOCUMENT_LOADER; + +	loader = GEDIT_DOCUMENT_LOADER (g_object_new (loader_type, +						      "document", doc, +						      "uri", uri, +						      "encoding", encoding, +						      NULL)); + +	return loader; +} + +/* If enconding == NULL, the encoding will be autodetected */ +void +gedit_document_loader_load (GeditDocumentLoader *loader) +{ +	gedit_debug (DEBUG_LOADER); + +	g_return_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader)); + +	/* the loader can be used just once, then it must be thrown away */ +	g_return_if_fail (loader->used == FALSE); +	loader->used = TRUE; + +	GEDIT_DOCUMENT_LOADER_GET_CLASS (loader)->load (loader); +} + +gboolean  +gedit_document_loader_cancel (GeditDocumentLoader *loader) +{ +	gedit_debug (DEBUG_LOADER); + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), FALSE); + +	return GEDIT_DOCUMENT_LOADER_GET_CLASS (loader)->cancel (loader); +} + +GeditDocument * +gedit_document_loader_get_document (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), NULL); + +	return loader->document; +} + +/* Returns STDIN_URI if loading from stdin */ +const gchar * +gedit_document_loader_get_uri (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), NULL); + +	return loader->uri; +} + +goffset +gedit_document_loader_get_bytes_read (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), 0); + +	return GEDIT_DOCUMENT_LOADER_GET_CLASS (loader)->get_bytes_read (loader); +} + +const GeditEncoding * +gedit_document_loader_get_encoding (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), NULL); + +	if (loader->encoding != NULL) +		return loader->encoding; + +	g_return_val_if_fail (loader->auto_detected_encoding != NULL,  +			      gedit_encoding_get_current ()); + +	return loader->auto_detected_encoding; +} + +GeditDocumentNewlineType +gedit_document_loader_get_newline_type (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), +			      GEDIT_DOCUMENT_NEWLINE_TYPE_LF); + +	return loader->auto_detected_newline_type; +} + +GFileInfo * +gedit_document_loader_get_info (GeditDocumentLoader *loader) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader), NULL); + +	return loader->info; +} diff --git a/gedit/gedit-document-loader.h b/gedit/gedit-document-loader.h new file mode 100755 index 00000000..a627fba0 --- /dev/null +++ b/gedit/gedit-document-loader.h @@ -0,0 +1,130 @@ +/* + * gedit-document-loader.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_DOCUMENT_LOADER_H__ +#define __GEDIT_DOCUMENT_LOADER_H__ + +#include <gedit/gedit-document.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_DOCUMENT_LOADER              (gedit_document_loader_get_type()) +#define GEDIT_DOCUMENT_LOADER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_DOCUMENT_LOADER, GeditDocumentLoader)) +#define GEDIT_DOCUMENT_LOADER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_DOCUMENT_LOADER, GeditDocumentLoaderClass)) +#define GEDIT_IS_DOCUMENT_LOADER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_DOCUMENT_LOADER)) +#define GEDIT_IS_DOCUMENT_LOADER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_LOADER)) +#define GEDIT_DOCUMENT_LOADER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_DOCUMENT_LOADER, GeditDocumentLoaderClass)) + +/* Private structure type */ +typedef struct _GeditDocumentLoaderPrivate GeditDocumentLoaderPrivate; + +/* + * Main object structure + */ +typedef struct _GeditDocumentLoader GeditDocumentLoader; + +struct _GeditDocumentLoader  +{ +	GObject object; + +	GeditDocument		 *document; +	gboolean		  used; + +	/* Info on the current file */ +	GFileInfo		 *info; +	gchar			 *uri; +	const GeditEncoding	 *encoding; +	const GeditEncoding	 *auto_detected_encoding; +	GeditDocumentNewlineType  auto_detected_newline_type; +}; + +/* + * Class definition + */ +typedef struct _GeditDocumentLoaderClass GeditDocumentLoaderClass; + +struct _GeditDocumentLoaderClass  +{ +	GObjectClass parent_class; + +	/* Signals */ +	void (* loading) (GeditDocumentLoader *loader, +			  gboolean             completed, +			  const GError        *error); + +	/* VTable */ +	void			(* load)		(GeditDocumentLoader *loader); +	gboolean		(* cancel)		(GeditDocumentLoader *loader); +	goffset			(* get_bytes_read)	(GeditDocumentLoader *loader); +}; + +/* + * Public methods + */ +GType 		 	 gedit_document_loader_get_type		(void) G_GNUC_CONST; + +/* If enconding == NULL, the encoding will be autodetected */ +GeditDocumentLoader 	*gedit_document_loader_new 		(GeditDocument       *doc, +								 const gchar         *uri, +								 const GeditEncoding *encoding); + +void			 gedit_document_loader_loading		(GeditDocumentLoader *loader, +								 gboolean             completed, +								 GError              *error); + +void			 gedit_document_loader_load		(GeditDocumentLoader *loader); +#if 0 +gboolean		 gedit_document_loader_load_from_stdin	(GeditDocumentLoader *loader); +#endif		  +gboolean		 gedit_document_loader_cancel		(GeditDocumentLoader *loader); + +GeditDocument		*gedit_document_loader_get_document	(GeditDocumentLoader *loader); + +/* Returns STDIN_URI if loading from stdin */ +#define STDIN_URI "stdin:"  +const gchar		*gedit_document_loader_get_uri		(GeditDocumentLoader *loader); + +const GeditEncoding	*gedit_document_loader_get_encoding	(GeditDocumentLoader *loader); + +GeditDocumentNewlineType gedit_document_loader_get_newline_type (GeditDocumentLoader *loader); + +goffset			 gedit_document_loader_get_bytes_read	(GeditDocumentLoader *loader); + +/* You can get from the info: content_type, time_modified, standard_size, access_can_write  +   and also the metadata*/ +GFileInfo		*gedit_document_loader_get_info		(GeditDocumentLoader *loader); + +G_END_DECLS + +#endif  /* __GEDIT_DOCUMENT_LOADER_H__  */ diff --git a/gedit/gedit-document-output-stream.c b/gedit/gedit-document-output-stream.c new file mode 100755 index 00000000..e1ef5af5 --- /dev/null +++ b/gedit/gedit-document-output-stream.c @@ -0,0 +1,391 @@ +/* + * gedit-document-output-stream.c + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + +#include "config.h" + +#include <string.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include "gedit-document-output-stream.h" + +/* NOTE: never use async methods on this stream, the stream is just + * a wrapper around GtkTextBuffer api so that we can use GIO Stream + * methods, but the undelying code operates on a GtkTextBuffer, so + * there is no I/O involved and should be accessed only by the main + * thread */ + +#define GEDIT_DOCUMENT_OUTPUT_STREAM_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object),\ +							 GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM,\ +							 GeditDocumentOutputStreamPrivate)) + +#define MAX_UNICHAR_LEN 6 + +struct _GeditDocumentOutputStreamPrivate +{ +	GeditDocument *doc; +	GtkTextIter    pos; + +	gchar *buffer; +	gsize buflen; + +	guint is_initialized : 1; +	guint is_closed : 1; +}; + +enum +{ +	PROP_0, +	PROP_DOCUMENT +}; + +G_DEFINE_TYPE (GeditDocumentOutputStream, gedit_document_output_stream, G_TYPE_OUTPUT_STREAM) + +static gssize	gedit_document_output_stream_write (GOutputStream            *stream, +						    const void               *buffer, +						    gsize                     count, +						    GCancellable             *cancellable, +						    GError                  **error); + +static gboolean	gedit_document_output_stream_close (GOutputStream     *stream, +						    GCancellable      *cancellable, +						    GError           **error); + +static void +gedit_document_output_stream_set_property (GObject      *object, +					   guint         prop_id, +					   const GValue *value, +					   GParamSpec   *pspec) +{ +	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			stream->priv->doc = GEDIT_DOCUMENT (g_value_get_object (value)); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_output_stream_get_property (GObject    *object, +					   guint       prop_id, +					   GValue     *value, +					   GParamSpec *pspec) +{ +	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			g_value_set_object (value, stream->priv->doc); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_output_stream_finalize (GObject *object) +{ +	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object); + +	g_free (stream->priv->buffer); + +	G_OBJECT_CLASS (gedit_document_output_stream_parent_class)->finalize (object); +} + +static void +gedit_document_output_stream_constructed (GObject *object) +{ +	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object); + +	if (!stream->priv->doc) +	{ +		g_critical ("This should never happen, a problem happened constructing the Document Output Stream!"); +		return; +	} + +	/* Init the undoable action */ +	gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (stream->priv->doc)); +	/* clear the buffer */ +	gtk_text_buffer_set_text (GTK_TEXT_BUFFER (stream->priv->doc), +				  "", 0); +	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->doc), +				      FALSE); + +	gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (stream->priv->doc)); +} + +static void +gedit_document_output_stream_class_init (GeditDocumentOutputStreamClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass); + +	object_class->get_property = gedit_document_output_stream_get_property; +	object_class->set_property = gedit_document_output_stream_set_property; +	object_class->finalize = gedit_document_output_stream_finalize; +	object_class->constructed = gedit_document_output_stream_constructed; + +	stream_class->write_fn = gedit_document_output_stream_write; +	stream_class->close_fn = gedit_document_output_stream_close; + +	g_object_class_install_property (object_class, +					 PROP_DOCUMENT, +					 g_param_spec_object ("document", +							      "Document", +							      "The document which is written", +							      GEDIT_TYPE_DOCUMENT, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	g_type_class_add_private (object_class, sizeof (GeditDocumentOutputStreamPrivate)); +} + +static void +gedit_document_output_stream_init (GeditDocumentOutputStream *stream) +{ +	stream->priv = GEDIT_DOCUMENT_OUTPUT_STREAM_GET_PRIVATE (stream); + +	stream->priv->buffer = NULL; +	stream->priv->buflen = 0; + +	stream->priv->is_initialized = FALSE; +	stream->priv->is_closed = FALSE; +} + +static GeditDocumentNewlineType +get_newline_type (GtkTextIter *end) +{ +	GeditDocumentNewlineType res; +	GtkTextIter copy; +	gunichar c; + +	copy = *end; +	c = gtk_text_iter_get_char (©); + +	if (g_unichar_break_type (c) == G_UNICODE_BREAK_CARRIAGE_RETURN) +	{ +		if (gtk_text_iter_forward_char (©) && +		    g_unichar_break_type (gtk_text_iter_get_char (©)) == G_UNICODE_BREAK_LINE_FEED) +		{ +			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF; +		} +		else +		{ +			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR; +		} +	} +	else +	{ +		res = GEDIT_DOCUMENT_NEWLINE_TYPE_LF; +	} + +	return res; +} + +GOutputStream * +gedit_document_output_stream_new (GeditDocument *doc) +{ +	return G_OUTPUT_STREAM (g_object_new (GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, +					      "document", doc, NULL)); +} + +GeditDocumentNewlineType +gedit_document_output_stream_detect_newline_type (GeditDocumentOutputStream *stream) +{ +	GeditDocumentNewlineType type; +	GtkTextIter iter; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_OUTPUT_STREAM (stream), +			      GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT); + +	type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT; + +	gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (stream->priv->doc), +					&iter); + +	if (gtk_text_iter_ends_line (&iter) || gtk_text_iter_forward_to_line_end (&iter)) +	{ +		type = get_newline_type (&iter); +	} + +	return type; +} + +/* If the last char is a newline, remove it from the buffer (otherwise +   GtkTextView shows it as an empty line). See bug #324942. */ +static void +remove_ending_newline (GeditDocumentOutputStream *stream) +{ +	GtkTextIter end; +	GtkTextIter start; + +	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (stream->priv->doc), &end); +	start = end; + +	gtk_text_iter_set_line_offset (&start, 0); + +	if (gtk_text_iter_ends_line (&start) && +	    gtk_text_iter_backward_line (&start)) +	{ +		if (!gtk_text_iter_ends_line (&start)) +		{ +			gtk_text_iter_forward_to_line_end (&start); +		} + +		/* Delete the empty line which is from 'start' to 'end' */ +		gtk_text_buffer_delete (GTK_TEXT_BUFFER (stream->priv->doc), +		                        &start, +		                        &end); +	} +} + +static void +end_append_text_to_document (GeditDocumentOutputStream *stream) +{ +	remove_ending_newline (stream); + +	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->doc), +				      FALSE); + +	gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (stream->priv->doc)); +} + +static gssize +gedit_document_output_stream_write (GOutputStream            *stream, +				    const void               *buffer, +				    gsize                     count, +				    GCancellable             *cancellable, +				    GError                  **error) +{ +	GeditDocumentOutputStream *ostream; +	gchar *text; +	gsize len; +	gboolean freetext = FALSE; +	const gchar *end; +	gsize nvalid; +	gboolean valid; + +	if (g_cancellable_set_error_if_cancelled (cancellable, error)) +		return -1; + +	ostream = GEDIT_DOCUMENT_OUTPUT_STREAM (stream); + +	if (!ostream->priv->is_initialized) +	{ +		/* Init the undoable action */ +		gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (ostream->priv->doc)); + +		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (ostream->priv->doc), +						&ostream->priv->pos); +		ostream->priv->is_initialized = TRUE; +	} + +	if (ostream->priv->buflen > 0) +	{ +		len = ostream->priv->buflen + count; +		text = g_new (gchar , len + 1); +		memcpy (text, ostream->priv->buffer, ostream->priv->buflen); +		memcpy (text + ostream->priv->buflen, buffer, count); +		text[len] = '\0'; +		g_free (ostream->priv->buffer); +		ostream->priv->buffer = NULL; +		ostream->priv->buflen = 0; +		freetext = TRUE; +	} +	else +	{ +		text = (gchar *) buffer; +		len = count; +	} + +	/* validate */ +	valid = g_utf8_validate (text, len, &end); +	nvalid = end - text; + +	if (!valid) +	{ +		gsize remainder; + +		remainder = len - nvalid; + +		if ((remainder < MAX_UNICHAR_LEN) && +		    (g_utf8_get_char_validated (text + nvalid, remainder) == (gunichar)-2)) +		{ +			ostream->priv->buffer = g_strndup (end, remainder); +			ostream->priv->buflen = remainder; +			len -= remainder; +		} +		else +		{ +			/* TODO: we cuould escape invalid text and tag it in red +			 * and make the doc readonly. +			 */ +			g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, +				     _("Invalid UTF-8 sequence in input")); + +			if (freetext) +				g_free (text); + +			return -1; +		} +	} + +	gtk_text_buffer_insert (GTK_TEXT_BUFFER (ostream->priv->doc), +				&ostream->priv->pos, text, len); + +	if (freetext) +		g_free (text); + +	return count; +} + +static gboolean +gedit_document_output_stream_close (GOutputStream     *stream, +				    GCancellable      *cancellable, +				    GError           **error) +{ +	GeditDocumentOutputStream *ostream = GEDIT_DOCUMENT_OUTPUT_STREAM (stream); + +	if (!ostream->priv->is_closed && ostream->priv->is_initialized) +	{ +		end_append_text_to_document (ostream); +		ostream->priv->is_closed = TRUE; +	} + +	if (ostream->priv->buflen > 0) +	{ +		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, +			     _("Incomplete UTF-8 sequence in input")); +		return FALSE; +	} + +	return TRUE; +} diff --git a/gedit/gedit-document-output-stream.h b/gedit/gedit-document-output-stream.h new file mode 100755 index 00000000..a3f99fce --- /dev/null +++ b/gedit/gedit-document-output-stream.h @@ -0,0 +1,64 @@ +/* + * gedit-document-output-stream.h + * This file is part of gedit + * + * Copyright (C) 2010 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + + +#ifndef __GEDIT_DOCUMENT_OUTPUT_STREAM_H__ +#define __GEDIT_DOCUMENT_OUTPUT_STREAM_H__ + +#include <gio/gio.h> +#include "gedit-document.h" + +G_BEGIN_DECLS + +#define GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM		(gedit_document_output_stream_get_type ()) +#define GEDIT_DOCUMENT_OUTPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStream)) +#define GEDIT_DOCUMENT_OUTPUT_STREAM_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStream const)) +#define GEDIT_DOCUMENT_OUTPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStreamClass)) +#define GEDIT_IS_DOCUMENT_OUTPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM)) +#define GEDIT_IS_DOCUMENT_OUTPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM)) +#define GEDIT_DOCUMENT_OUTPUT_STREAM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStreamClass)) + +typedef struct _GeditDocumentOutputStream		GeditDocumentOutputStream; +typedef struct _GeditDocumentOutputStreamClass		GeditDocumentOutputStreamClass; +typedef struct _GeditDocumentOutputStreamPrivate	GeditDocumentOutputStreamPrivate; + +struct _GeditDocumentOutputStream +{ +	GOutputStream parent; + +	GeditDocumentOutputStreamPrivate *priv; +}; + +struct _GeditDocumentOutputStreamClass +{ +	GOutputStreamClass parent_class; +}; + +GType			 gedit_document_output_stream_get_type		(void) G_GNUC_CONST; + +GOutputStream		*gedit_document_output_stream_new		(GeditDocument *doc); + +GeditDocumentNewlineType gedit_document_output_stream_detect_newline_type (GeditDocumentOutputStream *stream); + +G_END_DECLS + +#endif /* __GEDIT_DOCUMENT_OUTPUT_STREAM_H__ */ diff --git a/gedit/gedit-document-saver.c b/gedit/gedit-document-saver.c new file mode 100755 index 00000000..1a064da6 --- /dev/null +++ b/gedit/gedit-document-saver.c @@ -0,0 +1,359 @@ +/* + * gedit-document-saver.c + * This file is part of gedit + * + * Copyright (C) 2005-2006 - Paolo Borelli and Paolo Maggi + * Copyright (C) 2007 - Paolo Borelli, Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005-2006. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <glib/gi18n.h> + +#include "gedit-document-saver.h" +#include "gedit-debug.h" +#include "gedit-prefs-manager.h" +#include "gedit-marshal.h" +#include "gedit-utils.h" +#include "gedit-enum-types.h" +#include "gedit-gio-document-saver.h" + +G_DEFINE_ABSTRACT_TYPE(GeditDocumentSaver, gedit_document_saver, G_TYPE_OBJECT) + +/* Signals */ + +enum { +	SAVING, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* Properties */ + +enum { +	PROP_0, +	PROP_DOCUMENT, +	PROP_URI, +	PROP_ENCODING, +	PROP_NEWLINE_TYPE, +	PROP_FLAGS +}; + +static void +gedit_document_saver_set_property (GObject      *object, +				   guint         prop_id, +				   const GValue *value, +				   GParamSpec   *pspec) +{ +	GeditDocumentSaver *saver = GEDIT_DOCUMENT_SAVER (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			g_return_if_fail (saver->document == NULL); +			saver->document = g_value_get_object (value); +			break; +		case PROP_URI: +			g_return_if_fail (saver->uri == NULL); +			saver->uri = g_value_dup_string (value); +			break; +		case PROP_ENCODING: +			g_return_if_fail (saver->encoding == NULL); +			saver->encoding = g_value_get_boxed (value); +			break; +		case PROP_NEWLINE_TYPE: +			saver->newline_type = g_value_get_enum (value); +			break; +		case PROP_FLAGS: +			saver->flags = g_value_get_flags (value); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_saver_get_property (GObject    *object, +				   guint       prop_id, +				   GValue     *value, +				   GParamSpec *pspec) +{ +	GeditDocumentSaver *saver = GEDIT_DOCUMENT_SAVER (object); + +	switch (prop_id) +	{ +		case PROP_DOCUMENT: +			g_value_set_object (value, saver->document); +			break; +		case PROP_URI: +			g_value_set_string (value, saver->uri); +			break; +		case PROP_ENCODING: +			g_value_set_boxed (value, saver->encoding); +			break; +		case PROP_NEWLINE_TYPE: +			g_value_set_enum (value, saver->newline_type); +			break; +		case PROP_FLAGS: +			g_value_set_flags (value, saver->flags); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_saver_finalize (GObject *object) +{ +	GeditDocumentSaver *saver = GEDIT_DOCUMENT_SAVER (object); + +	g_free (saver->uri); + +	G_OBJECT_CLASS (gedit_document_saver_parent_class)->finalize (object); +} + +static void +gedit_document_saver_dispose (GObject *object) +{ +	GeditDocumentSaver *saver = GEDIT_DOCUMENT_SAVER (object); + +	if (saver->info != NULL) +	{ +		g_object_unref (saver->info); +		saver->info = NULL; +	} + +	G_OBJECT_CLASS (gedit_document_saver_parent_class)->dispose (object); +} + +static void  +gedit_document_saver_class_init (GeditDocumentSaverClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_document_saver_finalize; +	object_class->dispose = gedit_document_saver_dispose; +	object_class->set_property = gedit_document_saver_set_property; +	object_class->get_property = gedit_document_saver_get_property; + +	g_object_class_install_property (object_class, +					 PROP_DOCUMENT, +					 g_param_spec_object ("document", +							      "Document", +							      "The GeditDocument this GeditDocumentSaver is associated with", +							      GEDIT_TYPE_DOCUMENT, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_URI, +					 g_param_spec_string ("uri", +							      "URI", +							      "The URI this GeditDocumentSaver saves the document to", +							      "", +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_ENCODING, +					 g_param_spec_boxed ("encoding", +							     "URI", +							     "The encoding of the saved file", +							     GEDIT_TYPE_ENCODING, +							     G_PARAM_READWRITE | +							     G_PARAM_CONSTRUCT_ONLY | +							     G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_NEWLINE_TYPE, +					 g_param_spec_enum ("newline-type", +					                    "Newline type", +					                    "The accepted types of line ending", +					                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE, +					                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF, +					                    G_PARAM_READWRITE | +					                    G_PARAM_STATIC_NAME | +					                    G_PARAM_STATIC_BLURB | +					                    G_PARAM_CONSTRUCT_ONLY)); + +	g_object_class_install_property (object_class, +					 PROP_FLAGS, +					 g_param_spec_flags ("flags", +							     "Flags", +							     "The flags for the saving operation", +							     GEDIT_TYPE_DOCUMENT_SAVE_FLAGS, +							     0, +							     G_PARAM_READWRITE | +							     G_PARAM_CONSTRUCT_ONLY)); + +	signals[SAVING] = +		g_signal_new ("saving", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentSaverClass, saving), +			      NULL, NULL, +			      gedit_marshal_VOID__BOOLEAN_POINTER, +			      G_TYPE_NONE, +			      2, +			      G_TYPE_BOOLEAN, +			      G_TYPE_POINTER); +} + +static void +gedit_document_saver_init (GeditDocumentSaver *saver) +{ +	saver->used = FALSE; +} + +GeditDocumentSaver * +gedit_document_saver_new (GeditDocument           *doc, +			  const gchar             *uri, +			  const GeditEncoding     *encoding, +			  GeditDocumentNewlineType newline_type, +			  GeditDocumentSaveFlags   flags) +{ +	GeditDocumentSaver *saver; +	GType saver_type; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	saver_type = GEDIT_TYPE_GIO_DOCUMENT_SAVER; + +	if (encoding == NULL) +		encoding = gedit_encoding_get_utf8 (); + +	saver = GEDIT_DOCUMENT_SAVER (g_object_new (saver_type, +						    "document", doc, +						    "uri", uri, +						    "encoding", encoding, +						    "newline_type", newline_type, +						    "flags", flags, +						    NULL)); + +	return saver; +} + +void +gedit_document_saver_saving (GeditDocumentSaver *saver, +			     gboolean            completed, +			     GError             *error) +{ +	/* the object will be unrefed in the callback of the saving +	 * signal, so we need to prevent finalization. +	 */ +	if (completed) +	{ +		g_object_ref (saver); +	} + +	g_signal_emit (saver, signals[SAVING], 0, completed, error); + +	if (completed) +	{ +		if (error == NULL) +			gedit_debug_message (DEBUG_SAVER, "save completed"); +		else +			gedit_debug_message (DEBUG_SAVER, "save failed"); + +		g_object_unref (saver); +	} +} + +void +gedit_document_saver_save (GeditDocumentSaver     *saver, +			   GTimeVal               *old_mtime) +{ +	gedit_debug (DEBUG_SAVER); + +	g_return_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver)); +	g_return_if_fail (saver->uri != NULL && strlen (saver->uri) > 0); + +	g_return_if_fail (saver->used == FALSE); +	saver->used = TRUE; + +	// CHECK: +	// - sanity check a max len for the uri? +	// report async (in an idle handler) or sync (bool ret) +	// async is extra work here, sync is special casing in the caller + +	/* never keep backup of autosaves */ +	if ((saver->flags & GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP) != 0) +		saver->keep_backup = FALSE; +	else +		saver->keep_backup = gedit_prefs_manager_get_create_backup_copy (); + +	GEDIT_DOCUMENT_SAVER_GET_CLASS (saver)->save (saver, old_mtime); +} + +GeditDocument * +gedit_document_saver_get_document (GeditDocumentSaver *saver) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver), NULL); + +	return saver->document; +} + +const gchar * +gedit_document_saver_get_uri (GeditDocumentSaver *saver) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver), NULL); + +	return saver->uri; +} + +/* Returns 0 if file size is unknown */ +goffset +gedit_document_saver_get_file_size (GeditDocumentSaver *saver) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver), 0); + +	return GEDIT_DOCUMENT_SAVER_GET_CLASS (saver)->get_file_size (saver); +} + +goffset +gedit_document_saver_get_bytes_written (GeditDocumentSaver *saver) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver), 0); + +	return GEDIT_DOCUMENT_SAVER_GET_CLASS (saver)->get_bytes_written (saver); +} + +GFileInfo * +gedit_document_saver_get_info (GeditDocumentSaver *saver) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT_SAVER (saver), NULL); + +	return saver->info; +} diff --git a/gedit/gedit-document-saver.h b/gedit/gedit-document-saver.h new file mode 100755 index 00000000..ccc0b5c7 --- /dev/null +++ b/gedit/gedit-document-saver.h @@ -0,0 +1,133 @@ +/* + * gedit-document-saver.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyrhing (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_DOCUMENT_SAVER_H__ +#define __GEDIT_DOCUMENT_SAVER_H__ + +#include <gedit/gedit-document.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_DOCUMENT_SAVER              (gedit_document_saver_get_type()) +#define GEDIT_DOCUMENT_SAVER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_DOCUMENT_SAVER, GeditDocumentSaver)) +#define GEDIT_DOCUMENT_SAVER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_DOCUMENT_SAVER, GeditDocumentSaverClass)) +#define GEDIT_IS_DOCUMENT_SAVER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_DOCUMENT_SAVER)) +#define GEDIT_IS_DOCUMENT_SAVER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_SAVER)) +#define GEDIT_DOCUMENT_SAVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_DOCUMENT_SAVER, GeditDocumentSaverClass)) + +/* + * Main object structure + */ +typedef struct _GeditDocumentSaver GeditDocumentSaver; + +struct _GeditDocumentSaver  +{ +	GObject object; + +	/*< private >*/ +	GFileInfo		 *info; +	GeditDocument		 *document; +	gboolean		  used; + +	gchar			 *uri; +	const GeditEncoding      *encoding; +	GeditDocumentNewlineType  newline_type; + +	GeditDocumentSaveFlags    flags; + +	gboolean		  keep_backup; +}; + +/* + * Class definition + */ +typedef struct _GeditDocumentSaverClass GeditDocumentSaverClass; + +struct _GeditDocumentSaverClass  +{ +	GObjectClass parent_class; + +	/* Signals */ +	void (* saving) (GeditDocumentSaver *saver, +			 gboolean             completed, +			 const GError        *error); + +	/* VTable */ +	void			(* save)		(GeditDocumentSaver *saver, +							 GTimeVal           *old_mtime); +	goffset			(* get_file_size)	(GeditDocumentSaver *saver); +	goffset			(* get_bytes_written)	(GeditDocumentSaver *saver); +}; + +/* + * Public methods + */ +GType 		 	 gedit_document_saver_get_type		(void) G_GNUC_CONST; + +/* If enconding == NULL, the encoding will be autodetected */ +GeditDocumentSaver 	*gedit_document_saver_new 		(GeditDocument           *doc, +								 const gchar             *uri, +								 const GeditEncoding     *encoding, +								 GeditDocumentNewlineType newline_type, +								 GeditDocumentSaveFlags   flags); + +void			 gedit_document_saver_saving		(GeditDocumentSaver *saver, +								 gboolean            completed, +								 GError             *error); +void			 gedit_document_saver_save		(GeditDocumentSaver  *saver, +								 GTimeVal            *old_mtime); + +#if 0 +void			 gedit_document_saver_cancel		(GeditDocumentSaver  *saver); +#endif + +GeditDocument		*gedit_document_saver_get_document	(GeditDocumentSaver  *saver); + +const gchar		*gedit_document_saver_get_uri		(GeditDocumentSaver  *saver); + +/* If backup_uri is NULL no backup will be made */ +const gchar		*gedit_document_saver_get_backup_uri	(GeditDocumentSaver  *saver); +void			*gedit_document_saver_set_backup_uri	(GeditDocumentSaver  *saver, +							 	 const gchar         *backup_uri); + +/* Returns 0 if file size is unknown */ +goffset			 gedit_document_saver_get_file_size	(GeditDocumentSaver  *saver); + +goffset			 gedit_document_saver_get_bytes_written	(GeditDocumentSaver  *saver); + +GFileInfo		*gedit_document_saver_get_info		(GeditDocumentSaver  *saver); + +G_END_DECLS + +#endif  /* __GEDIT_DOCUMENT_SAVER_H__  */ diff --git a/gedit/gedit-document.c b/gedit/gedit-document.c new file mode 100755 index 00000000..d9612263 --- /dev/null +++ b/gedit/gedit-document.c @@ -0,0 +1,2732 @@ +/* + * gedit-document.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi  + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#include <glib/gi18n.h> +#include <gtksourceview/gtksourceiter.h> + +#include "gedit-prefs-manager-app.h" +#include "gedit-document.h" +#include "gedit-debug.h" +#include "gedit-utils.h" +#include "gedit-language-manager.h" +#include "gedit-style-scheme-manager.h" +#include "gedit-document-loader.h" +#include "gedit-document-saver.h" +#include "gedit-marshal.h" +#include "gedit-enum-types.h" +#include "gedittextregion.h" + +#ifndef ENABLE_GVFS_METADATA +#include "gedit-metadata-manager.h" +#else +#define METADATA_QUERY "metadata::*" +#endif + +#undef ENABLE_PROFILE  + +#ifdef ENABLE_PROFILE +#define PROFILE(x) x +#else +#define PROFILE(x) +#endif + +PROFILE (static GTimer *timer = NULL) + +#ifdef MAXPATHLEN +#define GEDIT_MAX_PATH_LEN  MAXPATHLEN +#elif defined (PATH_MAX) +#define GEDIT_MAX_PATH_LEN  PATH_MAX +#else +#define GEDIT_MAX_PATH_LEN  2048 +#endif + +#define GEDIT_DOCUMENT_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_DOCUMENT, GeditDocumentPrivate)) + +static void	gedit_document_load_real	(GeditDocument          *doc, +						 const gchar            *uri, +						 const GeditEncoding    *encoding, +						 gint                    line_pos, +						 gboolean                create); +static void	gedit_document_save_real	(GeditDocument          *doc, +						 const gchar            *uri, +						 const GeditEncoding    *encoding, +						 GeditDocumentSaveFlags  flags); +static void	to_search_region_range 		(GeditDocument *doc, +						 GtkTextIter   *start,  +						 GtkTextIter   *end); +static void 	insert_text_cb		 	(GeditDocument *doc,  +						 GtkTextIter   *pos, +						 const gchar   *text,  +						 gint           length); +						  +static void	delete_range_cb 		(GeditDocument *doc,  +						 GtkTextIter   *start, +						 GtkTextIter   *end); +			      +struct _GeditDocumentPrivate +{ +	gchar	    *uri; +	gint 	     untitled_number; +	gchar       *short_name; + +	GFileInfo   *metadata_info; + +	const GeditEncoding *encoding; + +	gchar	    *content_type; + +	GTimeVal     mtime; +	GTimeVal     time_of_last_save_or_load; + +	guint        search_flags; +	gchar       *search_text; +	gint	     num_of_lines_search_text; + +	GeditDocumentNewlineType newline_type; + +	/* Temp data while loading */ +	GeditDocumentLoader *loader; +	gboolean             create; /* Create file if uri points  +	                              * to a non existing file */ +	const GeditEncoding *requested_encoding; +	gint                 requested_line_pos; + +	/* Saving stuff */ +	GeditDocumentSaver *saver; + +	/* Search highlighting support variables */ +	GeditTextRegion *to_search_region; +	GtkTextTag      *found_tag; + +	/* Mount operation factory */ +	GeditMountOperationFactory  mount_operation_factory; +	gpointer		    mount_operation_userdata; + +	gint readonly : 1; +	gint last_save_was_manually : 1;  +	gint language_set_by_user : 1; +	gint stop_cursor_moved_emission : 1; +	gint dispose_has_run : 1; +}; + +enum { +	PROP_0, + +	PROP_URI, +	PROP_SHORTNAME, +	PROP_CONTENT_TYPE, +	PROP_MIME_TYPE, +	PROP_READ_ONLY, +	PROP_ENCODING, +	PROP_CAN_SEARCH_AGAIN, +	PROP_ENABLE_SEARCH_HIGHLIGHTING, +	PROP_NEWLINE_TYPE +}; + +enum { +	CURSOR_MOVED, +	LOAD, +	LOADING, +	LOADED, +	SAVE, +	SAVING, +	SAVED, +	SEARCH_HIGHLIGHT_UPDATED, +	LAST_SIGNAL +}; + +static guint document_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(GeditDocument, gedit_document, GTK_TYPE_SOURCE_BUFFER) + +GQuark +gedit_document_error_quark (void) +{ +	static GQuark quark = 0; + +	if (G_UNLIKELY (quark == 0)) +		quark = g_quark_from_static_string ("gedit_io_load_error"); + +	return quark; +} + +static GHashTable *allocated_untitled_numbers = NULL; + +static gint +get_untitled_number (void) +{ +	gint i = 1; + +	if (allocated_untitled_numbers == NULL) +		allocated_untitled_numbers = g_hash_table_new (NULL, NULL); + +	g_return_val_if_fail (allocated_untitled_numbers != NULL, -1); + +	while (TRUE) +	{ +		if (g_hash_table_lookup (allocated_untitled_numbers, GINT_TO_POINTER (i)) == NULL) +		{ +			g_hash_table_insert (allocated_untitled_numbers,  +					     GINT_TO_POINTER (i), +					     GINT_TO_POINTER (i)); + +			return i; +		} + +		++i; +	} +} + +static void +release_untitled_number (gint n) +{ +	g_return_if_fail (allocated_untitled_numbers != NULL); + +	g_hash_table_remove (allocated_untitled_numbers, GINT_TO_POINTER (n)); +} + +static void +gedit_document_dispose (GObject *object) +{ +	GeditDocument *doc = GEDIT_DOCUMENT (object);  + +	gedit_debug (DEBUG_DOCUMENT); + +	/* Metadata must be saved here and not in finalize +	 * because the language is gone by the time finalize runs. +	 * beside if some plugin prevents proper finalization by +	 * holding a ref to the doc, we still save the metadata */ +	if ((!doc->priv->dispose_has_run) && (doc->priv->uri != NULL)) +	{ +		GtkTextIter iter; +		gchar *position; +		const gchar *language = NULL; + +		if (doc->priv->language_set_by_user) +		{ +			GtkSourceLanguage *lang; + +			lang = gedit_document_get_language (doc); + +			if (lang == NULL) +				language = "_NORMAL_"; +			else +				language = gtk_source_language_get_id (lang); +		} + +		gtk_text_buffer_get_iter_at_mark ( +				GTK_TEXT_BUFFER (doc), +				&iter, +				gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (doc))); + +		position = g_strdup_printf ("%d",  +					    gtk_text_iter_get_offset (&iter)); + +		if (language == NULL) +			gedit_document_set_metadata (doc, GEDIT_METADATA_ATTRIBUTE_POSITION, +						     position, NULL); +		else +			gedit_document_set_metadata (doc, GEDIT_METADATA_ATTRIBUTE_POSITION, +						     position, GEDIT_METADATA_ATTRIBUTE_LANGUAGE, +						     language, NULL); +		g_free (position); +	} + +	if (doc->priv->loader) +	{ +		g_object_unref (doc->priv->loader); +		doc->priv->loader = NULL; +	} + +	if (doc->priv->metadata_info != NULL) +	{ +		g_object_unref (doc->priv->metadata_info); +		doc->priv->metadata_info = NULL; +	} + +	doc->priv->dispose_has_run = TRUE; + +	G_OBJECT_CLASS (gedit_document_parent_class)->dispose (object); +} + +static void +gedit_document_finalize (GObject *object) +{ +	GeditDocument *doc = GEDIT_DOCUMENT (object);  + +	gedit_debug (DEBUG_DOCUMENT); + +	if (doc->priv->untitled_number > 0) +	{ +		g_return_if_fail (doc->priv->uri == NULL); +		release_untitled_number (doc->priv->untitled_number); +	} + +	g_free (doc->priv->uri); +	g_free (doc->priv->content_type); +	g_free (doc->priv->search_text); + +	if (doc->priv->to_search_region != NULL) +	{ +		/* we can't delete marks if we're finalizing the buffer */ +		gedit_text_region_destroy (doc->priv->to_search_region, FALSE); +	} + +	G_OBJECT_CLASS (gedit_document_parent_class)->finalize (object); +} + +static void +gedit_document_get_property (GObject    *object, +			     guint       prop_id, +			     GValue     *value, +			     GParamSpec *pspec) +{ +	GeditDocument *doc = GEDIT_DOCUMENT (object); + +	switch (prop_id) +	{ +		case PROP_URI: +			g_value_set_string (value, doc->priv->uri); +			break; +		case PROP_SHORTNAME: +			g_value_take_string (value, gedit_document_get_short_name_for_display (doc)); +			break; +		case PROP_CONTENT_TYPE: +			g_value_take_string (value, gedit_document_get_content_type (doc)); +			break; +		case PROP_MIME_TYPE: +			g_value_take_string (value, gedit_document_get_mime_type (doc)); +			break; +		case PROP_READ_ONLY: +			g_value_set_boolean (value, doc->priv->readonly); +			break; +		case PROP_ENCODING: +			g_value_set_boxed (value, doc->priv->encoding); +			break; +		case PROP_CAN_SEARCH_AGAIN: +			g_value_set_boolean (value, gedit_document_get_can_search_again (doc)); +			break; +		case PROP_ENABLE_SEARCH_HIGHLIGHTING: +			g_value_set_boolean (value, gedit_document_get_enable_search_highlighting (doc)); +			break; +		case PROP_NEWLINE_TYPE: +			g_value_set_enum (value, doc->priv->newline_type); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_document_set_property (GObject      *object, +			     guint         prop_id, +			     const GValue *value, +			     GParamSpec   *pspec) +{ +	GeditDocument *doc = GEDIT_DOCUMENT (object); + +	switch (prop_id) +	{ +		case PROP_ENABLE_SEARCH_HIGHLIGHTING: +			gedit_document_set_enable_search_highlighting (doc, +								       g_value_get_boolean (value)); +			break; +		case PROP_NEWLINE_TYPE: +			gedit_document_set_newline_type (doc, +							 g_value_get_enum (value)); +			break; +		case PROP_SHORTNAME: +			gedit_document_set_short_name_for_display (doc, +			                                           g_value_get_string (value)); +			break; +		case PROP_CONTENT_TYPE: +			gedit_document_set_content_type (doc, +			                                 g_value_get_string (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +emit_cursor_moved (GeditDocument *doc) +{ +	if (!doc->priv->stop_cursor_moved_emission) +	{ +		g_signal_emit (doc, +			       document_signals[CURSOR_MOVED], +			       0); +	} +} + +static void +gedit_document_mark_set (GtkTextBuffer     *buffer, +                         const GtkTextIter *iter, +                         GtkTextMark       *mark) +{ +	GeditDocument *doc = GEDIT_DOCUMENT (buffer); + +	if (GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->mark_set) +		GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->mark_set (buffer, +									       iter, +									       mark); + +	if (mark == gtk_text_buffer_get_insert (buffer)) +	{ +		emit_cursor_moved (doc); +	} +} + +static void +gedit_document_changed (GtkTextBuffer *buffer) +{ +	emit_cursor_moved (GEDIT_DOCUMENT (buffer)); + +	GTK_TEXT_BUFFER_CLASS (gedit_document_parent_class)->changed (buffer); +} + +static void  +gedit_document_class_init (GeditDocumentClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GtkTextBufferClass *buf_class = GTK_TEXT_BUFFER_CLASS (klass); + +	object_class->dispose = gedit_document_dispose; +	object_class->finalize = gedit_document_finalize; +	object_class->get_property = gedit_document_get_property; +	object_class->set_property = gedit_document_set_property; + +	buf_class->mark_set = gedit_document_mark_set; +	buf_class->changed = gedit_document_changed; + +	klass->load = gedit_document_load_real; +	klass->save = gedit_document_save_real; + +	g_object_class_install_property (object_class, PROP_URI, +					 g_param_spec_string ("uri", +							      "URI", +							      "The document's URI", +							      NULL, +							      G_PARAM_READABLE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_SHORTNAME, +					 g_param_spec_string ("shortname", +							      "Short Name", +							      "The document's short name", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_CONTENT_TYPE, +					 g_param_spec_string ("content-type", +							      "Content Type", +							      "The document's Content Type", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_MIME_TYPE, +					 g_param_spec_string ("mime-type", +							      "MIME Type", +							      "The document's MIME Type", +							      "text/plain", +							      G_PARAM_READABLE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_READ_ONLY, +					 g_param_spec_boolean ("read-only", +							       "Read Only", +							       "Whether the document is read only or not", +							       FALSE, +							       G_PARAM_READABLE | +							       G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_ENCODING, +					 g_param_spec_boxed ("encoding", +							     "Encoding", +							     "The GeditEncoding used for the document", +							     GEDIT_TYPE_ENCODING, +							     G_PARAM_READABLE | +							     G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_CAN_SEARCH_AGAIN, +					 g_param_spec_boolean ("can-search-again", +							       "Can search again", +							       "Wheter it's possible to search again in the document", +							       FALSE, +							       G_PARAM_READABLE | +							       G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, PROP_ENABLE_SEARCH_HIGHLIGHTING, +					 g_param_spec_boolean ("enable-search-highlighting", +							       "Enable Search Highlighting", +							       "Whether all the occurences of the searched string must be highlighted", +							       FALSE, +							       G_PARAM_READWRITE | +							       G_PARAM_STATIC_STRINGS)); + +	/** +	 * GeditDocument:newline-type: +	 * +	 * The :newline-type property determines what is considered +	 * as a line ending when saving the document +	 */ +	g_object_class_install_property (object_class, PROP_NEWLINE_TYPE, +	                                 g_param_spec_enum ("newline-type", +	                                                    "Newline type", +	                                                    "The accepted types of line ending", +	                                                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE, +	                                                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF, +	                                                    G_PARAM_READWRITE | +	                                                    G_PARAM_STATIC_NAME | +	                                                    G_PARAM_STATIC_BLURB)); + +	/* This signal is used to update the cursor position is the statusbar, +	 * it's emitted either when the insert mark is moved explicitely or +	 * when the buffer changes (insert/delete). +	 * We prevent the emission of the signal during replace_all to  +	 * improve performance. +	 */ +	document_signals[CURSOR_MOVED] = +   		g_signal_new ("cursor-moved", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, cursor_moved), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, +			      0); + +	/** +	 * GeditDocument::load: +	 * @document: the #GeditDocument. +	 * @uri: the uri where to load the document from. +	 * @encoding: the #GeditEncoding to encode the document. +	 * @line_pos: the line to show. +	 * @create: whether the document should be created if it doesn't exist. +	 * +	 * The "load" signal is emitted when a document is loaded. +	 * +	 * Since: 2.22 +	 */ +	document_signals[LOAD] = +		g_signal_new ("load", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, load), +			      NULL, NULL, +			      gedit_marshal_VOID__STRING_BOXED_INT_BOOLEAN, +			      G_TYPE_NONE, +			      4, +			      G_TYPE_STRING, +			      /* we rely on the fact that the GeditEncoding pointer stays +			       * the same forever */ +			      GEDIT_TYPE_ENCODING | G_SIGNAL_TYPE_STATIC_SCOPE, +			      G_TYPE_INT, +			      G_TYPE_BOOLEAN); + + +	document_signals[LOADING] = +		g_signal_new ("loading", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, loading), +			      NULL, NULL, +			      gedit_marshal_VOID__UINT64_UINT64, +			      G_TYPE_NONE, +			      2, +			      G_TYPE_UINT64, +			      G_TYPE_UINT64); + +	document_signals[LOADED] = +   		g_signal_new ("loaded", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, loaded), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__POINTER, +			      G_TYPE_NONE, +			      1, +			      G_TYPE_POINTER); + +	/** +	 * GeditDocument::save: +	 * @document: the #GeditDocument. +	 * @uri: the uri where the document is about to be saved. +	 * @encoding: the #GeditEncoding used to save the document. +	 * @flags: the #GeditDocumentSaveFlags for the save operation. +	 * +	 * The "save" signal is emitted when the document is saved. +	 * +	 * Since: 2.20 +	 */ +	document_signals[SAVE] = +		g_signal_new ("save", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, save), +			      NULL, NULL, +			      gedit_marshal_VOID__STRING_BOXED_FLAGS, +			      G_TYPE_NONE, +			      3, +			      G_TYPE_STRING, +			      /* we rely on the fact that the GeditEncoding pointer stays +			       * the same forever */ +			      GEDIT_TYPE_ENCODING | G_SIGNAL_TYPE_STATIC_SCOPE, +			      GEDIT_TYPE_DOCUMENT_SAVE_FLAGS); + +	document_signals[SAVING] = +   		g_signal_new ("saving", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, saving), +			      NULL, NULL, +			      gedit_marshal_VOID__UINT64_UINT64, +			      G_TYPE_NONE, +			      2, +			      G_TYPE_UINT64, +			      G_TYPE_UINT64); + +	document_signals[SAVED] = +   		g_signal_new ("saved", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, saved), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__POINTER, +			      G_TYPE_NONE, +			      1, +			      G_TYPE_POINTER); + +	document_signals[SEARCH_HIGHLIGHT_UPDATED] = +	    	g_signal_new ("search_highlight_updated", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditDocumentClass, search_highlight_updated), +			      NULL, NULL, +			      gedit_marshal_VOID__BOXED_BOXED, +			      G_TYPE_NONE,  +			      2,  +			      GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE, +			      GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE); + +	g_type_class_add_private (object_class, sizeof(GeditDocumentPrivate)); +} + +static void +set_language (GeditDocument     *doc,  +              GtkSourceLanguage *lang, +              gboolean           set_by_user) +{ +	GtkSourceLanguage *old_lang; + +	gedit_debug (DEBUG_DOCUMENT); +	 +	old_lang = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (doc)); +	 +	if (old_lang == lang) +		return; + +	gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (doc), lang); + +	if (lang != NULL) +		gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (doc), +				 gedit_prefs_manager_get_enable_syntax_highlighting ()); +	else +		gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (doc),  +				 FALSE); + +	if (set_by_user && (doc->priv->uri != NULL)) +	{ +		gedit_document_set_metadata (doc, GEDIT_METADATA_ATTRIBUTE_LANGUAGE, +			(lang == NULL) ? "_NORMAL_" : gtk_source_language_get_id (lang), +			NULL); +	} + +	doc->priv->language_set_by_user = set_by_user; +} + +static void +set_encoding (GeditDocument       *doc,  +	      const GeditEncoding *encoding, +	      gboolean             set_by_user) +{ +	g_return_if_fail (encoding != NULL); + +	gedit_debug (DEBUG_DOCUMENT); + +	if (doc->priv->encoding == encoding) +		return; + +	doc->priv->encoding = encoding; + +	if (set_by_user) +	{ +		const gchar *charset; + +		charset = gedit_encoding_get_charset (encoding); + +		gedit_document_set_metadata (doc, GEDIT_METADATA_ATTRIBUTE_ENCODING, +					     charset, NULL); +	} + +	g_object_notify (G_OBJECT (doc), "encoding"); +} + +static GtkSourceStyleScheme * +get_default_style_scheme (void) +{ +	gchar *scheme_id; +	GtkSourceStyleScheme *def_style; +	GtkSourceStyleSchemeManager *manager; + +	manager = gedit_get_style_scheme_manager (); +	scheme_id = gedit_prefs_manager_get_source_style_scheme (); +	def_style = gtk_source_style_scheme_manager_get_scheme (manager, +								scheme_id); + +	if (def_style == NULL) +	{ +		g_warning ("Default style scheme '%s' cannot be found, falling back to 'classic' style scheme ", scheme_id); + +		def_style = gtk_source_style_scheme_manager_get_scheme (manager, "classic"); +		if (def_style == NULL)  +		{ +			g_warning ("Style scheme 'classic' cannot be found, check your GtkSourceView installation.");			 +		} +	} + +	g_free (scheme_id); + +	return def_style; +} + +static void +on_uri_changed (GeditDocument *doc, +		GParamSpec    *pspec, +		gpointer       useless) +{ +#ifdef ENABLE_GVFS_METADATA +	GFile *location; + +	location = gedit_document_get_location (doc); + +	/* load metadata for this uri: we load sync since metadata is +	 * always local so it should be fast and we need the information +	 * right after the uri was set. +	 */ +	if (location != NULL) +	{ +		GError *error = NULL; + +		if (doc->priv->metadata_info != NULL) +			g_object_unref (doc->priv->metadata_info); + +		doc->priv->metadata_info = g_file_query_info (location, +							      METADATA_QUERY, +							      G_FILE_QUERY_INFO_NONE, +							      NULL, +							      &error); + +		if (error != NULL) +		{ +			if (error->code != G_FILE_ERROR_ISDIR && +			    error->code != G_FILE_ERROR_NOTDIR && +			    error->code != G_FILE_ERROR_NOENT) +			{ +				g_warning ("%s", error->message); +			} + +			g_error_free (error); +		} + +		g_object_unref (location); +	} +#endif +} + +static GtkSourceLanguage * +guess_language (GeditDocument *doc, +		const gchar   *content_type) +{ +	gchar *data; +	GtkSourceLanguage *language = NULL; + +	data = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_LANGUAGE); + +	if (data != NULL) +	{ +		gedit_debug_message (DEBUG_DOCUMENT, "Language from metadata: %s", data); + +		if (strcmp (data, "_NORMAL_") != 0) +		{ +			language = gtk_source_language_manager_get_language ( +						gedit_get_language_manager (), +						data); +		} + +		g_free (data); +	} +	else +	{ +		GFile *file; +		gchar *basename = NULL; + +		file = gedit_document_get_location (doc); +		gedit_debug_message (DEBUG_DOCUMENT, "Sniffing Language"); + +		if (file) +		{ +			basename = g_file_get_basename (file); +		} +		else if (doc->priv->short_name != NULL) +		{ +			basename = g_strdup (doc->priv->short_name); +		} + +		language = gtk_source_language_manager_guess_language ( +					gedit_get_language_manager (), +					basename, +					content_type); + +		g_free (basename); + +		if (file != NULL) +		{ +			g_object_unref (file); +		} +	} + +	return language; +} + +static void +on_content_type_changed (GeditDocument *doc, +			 GParamSpec    *pspec, +			 gpointer       useless) +{ +	if (!doc->priv->language_set_by_user) +	{ +		GtkSourceLanguage *language; + +		language = guess_language (doc, doc->priv->content_type); + +		gedit_debug_message (DEBUG_DOCUMENT, "Language: %s", +				     language != NULL ? gtk_source_language_get_name (language) : "None"); + +		set_language (doc, language, FALSE); +	} +} + +static gchar * +get_default_content_type (void) +{ +	return g_content_type_from_mime_type ("text/plain"); +} + +static void +gedit_document_init (GeditDocument *doc) +{ +	GtkSourceStyleScheme *style_scheme; + +	gedit_debug (DEBUG_DOCUMENT); + +	doc->priv = GEDIT_DOCUMENT_GET_PRIVATE (doc); + +	doc->priv->uri = NULL; +	doc->priv->untitled_number = get_untitled_number (); + +	doc->priv->metadata_info = NULL; + +	doc->priv->content_type = get_default_content_type (); + +	doc->priv->readonly = FALSE; + +	doc->priv->stop_cursor_moved_emission = FALSE; + +	doc->priv->last_save_was_manually = TRUE; +	doc->priv->language_set_by_user = FALSE; + +	doc->priv->dispose_has_run = FALSE; + +	doc->priv->mtime.tv_sec = 0; +	doc->priv->mtime.tv_usec = 0; + +	g_get_current_time (&doc->priv->time_of_last_save_or_load); + +	doc->priv->encoding = gedit_encoding_get_utf8 (); + +	doc->priv->newline_type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT; + +	gtk_source_buffer_set_max_undo_levels (GTK_SOURCE_BUFFER (doc),  +					       gedit_prefs_manager_get_undo_actions_limit ()); + +	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (doc),  +							   gedit_prefs_manager_get_bracket_matching ()); + +	gedit_document_set_enable_search_highlighting (doc, +						       gedit_prefs_manager_get_enable_search_highlighting ()); + +	style_scheme = get_default_style_scheme (); +	if (style_scheme != NULL) +		gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (doc), +						    style_scheme); + +	g_signal_connect_after (doc,  +			  	"insert-text", +			  	G_CALLBACK (insert_text_cb), +			  	NULL); + +	g_signal_connect_after (doc,  +			  	"delete-range", +			  	G_CALLBACK (delete_range_cb), +			  	NULL); + +	g_signal_connect (doc, +			  "notify::content-type", +			  G_CALLBACK (on_content_type_changed), +			  NULL); + +	g_signal_connect (doc, +			  "notify::uri", +			  G_CALLBACK (on_uri_changed), +			  NULL); +} + +GeditDocument * +gedit_document_new (void) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	return GEDIT_DOCUMENT (g_object_new (GEDIT_TYPE_DOCUMENT, NULL)); +} + +static void +set_content_type_no_guess (GeditDocument *doc, +			   const gchar   *content_type) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	if (doc->priv->content_type != NULL && content_type != NULL && +	    (0 == strcmp (doc->priv->content_type, content_type))) +		return; + +	g_free (doc->priv->content_type); + +	if (content_type == NULL || g_content_type_is_unknown (content_type)) +		doc->priv->content_type = get_default_content_type (); +	else +		doc->priv->content_type = g_strdup (content_type); + +	g_object_notify (G_OBJECT (doc), "content-type"); +} + +static void +set_content_type (GeditDocument *doc, +		  const gchar   *content_type) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	if (content_type == NULL) +	{ +		GFile *file; +		gchar *guessed_type = NULL; + +		/* If content type is null, we guess from the filename */ +		file = gedit_document_get_location (doc); +		if (file != NULL) +		{ +			gchar *basename; + +			basename = g_file_get_basename (file); +			guessed_type = g_content_type_guess (basename, NULL, 0, NULL); + +			g_free (basename); +			g_object_unref (file); +		} + +		set_content_type_no_guess (doc, guessed_type); + +		g_free (guessed_type); +	} +	else +	{ +		set_content_type_no_guess (doc, content_type); +	} +} + +void +gedit_document_set_content_type (GeditDocument *doc, +                                 const gchar   *content_type) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	set_content_type (doc, content_type); +} + +static void +set_uri (GeditDocument *doc, +	 const gchar   *uri) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	g_return_if_fail ((uri == NULL) || gedit_utils_is_valid_uri (uri)); + +	if (uri != NULL) +	{ +		if (doc->priv->uri == uri) +			return; + +		g_free (doc->priv->uri); +		doc->priv->uri = g_strdup (uri); + +		if (doc->priv->untitled_number > 0) +		{ +			release_untitled_number (doc->priv->untitled_number); +			doc->priv->untitled_number = 0; +		} +	} + +	g_object_notify (G_OBJECT (doc), "uri"); + +	if (doc->priv->short_name == NULL) +	{ +		g_object_notify (G_OBJECT (doc), "shortname"); +	} +} + +GFile * +gedit_document_get_location (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	return doc->priv->uri == NULL ? NULL : g_file_new_for_uri (doc->priv->uri); +} + +gchar * +gedit_document_get_uri (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	return g_strdup (doc->priv->uri); +} + +void +gedit_document_set_uri (GeditDocument *doc, +			const gchar   *uri) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (uri != NULL); + +	set_uri (doc, uri); +	set_content_type (doc, NULL); +} + +/* Never returns NULL */ +gchar * +gedit_document_get_uri_for_display (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("")); + +	if (doc->priv->uri == NULL) +		return g_strdup_printf (_("Unsaved Document %d"), +					doc->priv->untitled_number); +	else +		return gedit_utils_uri_for_display (doc->priv->uri); +} + +/* Never returns NULL */ +gchar * +gedit_document_get_short_name_for_display (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("")); + +	if (doc->priv->short_name != NULL) +		return g_strdup (doc->priv->short_name); +	else if (doc->priv->uri == NULL) +		return g_strdup_printf (_("Unsaved Document %d"), +					doc->priv->untitled_number); +	else +		return gedit_utils_basename_for_display (doc->priv->uri); +} + +void +gedit_document_set_short_name_for_display (GeditDocument *doc, +                                           const gchar   *short_name) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	g_free (doc->priv->short_name); +	doc->priv->short_name = g_strdup (short_name); + +	g_object_notify (G_OBJECT (doc), "shortname"); +} + +gchar * +gedit_document_get_content_type (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + + 	return g_strdup (doc->priv->content_type); +} + +/* Never returns NULL */ +gchar * +gedit_document_get_mime_type (GeditDocument *doc) +{ +	gchar *mime_type = NULL; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), g_strdup ("text/plain")); + +	if ((doc->priv->content_type != NULL) && +	    (!g_content_type_is_unknown (doc->priv->content_type))) +	{ +		mime_type = g_content_type_get_mime_type (doc->priv->content_type); +	} + + 	return mime_type != NULL ? mime_type : g_strdup ("text/plain"); +} + +/* Note: do not emit the notify::read-only signal */ +static gboolean +set_readonly (GeditDocument *doc, +	      gboolean       readonly) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	readonly = (readonly != FALSE); + +	if (doc->priv->readonly == readonly)  +		return FALSE; + +	doc->priv->readonly = readonly; + +	return TRUE; +} + +/** + * gedit_document_set_readonly: + * @doc: a #GeditDocument + * @readonly: %TRUE to se the document as read-only + * + * If @readonly is %TRUE sets @doc as read-only. + */ +void +_gedit_document_set_readonly (GeditDocument *doc, +			      gboolean       readonly) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	if (set_readonly (doc, readonly)) +	{ +		g_object_notify (G_OBJECT (doc), "read-only"); +	} +} + +gboolean +gedit_document_get_readonly (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE); + +	return doc->priv->readonly; +} + +gboolean +_gedit_document_check_externally_modified (GeditDocument *doc) +{ +	GFile *gfile; +	GFileInfo *info; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + +	if (doc->priv->uri == NULL) +	{ +		return FALSE; +	} + +	gfile = g_file_new_for_uri (doc->priv->uri); +	info = g_file_query_info (gfile, +				  G_FILE_ATTRIBUTE_TIME_MODIFIED "," \ +				  G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, +				  G_FILE_QUERY_INFO_NONE, +				  NULL, NULL); +	g_object_unref (gfile); + +	if (info != NULL) +	{ +		/* While at it also check if permissions changed */ +		if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) +		{ +			gboolean read_only; + +			read_only = !g_file_info_get_attribute_boolean (info, +									G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); + +			_gedit_document_set_readonly (doc, read_only); +		} + +		if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) +		{ +			GTimeVal timeval; + +			g_file_info_get_modification_time (info, &timeval); +			g_object_unref (info); +	 +			return (timeval.tv_sec > doc->priv->mtime.tv_sec) || +			       (timeval.tv_sec == doc->priv->mtime.tv_sec &&  +			       timeval.tv_usec > doc->priv->mtime.tv_usec); +		} +	} + +	return FALSE; +} + +static void +reset_temp_loading_data (GeditDocument       *doc) +{ +	/* the loader has been used, throw it away */ +	g_object_unref (doc->priv->loader); +	doc->priv->loader = NULL; + +	doc->priv->requested_encoding = NULL; +	doc->priv->requested_line_pos = 0; +} + +static void +document_loader_loaded (GeditDocumentLoader *loader, +			const GError        *error, +			GeditDocument       *doc) +{ +	/* load was successful */ +	if (error == NULL || +	    (error->domain == GEDIT_DOCUMENT_ERROR && +	     error->code == GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK)) +	{ +		GtkTextIter iter; +		GFileInfo *info; +		const gchar *content_type = NULL; +		gboolean read_only = FALSE; +		GTimeVal mtime = {0, 0}; + +		info = gedit_document_loader_get_info (loader); + +		if (info) +		{ +			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)) +				content_type = g_file_info_get_attribute_string (info, +										 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE); + +			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) +				g_file_info_get_modification_time (info, &mtime); + +			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) +				read_only = !g_file_info_get_attribute_boolean (info, +										G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); +		} + +		doc->priv->mtime = mtime; + +		set_readonly (doc, read_only); + +		g_get_current_time (&doc->priv->time_of_last_save_or_load); + +		set_encoding (doc,  +			      gedit_document_loader_get_encoding (loader), +			      (doc->priv->requested_encoding != NULL)); + +		set_content_type (doc, content_type); + +		gedit_document_set_newline_type (doc, +		                                 gedit_document_loader_get_newline_type (loader)); + +		/* move the cursor at the requested line if any */ +		if (doc->priv->requested_line_pos > 0) +		{ +			/* line_pos - 1 because get_iter_at_line counts from 0 */ +			gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc), +							  &iter, +							  doc->priv->requested_line_pos - 1); +		} +		/* else, if enabled, to the position stored in the metadata */ +		else if (gedit_prefs_manager_get_restore_cursor_position ()) +		{ +			gchar *pos; +			gint offset; + +			pos = gedit_document_get_metadata (doc, GEDIT_METADATA_ATTRIBUTE_POSITION); + +			offset = pos ? atoi (pos) : 0; +			g_free (pos); + +			gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc), +							    &iter, +							    MAX (offset, 0)); + +			/* make sure it's a valid position, if the file +			 * changed we may have ended up in the middle of +			 * a utf8 character cluster */ +			if (!gtk_text_iter_is_cursor_position (&iter)) +			{ +				gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), +								&iter); +			} +		} +		/* otherwise to the top */ +		else +		{ +			gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), +							&iter); +		} + +		gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter); +	} + +	/* special case creating a named new doc */ +	else if (doc->priv->create && +	         (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) && +	         (gedit_utils_uri_has_file_scheme (doc->priv->uri))) +	{ +		reset_temp_loading_data (doc); + +		g_signal_emit (doc, +			       document_signals[LOADED], +			       0, +			       NULL); + +		return; +	} +	 +	g_signal_emit (doc, +		       document_signals[LOADED], +		       0, +		       error); + +	reset_temp_loading_data (doc); +} + +static void +document_loader_loading (GeditDocumentLoader *loader, +			 gboolean             completed, +			 const GError        *error, +			 GeditDocument       *doc) +{ +	if (completed) +	{ +		document_loader_loaded (loader, error, doc); +	} +	else +	{ +		goffset size = 0; +		goffset read; +		GFileInfo *info; + +		info = gedit_document_loader_get_info (loader); + +		if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) +			size = g_file_info_get_attribute_uint64 (info, +								 G_FILE_ATTRIBUTE_STANDARD_SIZE); + +		read = gedit_document_loader_get_bytes_read (loader); + +		g_signal_emit (doc,  +			       document_signals[LOADING], +			       0, +			       read, +			       size); +	} +} + +static void +gedit_document_load_real (GeditDocument       *doc, +			  const gchar         *uri, +			  const GeditEncoding *encoding, +			  gint                 line_pos, +			  gboolean             create) +{ +	g_return_if_fail (doc->priv->loader == NULL); + +	gedit_debug_message (DEBUG_DOCUMENT, "load_real: uri = %s", uri); + +	/* create a loader. It will be destroyed when loading is completed */ +	doc->priv->loader = gedit_document_loader_new (doc, uri, encoding); + +	g_signal_connect (doc->priv->loader, +			  "loading", +			  G_CALLBACK (document_loader_loading), +			  doc); + +	doc->priv->create = create; +	doc->priv->requested_encoding = encoding; +	doc->priv->requested_line_pos = line_pos; + +	set_uri (doc, uri); +	set_content_type (doc, NULL); + +	gedit_document_loader_load (doc->priv->loader); +} + +/** + * gedit_document_load: + * @doc: the #GeditDocument. + * @uri: the uri where to load the document from. + * @encoding: the #GeditEncoding to encode the document. + * @line_pos: the line to show. + * @create: whether the document should be created if it doesn't exist. + * + * Load a document. This results in the "load" signal to be emitted. + */ +void +gedit_document_load (GeditDocument       *doc, +		     const gchar         *uri, +		     const GeditEncoding *encoding, +		     gint                 line_pos, +		     gboolean             create) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (uri != NULL); +	g_return_if_fail (gedit_utils_is_valid_uri (uri)); + +	g_signal_emit (doc, document_signals[LOAD], 0, uri, encoding, line_pos, create); +} + +/** + * gedit_document_load_cancel: + * @doc: the #GeditDocument. + * + * Cancel load of a document. + */ +gboolean +gedit_document_load_cancel (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + +	if (doc->priv->loader == NULL) +		return FALSE; + +	return gedit_document_loader_cancel (doc->priv->loader); +} + +static void +document_saver_saving (GeditDocumentSaver *saver, +		       gboolean            completed, +		       const GError       *error, +		       GeditDocument      *doc) +{ +	gedit_debug (DEBUG_DOCUMENT); + +	if (completed) +	{ +		/* save was successful */ +		if (error == NULL) +		{ +			const gchar *uri; +			const gchar *content_type = NULL; +			GTimeVal mtime = {0, 0}; +			GFileInfo *info; + +			uri = gedit_document_saver_get_uri (saver); +			set_uri (doc, uri); + +			info = gedit_document_saver_get_info (saver); + +			if (info != NULL) +			{ +				if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)) +					content_type = g_file_info_get_attribute_string (info, +											 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE); + +				if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) +					g_file_info_get_modification_time (info, &mtime); +			} + +			set_content_type (doc, content_type); +			doc->priv->mtime = mtime; + +			g_get_current_time (&doc->priv->time_of_last_save_or_load); + +			_gedit_document_set_readonly (doc, FALSE); + +			gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (doc), +						      FALSE); + +			set_encoding (doc,  +				      doc->priv->requested_encoding,  +				      TRUE); +		} + +		g_signal_emit (doc, +			       document_signals[SAVED], +			       0, +			       error); + +		/* the saver has been used, throw it away */ +		g_object_unref (doc->priv->saver); +		doc->priv->saver = NULL; +	} +	else +	{ +		goffset size = 0; +		goffset written = 0; + +		size = gedit_document_saver_get_file_size (saver); +		written = gedit_document_saver_get_bytes_written (saver); + +		gedit_debug_message (DEBUG_DOCUMENT, "save progress: %" G_GINT64_FORMAT " of %" G_GINT64_FORMAT, written, size); + +		g_signal_emit (doc, +			       document_signals[SAVING], +			       0, +			       written, +			       size); +	} +} + +static void +gedit_document_save_real (GeditDocument          *doc, +			  const gchar            *uri, +			  const GeditEncoding    *encoding, +			  GeditDocumentSaveFlags  flags) +{ +	g_return_if_fail (doc->priv->saver == NULL); + +	/* create a saver, it will be destroyed once saving is complete */ +	doc->priv->saver = gedit_document_saver_new (doc, uri, encoding, +						     doc->priv->newline_type, +						     flags); + +	g_signal_connect (doc->priv->saver, +			  "saving", +			  G_CALLBACK (document_saver_saving), +			  doc); + +	doc->priv->requested_encoding = encoding; + +	gedit_document_saver_save (doc->priv->saver, +				   &doc->priv->mtime); +} + +/** + * gedit_document_save: + * @doc: the #GeditDocument. + * @flags: optionnal #GeditDocumentSaveFlags. + * + * Save the document to its previous location. This results in the "save" + * signal to be emitted. + */ +void +gedit_document_save (GeditDocument          *doc, +		     GeditDocumentSaveFlags  flags) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (doc->priv->uri != NULL); + +	g_signal_emit (doc, +		       document_signals[SAVE], +		       0, +		       doc->priv->uri, +		       doc->priv->encoding, +		       flags); +} + +/** + * gedit_document_save_as: + * @doc: the #GeditDocument. + * @uri: the uri where to save the document. + * @encoding: the #GeditEncoding to encode the document. + * @flags: optionnal #GeditDocumentSaveFlags. + * + * Save the document to a new location. This results in the "save" signal + * to be emitted. + */ +void +gedit_document_save_as (GeditDocument          *doc, +			const gchar            *uri, +			const GeditEncoding    *encoding, +			GeditDocumentSaveFlags  flags) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (uri != NULL); +	g_return_if_fail (encoding != NULL); + +	/* priv->mtime refers to the the old uri (if any). Thus, it should be +	 * ignored when saving as. */ +	g_signal_emit (doc, +		       document_signals[SAVE], +		       0, +		       uri, +		       encoding, +		       flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME); +} + +gboolean +gedit_document_insert_file (GeditDocument       *doc, +			    GtkTextIter         *iter, +			    const gchar         *uri, +			    const GeditEncoding *encoding) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	g_return_val_if_fail (iter != NULL, FALSE); +	g_return_val_if_fail (gtk_text_iter_get_buffer (iter) ==  +				GTK_TEXT_BUFFER (doc), FALSE); + +	/* TODO */ + +	return FALSE; +} + +gboolean	  +gedit_document_is_untouched (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE); + +	return (doc->priv->uri == NULL) &&  +		(!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))); +} + +gboolean  +gedit_document_is_untitled (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE); + +	return (doc->priv->uri == NULL); +} + +gboolean +gedit_document_is_local (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + +	if (doc->priv->uri == NULL) +	{ +		return FALSE; +	} + +	return gedit_utils_uri_has_file_scheme (doc->priv->uri); +} + +gboolean +gedit_document_get_deleted (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + +	return doc->priv->uri && !gedit_utils_uri_exists (doc->priv->uri); +} + +/* + * If @line is bigger than the lines of the document, the cursor is moved + * to the last line and FALSE is returned. + */ +gboolean +gedit_document_goto_line (GeditDocument *doc,  +			  gint           line) +{ +	gboolean ret = TRUE; +	guint line_count; +	GtkTextIter iter; + +	gedit_debug (DEBUG_DOCUMENT); + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	g_return_val_if_fail (line >= -1, FALSE); + +	line_count = gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (doc)); + +	if (line >= line_count) +	{ +		ret = FALSE; +		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), +					      &iter); +	} +	else +	{ +		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc), +						  &iter, +						  line); +	} + +	gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter); + +	return ret; +} + +gboolean +gedit_document_goto_line_offset (GeditDocument *doc, +				 gint           line, +				 gint           line_offset) +{ +	gboolean ret = TRUE; +	guint offset_count; +	GtkTextIter iter; +	 +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	g_return_val_if_fail (line >= -1, FALSE); +	g_return_val_if_fail (line_offset >= -1, FALSE); +	 +	gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc), +					  &iter, +					  line); + +	offset_count = gtk_text_iter_get_chars_in_line (&iter); +	if (line_offset > offset_count) +	{ +		ret = FALSE; +	} +	else +	{ +		gtk_text_iter_set_line_offset (&iter, line_offset); +	} +	 +	gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter); + +	return ret; +} + +static gint +compute_num_of_lines (const gchar *text) +{ +	const gchar *p; +	gint len; +	gint n = 1; + +	g_return_val_if_fail (text != NULL, 0); + +	len = strlen (text); +	p = text; + +	while (len > 0) +	{ +		gint del, par; + +		pango_find_paragraph_boundary (p, len, &del, &par); + +		if (del == par) /* not found */ +			break; + +		p += par; +		len -= par; +		++n; +	} + +	return n; +} + +void +gedit_document_set_search_text (GeditDocument *doc, +				const gchar   *text, +				guint          flags) +{ +	gchar *converted_text; +	gboolean notify = FALSE; +	gboolean update_to_search_region = FALSE; +	 +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail ((text == NULL) || (doc->priv->search_text != text)); +	g_return_if_fail ((text == NULL) || g_utf8_validate (text, -1, NULL)); + +	gedit_debug_message (DEBUG_DOCUMENT, "text = %s", text); + +	if (text != NULL) +	{ +		if (*text != '\0') +		{ +			converted_text = gedit_utils_unescape_search_text (text); +			notify = !gedit_document_get_can_search_again (doc); +		} +		else +		{ +			converted_text = g_strdup(""); +			notify = gedit_document_get_can_search_again (doc); +		} +		 +		g_free (doc->priv->search_text); +	 +		doc->priv->search_text = converted_text; +		doc->priv->num_of_lines_search_text = compute_num_of_lines (doc->priv->search_text); +		update_to_search_region = TRUE; +	} +	 +	if (!GEDIT_SEARCH_IS_DONT_SET_FLAGS (flags)) +	{ +		if (doc->priv->search_flags != flags) +			update_to_search_region = TRUE; +			 +		doc->priv->search_flags = flags; + +	} + +	if (update_to_search_region) +	{ +		GtkTextIter begin; +		GtkTextIter end; +		 +		gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), +					    &begin, +					    &end); +					     +		to_search_region_range (doc, +					&begin, +					&end); +	} +	 +	if (notify) +		g_object_notify (G_OBJECT (doc), "can-search-again"); +} + +gchar * +gedit_document_get_search_text (GeditDocument *doc, +				guint         *flags) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	if (flags != NULL) +		*flags = doc->priv->search_flags; + +	return gedit_utils_escape_search_text (doc->priv->search_text); +} + +gboolean +gedit_document_get_can_search_again (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); + +	return ((doc->priv->search_text != NULL) &&  +	        (*doc->priv->search_text != '\0')); +} + +gboolean +gedit_document_search_forward (GeditDocument     *doc, +			       const GtkTextIter *start, +			       const GtkTextIter *end, +			       GtkTextIter       *match_start, +			       GtkTextIter       *match_end) +{ +	GtkTextIter iter; +	GtkSourceSearchFlags search_flags; +	gboolean found = FALSE; +	GtkTextIter m_start; +	GtkTextIter m_end; +	 +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	g_return_val_if_fail ((start == NULL) ||  +			      (gtk_text_iter_get_buffer (start) ==  GTK_TEXT_BUFFER (doc)), FALSE); +	g_return_val_if_fail ((end == NULL) ||  +			      (gtk_text_iter_get_buffer (end) ==  GTK_TEXT_BUFFER (doc)), FALSE); +	 +	if (doc->priv->search_text == NULL) +	{ +		gedit_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n"); +		return FALSE; +	} +	else +		gedit_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text); +				       +	if (start == NULL) +		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), &iter); +	else +		iter = *start; +		 +	search_flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY; + +	if (!GEDIT_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags)) +	{ +		search_flags = search_flags | GTK_SOURCE_SEARCH_CASE_INSENSITIVE; +	} +		 +	while (!found) +	{ +		found = gtk_source_iter_forward_search (&iter, +							doc->priv->search_text,  +							search_flags, +                        	                	&m_start,  +                        	                	&m_end, +                                	               	end); +      	               	 +		if (found && GEDIT_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags)) +		{ +			found = gtk_text_iter_starts_word (&m_start) &&  +					gtk_text_iter_ends_word (&m_end); + +			if (!found)  +				iter = m_end; +		} +		else +			break; +	} +	 +	if (found && (match_start != NULL)) +		*match_start = m_start; +	 +	if (found && (match_end != NULL)) +		*match_end = m_end; +	 +	return found;			     +} +						  +gboolean +gedit_document_search_backward (GeditDocument     *doc, +				const GtkTextIter *start, +				const GtkTextIter *end, +				GtkTextIter       *match_start, +				GtkTextIter       *match_end) +{ +	GtkTextIter iter; +	GtkSourceSearchFlags search_flags; +	gboolean found = FALSE; +	GtkTextIter m_start; +	GtkTextIter m_end; +	 +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	g_return_val_if_fail ((start == NULL) ||  +			      (gtk_text_iter_get_buffer (start) ==  GTK_TEXT_BUFFER (doc)), FALSE); +	g_return_val_if_fail ((end == NULL) ||  +			      (gtk_text_iter_get_buffer (end) ==  GTK_TEXT_BUFFER (doc)), FALSE); +	 +	if (doc->priv->search_text == NULL) +	{ +		gedit_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n"); +		return FALSE; +	} +	else +		gedit_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text); +				       +	if (end == NULL) +		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter); +	else +		iter = *end; +		 +	search_flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY; + +	if (!GEDIT_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags)) +	{ +		search_flags = search_flags | GTK_SOURCE_SEARCH_CASE_INSENSITIVE; +	} + +	while (!found) +	{ +		found = gtk_source_iter_backward_search (&iter, +							 doc->priv->search_text,  +							 search_flags, +                        	                	 &m_start,  +                        	                	 &m_end, +                                	               	 start); +      	               	 +		if (found && GEDIT_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags)) +		{ +			found = gtk_text_iter_starts_word (&m_start) &&  +					gtk_text_iter_ends_word (&m_end); + +			if (!found)  +				iter = m_start; +		} +		else +			break; +	} +	 +	if (found && (match_start != NULL)) +		*match_start = m_start; +	 +	if (found && (match_end != NULL)) +		*match_end = m_end; +	 +	return found;		       +} + +gint  +gedit_document_replace_all (GeditDocument       *doc, +			    const gchar         *find,  +			    const gchar         *replace,  +			    guint                flags) +{ +	GtkTextIter iter; +	GtkTextIter m_start; +	GtkTextIter m_end; +	GtkSourceSearchFlags search_flags = 0; +	gboolean found = TRUE; +	gint cont = 0; +	gchar *search_text; +	gchar *replace_text; +	gint replace_text_len; +	GtkTextBuffer *buffer; +	gboolean brackets_highlighting; +	gboolean search_highliting; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), 0); +	g_return_val_if_fail (replace != NULL, 0); +	g_return_val_if_fail ((find != NULL) || (doc->priv->search_text != NULL), 0); + +	buffer = GTK_TEXT_BUFFER (doc); + +	if (find == NULL) +		search_text = g_strdup (doc->priv->search_text); +	else +		search_text = gedit_utils_unescape_search_text (find); + +	replace_text = gedit_utils_unescape_search_text (replace); + +	gtk_text_buffer_get_start_iter (buffer, &iter); + +	search_flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY; + +	if (!GEDIT_SEARCH_IS_CASE_SENSITIVE (flags)) +	{ +		search_flags = search_flags | GTK_SOURCE_SEARCH_CASE_INSENSITIVE; +	} + +	replace_text_len = strlen (replace_text); + +	/* disable cursor_moved emission until the end of the +	 * replace_all so that we don't spend all the time +	 * updating the position in the statusbar +	 */ +	doc->priv->stop_cursor_moved_emission = TRUE; + +	/* also avoid spending time matching brackets */ +	brackets_highlighting = gtk_source_buffer_get_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer)); +	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer), FALSE); + +	/* and do search highliting later */ +	search_highliting = gedit_document_get_enable_search_highlighting (doc); +	gedit_document_set_enable_search_highlighting (doc, FALSE); + +	gtk_text_buffer_begin_user_action (buffer); + +	do +	{ +		found = gtk_source_iter_forward_search (&iter, +							search_text,  +							search_flags, +                        	                	&m_start,  +                        	                	&m_end, +                                	               	NULL); + +		if (found && GEDIT_SEARCH_IS_ENTIRE_WORD (flags)) +		{ +			gboolean word; + +			word = gtk_text_iter_starts_word (&m_start) &&  +			       gtk_text_iter_ends_word (&m_end); + +			if (!word) +			{ +				iter = m_end; +				continue; +			} +		} + +		if (found) +		{ +			++cont; + +			gtk_text_buffer_delete (buffer,  +						&m_start, +						&m_end); +			gtk_text_buffer_insert (buffer, +						&m_start, +						replace_text, +						replace_text_len); + +			iter = m_start; +		}		 + +	} while (found); + +	gtk_text_buffer_end_user_action (buffer); + +	/* re-enable cursor_moved emission and notify +	 * the current position  +	 */ +	doc->priv->stop_cursor_moved_emission = FALSE; +	emit_cursor_moved (doc); + +	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer), +							   brackets_highlighting); +	gedit_document_set_enable_search_highlighting (doc, search_highliting); + +	g_free (search_text); +	g_free (replace_text); + +	return cont; +} + +void +gedit_document_set_language (GeditDocument     *doc,  +			     GtkSourceLanguage *lang) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	set_language (doc, lang, TRUE); +} + +GtkSourceLanguage * +gedit_document_get_language (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (doc)); +} + +const GeditEncoding * +gedit_document_get_encoding (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	return doc->priv->encoding; +} + +glong +_gedit_document_get_seconds_since_last_save_or_load (GeditDocument *doc) +{ +	GTimeVal current_time; + +	gedit_debug (DEBUG_DOCUMENT); +	 +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), -1); + +	g_get_current_time (¤t_time); + +	return (current_time.tv_sec - doc->priv->time_of_last_save_or_load.tv_sec); +} + +static void +get_search_match_colors (GeditDocument *doc, +			 gboolean      *foreground_set, +			 GdkColor      *foreground, +			 gboolean      *background_set, +			 GdkColor      *background) +{ +	GtkSourceStyleScheme *style_scheme; +	GtkSourceStyle *style; +	gchar *bg; +	gchar *fg; + +	style_scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (doc)); +	if (style_scheme == NULL) +		goto fallback; + +	style = gtk_source_style_scheme_get_style (style_scheme, +						   "search-match"); +	if (style == NULL) +		goto fallback; + +	g_object_get (style,  +		      "foreground-set", foreground_set,  +		      "foreground", &fg, +		      "background-set", background_set,  +		      "background", &bg, +		      NULL); + +	if (*foreground_set) +	{ +		if (fg == NULL || +		    !gdk_color_parse (fg, foreground)) +		{ +			*foreground_set = FALSE; +		} +	} + +	if (*background_set) +	{ +		if (bg == NULL || +		    !gdk_color_parse (bg, background)) +		{ +			*background_set = FALSE; +		} +	}	 + +	g_free (fg); +	g_free (bg); + +	return; + + fallback: +	gedit_debug_message (DEBUG_DOCUMENT,  +			     "Falling back to hard-coded colors " +			     "for the \"found\" text tag."); + +	gdk_color_parse ("#FFFF78", background); +	*background_set = TRUE; +	*foreground_set = FALSE; + +	return; +} + +static void +sync_found_tag (GeditDocument *doc, +		GParamSpec    *pspec, +		gpointer       data) +{ +	GdkColor fg; +	GdkColor bg; +	gboolean fg_set; +	gboolean bg_set; + +	gedit_debug (DEBUG_DOCUMENT); + +	g_return_if_fail (GTK_TEXT_TAG (doc->priv->found_tag)); + +	get_search_match_colors (doc, +				 &fg_set, &fg, +				 &bg_set, &bg); + +	g_object_set (doc->priv->found_tag, +		      "foreground-gdk", fg_set ? &fg : NULL, +		      NULL); +	g_object_set (doc->priv->found_tag, +		      "background-gdk", bg_set ? &bg : NULL, +		      NULL); +} + +static void +text_tag_set_highest_priority (GtkTextTag    *tag, +			       GtkTextBuffer *buffer) +{ +	GtkTextTagTable *table; +	gint n; + +	table = gtk_text_buffer_get_tag_table (buffer); +	n = gtk_text_tag_table_get_size (table); +	gtk_text_tag_set_priority (tag, n - 1); +} + +static void +search_region (GeditDocument *doc, +	       GtkTextIter   *start, +	       GtkTextIter   *end) +{ +	GtkTextIter iter; +	GtkTextIter m_start; +	GtkTextIter m_end;	 +	GtkSourceSearchFlags search_flags = 0; +	gboolean found = TRUE; + +	GtkTextBuffer *buffer;	 + +	gedit_debug (DEBUG_DOCUMENT); +	 +	buffer = GTK_TEXT_BUFFER (doc); +	 +	if (doc->priv->found_tag == NULL) +	{ +		doc->priv->found_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (doc),  +								   "found", +								   NULL); + +		sync_found_tag (doc, NULL, NULL); + +		g_signal_connect (doc,  +				  "notify::style-scheme", +				  G_CALLBACK (sync_found_tag), +				  NULL); +	} + +	/* make sure the 'found' tag has the priority over +	 * syntax highlighting tags */ +	text_tag_set_highest_priority (doc->priv->found_tag, +				       GTK_TEXT_BUFFER (doc)); +				    + +	if (doc->priv->search_text == NULL) +		return; + +	g_return_if_fail (doc->priv->num_of_lines_search_text > 0); + +	gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text); +	gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text); + +	if (gtk_text_iter_has_tag (start, doc->priv->found_tag) && +	    !gtk_text_iter_begins_tag (start, doc->priv->found_tag)) +		gtk_text_iter_backward_to_tag_toggle (start, doc->priv->found_tag); + +	if (gtk_text_iter_has_tag (end, doc->priv->found_tag) && +	    !gtk_text_iter_ends_tag (end, doc->priv->found_tag)) +		gtk_text_iter_forward_to_tag_toggle (end, doc->priv->found_tag); + +	/* +	g_print ("[%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start), +					   gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end)); +	*/ + +	gtk_text_buffer_remove_tag (buffer, +				    doc->priv->found_tag, +				    start, +				    end); + +	if (*doc->priv->search_text == '\0') +		return; + +	iter = *start; +		 +	search_flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY; + +	if (!GEDIT_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags)) +	{ +		search_flags = search_flags | GTK_SOURCE_SEARCH_CASE_INSENSITIVE; +	} +	 +	do +	{ +		if ((end != NULL) && gtk_text_iter_is_end (end)) +			end = NULL; +			 +		found = gtk_source_iter_forward_search (&iter, +							doc->priv->search_text,  +							search_flags, +                        	                	&m_start,  +                        	                	&m_end, +                                	               	end); +				 +		iter = m_end; +						      	               	 +		if (found && GEDIT_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags)) +		{ +			gboolean word; +						 +			word = gtk_text_iter_starts_word (&m_start) &&  +			       gtk_text_iter_ends_word (&m_end); +				 +			if (!word) +				continue; +		} + +		if (found) +		{ +			gtk_text_buffer_apply_tag (buffer, +						   doc->priv->found_tag, +						   &m_start, +						   &m_end); +		}		 + +	} while (found); +} + +static void +to_search_region_range (GeditDocument *doc, +			GtkTextIter   *start,  +			GtkTextIter   *end) +{ +	gedit_debug (DEBUG_DOCUMENT); +	 +	if (doc->priv->to_search_region == NULL) +		return; +		 +	gtk_text_iter_set_line_offset (start, 0); +	gtk_text_iter_forward_to_line_end (end); +	 +	/* +	g_print ("+ [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start), +					   gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end)); +	*/ + +	/* Add the region to the refresh region */ +	gedit_text_region_add (doc->priv->to_search_region, start, end); + +	/* Notify views of the updated highlight region */ +	gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text); +	gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text); +	 +	g_signal_emit (doc, document_signals [SEARCH_HIGHLIGHT_UPDATED], 0, start, end); +} + +void +_gedit_document_search_region (GeditDocument     *doc, +			       const GtkTextIter *start, +			       const GtkTextIter *end) +{ +	GeditTextRegion *region; + +	gedit_debug (DEBUG_DOCUMENT); +		 +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (start != NULL); +	g_return_if_fail (end != NULL); +	 +	if (doc->priv->to_search_region == NULL) +		return; + +	/* +	g_print ("U [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start), +					   gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end)); +	*/ +		 +	/* get the subregions not yet highlighted */ +	region = gedit_text_region_intersect (doc->priv->to_search_region,  +					      start, +					      end); +	if (region)  +	{ +		gint i; +		GtkTextIter start_search; +		GtkTextIter end_search; + +		i = gedit_text_region_subregions (region); +		gedit_text_region_nth_subregion (region,  +						 0, +						 &start_search, +						 NULL); + +		gedit_text_region_nth_subregion (region,  +						 i - 1, +						 NULL, +						 &end_search); + +		gedit_text_region_destroy (region, TRUE); + +		gtk_text_iter_order (&start_search, &end_search); + +		search_region (doc, &start_search, &end_search); + +		/* remove the just highlighted region */ +		gedit_text_region_subtract (doc->priv->to_search_region, +					    start,  +					    end); +	} +} + +static void +insert_text_cb (GeditDocument *doc,  +		GtkTextIter   *pos, +		const gchar   *text,  +		gint           length) +{ +	GtkTextIter start; +	GtkTextIter end; + +	gedit_debug (DEBUG_DOCUMENT); +		 +	start = end = *pos; + +	/* +	 * pos is invalidated when +	 * insertion occurs (because the buffer contents change), but the +	 * default signal handler revalidates it to point to the end of the +	 * inserted text  +	 */ +	gtk_text_iter_backward_chars (&start, +				      g_utf8_strlen (text, length)); +				      +	to_search_region_range (doc, &start, &end); +} +						  +static void	 +delete_range_cb (GeditDocument *doc,  +		 GtkTextIter   *start, +		 GtkTextIter   *end) +{ +	GtkTextIter d_start; +	GtkTextIter d_end; + +	gedit_debug (DEBUG_DOCUMENT); +		 +	d_start = *start; +	d_end = *end; +	 +	to_search_region_range (doc, &d_start, &d_end); +} + +void +gedit_document_set_enable_search_highlighting (GeditDocument *doc, +					       gboolean       enable) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	 +	enable = enable != FALSE; +	 +	if ((doc->priv->to_search_region != NULL) == enable) +		return; +	 +	if (doc->priv->to_search_region != NULL) +	{ +		/* Disable search highlighting */ +		if (doc->priv->found_tag != NULL) +		{ +			/* If needed remove the found_tag */ +			GtkTextIter begin; +			GtkTextIter end; +		 +			gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), +						    &begin, +						    &end); + +			gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (doc), +				    		    doc->priv->found_tag, +				    		    &begin, +				    		    &end); +		} +		 +		gedit_text_region_destroy (doc->priv->to_search_region, +					   TRUE); +		doc->priv->to_search_region = NULL; +	} +	else +	{ +		doc->priv->to_search_region = gedit_text_region_new (GTK_TEXT_BUFFER (doc)); +		if (gedit_document_get_can_search_again (doc)) +		{ +			/* If search_text is not empty, highligth all its occurrences */ +			GtkTextIter begin; +			GtkTextIter end; +		 +			gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), +						    &begin, +						    &end); +					     +			to_search_region_range (doc, +						&begin, +						&end); +		} +	} +} + +gboolean +gedit_document_get_enable_search_highlighting (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE); +	 +	return (doc->priv->to_search_region != NULL); +} + +void +gedit_document_set_newline_type (GeditDocument           *doc, +				 GeditDocumentNewlineType newline_type) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	if (doc->priv->newline_type != newline_type) +	{ +		doc->priv->newline_type = newline_type; + +		g_object_notify (G_OBJECT (doc), "newline-type"); +	} +} + +GeditDocumentNewlineType +gedit_document_get_newline_type (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), 0); + +	return doc->priv->newline_type; +} + +void +_gedit_document_set_mount_operation_factory (GeditDocument 	       *doc, +					    GeditMountOperationFactory	callback, +					    gpointer	                userdata) +{ +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	 +	doc->priv->mount_operation_factory = callback; +	doc->priv->mount_operation_userdata = userdata; +} + +GMountOperation * +_gedit_document_create_mount_operation (GeditDocument *doc) +{ +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); +	 +	if (doc->priv->mount_operation_factory == NULL) +		return g_mount_operation_new (); +	else +		return doc->priv->mount_operation_factory (doc,  +						           doc->priv->mount_operation_userdata); +} + +#ifndef ENABLE_GVFS_METADATA +gchar * +gedit_document_get_metadata (GeditDocument *doc, +			     const gchar   *key) +{ +	gchar *value = NULL; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); +	g_return_val_if_fail (key != NULL, NULL); + +	if (!gedit_document_is_untitled (doc)) +	{ +		value = gedit_metadata_manager_get (doc->priv->uri, key); +	} + +	return value; +} + +void +gedit_document_set_metadata (GeditDocument *doc, +			     const gchar   *first_key, +			     ...) +{ +	const gchar *key; +	const gchar *value; +	va_list var_args; + +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (first_key != NULL); + +	if (gedit_document_is_untitled (doc)) +	{ +		/* Can't set metadata for untitled documents */ +		return; +	} + +	va_start (var_args, first_key); + +	for (key = first_key; key; key = va_arg (var_args, const gchar *)) +	{ +		value = va_arg (var_args, const gchar *); +		 +		gedit_metadata_manager_set (doc->priv->uri, +					    key, +					    value); +	} + +	va_end (var_args); +} + +#else + +/** + * gedit_document_get_metadata: + * @doc: a #GeditDocument + * @key: name of the key + * + * Gets the metadata assigned to @key. + * + * Returns: the value assigned to @key. + */ +gchar * +gedit_document_get_metadata (GeditDocument *doc, +			     const gchar   *key) +{ +	gchar *value = NULL; + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); +	g_return_val_if_fail (key != NULL, NULL); + +	if (doc->priv->metadata_info && g_file_info_has_attribute (doc->priv->metadata_info, +								   key)) +	{ +		value = g_strdup (g_file_info_get_attribute_string (doc->priv->metadata_info, +								    key)); +	} + +	return value; +} + +static void +set_attributes_cb (GObject      *source, +		   GAsyncResult *res, +		   gpointer      useless) +{ +	g_file_set_attributes_finish (G_FILE (source), +				      res, +				      NULL, +				      NULL); +} + +/** + * gedit_document_set_metadata: + * @doc: a #GeditDocument + * @first_key: name of the first key to set + * @...: value for the first key, followed optionally by more key/value pairs, + * followed by %NULL. + * + * Sets metadata on a document. + */ +void +gedit_document_set_metadata (GeditDocument *doc, +			     const gchar   *first_key, +			     ...) +{ +	const gchar *key; +	const gchar *value; +	va_list var_args; +	GFileInfo *info; +	GFile *location; + +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (first_key != NULL); + +	info = g_file_info_new (); + +	va_start (var_args, first_key); + +	for (key = first_key; key; key = va_arg (var_args, const gchar *)) +	{ +		value = va_arg (var_args, const gchar *); +		 +		if (value != NULL) +		{ +			g_file_info_set_attribute_string (info, +							  key, value); +		} +		else +		{ +			/* Unset the key */ +			g_file_info_set_attribute (info, key, +						   G_FILE_ATTRIBUTE_TYPE_INVALID, +						   NULL); +		} +	} + +	va_end (var_args); + +	if (doc->priv->metadata_info != NULL) +		g_file_info_copy_into (info, doc->priv->metadata_info); + +	location = gedit_document_get_location (doc); + +	if (location != NULL) +	{ +		g_file_set_attributes_async (location, +					     info, +					     G_FILE_QUERY_INFO_NONE, +					     G_PRIORITY_DEFAULT, +					     NULL, +					     set_attributes_cb, +					     NULL); + +		g_object_unref (location); +	} +	 +	g_object_unref (info); +} +#endif diff --git a/gedit/gedit-document.h b/gedit/gedit-document.h new file mode 100755 index 00000000..cf966b15 --- /dev/null +++ b/gedit/gedit-document.h @@ -0,0 +1,338 @@ +/* + * gedit-document.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi  + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ +  +#ifndef __GEDIT_DOCUMENT_H__ +#define __GEDIT_DOCUMENT_H__ + +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <gtksourceview/gtksourcebuffer.h> + +#include <gedit/gedit-encodings.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_DOCUMENT              (gedit_document_get_type()) +#define GEDIT_DOCUMENT(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_DOCUMENT, GeditDocument)) +#define GEDIT_DOCUMENT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_DOCUMENT, GeditDocumentClass)) +#define GEDIT_IS_DOCUMENT(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_DOCUMENT)) +#define GEDIT_IS_DOCUMENT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT)) +#define GEDIT_DOCUMENT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_DOCUMENT, GeditDocumentClass)) + +#ifdef G_OS_WIN32 +#define GEDIT_METADATA_ATTRIBUTE_POSITION "position" +#define GEDIT_METADATA_ATTRIBUTE_ENCODING "encoding" +#define GEDIT_METADATA_ATTRIBUTE_LANGUAGE "language" +#else +#define GEDIT_METADATA_ATTRIBUTE_POSITION "metadata::gedit-position" +#define GEDIT_METADATA_ATTRIBUTE_ENCODING "metadata::gedit-encoding" +#define GEDIT_METADATA_ATTRIBUTE_LANGUAGE "metadata::gedit-language" +#endif + +typedef enum +{ +	GEDIT_DOCUMENT_NEWLINE_TYPE_LF, +	GEDIT_DOCUMENT_NEWLINE_TYPE_CR, +	GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF +} GeditDocumentNewlineType; + +#ifdef G_OS_WIN32 +#define GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF +#else +#define GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT GEDIT_DOCUMENT_NEWLINE_TYPE_LF +#endif + +typedef enum +{ +	GEDIT_SEARCH_DONT_SET_FLAGS	= 1 << 0,  +	GEDIT_SEARCH_ENTIRE_WORD	= 1 << 1, +	GEDIT_SEARCH_CASE_SENSITIVE	= 1 << 2 + +} GeditSearchFlags; + +/** + * GeditDocumentSaveFlags: + * @GEDIT_DOCUMENT_SAVE_IGNORE_MTIME: save file despite external modifications. + * @GEDIT_DOCUMENT_SAVE_IGNORE_BACKUP: write the file directly without attempting to backup. + * @GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP: preserve previous backup file, needed to support autosaving. + */ +typedef enum +{ +	GEDIT_DOCUMENT_SAVE_IGNORE_MTIME 	= 1 << 0, +	GEDIT_DOCUMENT_SAVE_IGNORE_BACKUP	= 1 << 1, +	GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP	= 1 << 2 +} GeditDocumentSaveFlags; + +/* Private structure type */ +typedef struct _GeditDocumentPrivate    GeditDocumentPrivate; + +/* + * Main object structure + */ +typedef struct _GeditDocument           GeditDocument; +  +struct _GeditDocument +{ +	GtkSourceBuffer buffer; +	 +	/*< private > */ +	GeditDocumentPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditDocumentClass 	GeditDocumentClass; + +struct _GeditDocumentClass +{ +	GtkSourceBufferClass parent_class; + +	/* Signals */ // CHECK: ancora da rivedere + +	void (* cursor_moved)		(GeditDocument    *document); + +	/* Document load */ +	void (* load)			(GeditDocument       *document, +					 const gchar         *uri, +					 const GeditEncoding *encoding, +					 gint                 line_pos, +					 gboolean             create); + +	void (* loading)		(GeditDocument    *document, +					 goffset	   size, +					 goffset	   total_size); + +	void (* loaded)			(GeditDocument    *document, +					 const GError     *error); + +	/* Document save */ +	void (* save)			(GeditDocument          *document, +					 const gchar            *uri, +					 const GeditEncoding    *encoding, +					 GeditDocumentSaveFlags  flags); + +	void (* saving)			(GeditDocument    *document, +					 goffset	   size, +					 goffset	   total_size); + +	void (* saved)  		(GeditDocument    *document, +					 const GError     *error); + +	void (* search_highlight_updated) +					(GeditDocument    *document, +					 GtkTextIter      *start, +					 GtkTextIter      *end); +}; + + +#define GEDIT_DOCUMENT_ERROR gedit_document_error_quark () + +enum +{ +	GEDIT_DOCUMENT_ERROR_EXTERNALLY_MODIFIED, +	GEDIT_DOCUMENT_ERROR_CANT_CREATE_BACKUP, +	GEDIT_DOCUMENT_ERROR_TOO_BIG, +	GEDIT_DOCUMENT_ERROR_ENCODING_AUTO_DETECTION_FAILED, +	GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK, +	GEDIT_DOCUMENT_NUM_ERRORS +}; + +GQuark		 gedit_document_error_quark	(void); + +GType		 gedit_document_get_type      	(void) G_GNUC_CONST; + +GeditDocument   *gedit_document_new 		(void); + +GFile		*gedit_document_get_location	(GeditDocument       *doc); + +gchar		*gedit_document_get_uri 	(GeditDocument       *doc); +void		 gedit_document_set_uri		(GeditDocument       *doc, +						 const gchar 	     *uri); + +gchar		*gedit_document_get_uri_for_display +						(GeditDocument       *doc); +gchar		*gedit_document_get_short_name_for_display +					 	(GeditDocument       *doc); + +void		 gedit_document_set_short_name_for_display +						(GeditDocument       *doc, +						 const gchar         *name); + +gchar		*gedit_document_get_content_type +					 	(GeditDocument       *doc); + +void		 gedit_document_set_content_type +					 	(GeditDocument       *doc, +					 	 const gchar         *content_type); + +gchar		*gedit_document_get_mime_type 	(GeditDocument       *doc); + +gboolean	 gedit_document_get_readonly 	(GeditDocument       *doc); + +void		 gedit_document_load 		(GeditDocument       *doc, +						 const gchar         *uri, +						 const GeditEncoding *encoding, +						 gint                 line_pos, +						 gboolean             create);  + +gboolean	 gedit_document_insert_file	(GeditDocument       *doc, +						 GtkTextIter         *iter,  +						 const gchar         *uri,  +						 const GeditEncoding *encoding); + +gboolean	 gedit_document_load_cancel	(GeditDocument       *doc); + +void		 gedit_document_save 		(GeditDocument       *doc, +						 GeditDocumentSaveFlags flags); + +void		 gedit_document_save_as 	(GeditDocument       *doc,	 +						 const gchar         *uri,  +						 const GeditEncoding *encoding, +						 GeditDocumentSaveFlags flags); + +gboolean	 gedit_document_is_untouched 	(GeditDocument       *doc); +gboolean	 gedit_document_is_untitled 	(GeditDocument       *doc); + +gboolean	 gedit_document_is_local	(GeditDocument       *doc); + +gboolean	 gedit_document_get_deleted	(GeditDocument       *doc); + +gboolean	 gedit_document_goto_line 	(GeditDocument       *doc,  +						 gint                 line); + +gboolean	 gedit_document_goto_line_offset(GeditDocument *doc, +						 gint           line, +						 gint           line_offset); + +void		 gedit_document_set_search_text	(GeditDocument       *doc, +						 const gchar         *text, +						 guint                flags); +						  +gchar		*gedit_document_get_search_text	(GeditDocument       *doc, +						 guint               *flags); + +gboolean	 gedit_document_get_can_search_again +						(GeditDocument       *doc); + +gboolean	 gedit_document_search_forward	(GeditDocument       *doc, +						 const GtkTextIter   *start, +						 const GtkTextIter   *end, +						 GtkTextIter         *match_start, +						 GtkTextIter         *match_end); +						  +gboolean	 gedit_document_search_backward	(GeditDocument       *doc, +						 const GtkTextIter   *start, +						 const GtkTextIter   *end, +						 GtkTextIter         *match_start, +						 GtkTextIter         *match_end); + +gint		 gedit_document_replace_all 	(GeditDocument       *doc, +				            	 const gchar         *find,  +						 const gchar         *replace,  +					    	 guint                flags); + +void 		 gedit_document_set_language 	(GeditDocument       *doc, +						 GtkSourceLanguage   *lang); +GtkSourceLanguage  +		*gedit_document_get_language 	(GeditDocument       *doc); + +const GeditEncoding  +		*gedit_document_get_encoding	(GeditDocument       *doc); + +void		 gedit_document_set_enable_search_highlighting  +						(GeditDocument       *doc, +						 gboolean             enable); + +gboolean	 gedit_document_get_enable_search_highlighting +						(GeditDocument       *doc); + +void		 gedit_document_set_newline_type (GeditDocument           *doc, +						  GeditDocumentNewlineType newline_type); + +GeditDocumentNewlineType +		 gedit_document_get_newline_type (GeditDocument *doc); + +gchar		*gedit_document_get_metadata	(GeditDocument *doc, +						 const gchar   *key); + +void		 gedit_document_set_metadata	(GeditDocument *doc, +						 const gchar   *first_key, +						 ...); + +/*  + * Non exported functions + */ +void		 _gedit_document_set_readonly 	(GeditDocument       *doc, +						 gboolean             readonly); + +glong		 _gedit_document_get_seconds_since_last_save_or_load  +						(GeditDocument       *doc); + +/* Note: this is a sync stat: use only on local files */ +gboolean	_gedit_document_check_externally_modified +						(GeditDocument       *doc); + +void		_gedit_document_search_region   (GeditDocument       *doc, +						 const GtkTextIter   *start, +						 const GtkTextIter   *end); +						   +/* Search macros */ +#define GEDIT_SEARCH_IS_DONT_SET_FLAGS(sflags) ((sflags & GEDIT_SEARCH_DONT_SET_FLAGS) != 0) +#define GEDIT_SEARCH_SET_DONT_SET_FLAGS(sflags,state) ((state == TRUE) ? \ +(sflags |= GEDIT_SEARCH_DONT_SET_FLAGS) : (sflags &= ~GEDIT_SEARCH_DONT_SET_FLAGS)) + +#define GEDIT_SEARCH_IS_ENTIRE_WORD(sflags) ((sflags & GEDIT_SEARCH_ENTIRE_WORD) != 0) +#define GEDIT_SEARCH_SET_ENTIRE_WORD(sflags,state) ((state == TRUE) ? \ +(sflags |= GEDIT_SEARCH_ENTIRE_WORD) : (sflags &= ~GEDIT_SEARCH_ENTIRE_WORD)) + +#define GEDIT_SEARCH_IS_CASE_SENSITIVE(sflags) ((sflags &  GEDIT_SEARCH_CASE_SENSITIVE) != 0) +#define GEDIT_SEARCH_SET_CASE_SENSITIVE(sflags,state) ((state == TRUE) ? \ +(sflags |= GEDIT_SEARCH_CASE_SENSITIVE) : (sflags &= ~GEDIT_SEARCH_CASE_SENSITIVE)) + +typedef GMountOperation *(*GeditMountOperationFactory)(GeditDocument *doc,  +						       gpointer       userdata); + +void		 _gedit_document_set_mount_operation_factory +						(GeditDocument	            *doc, +						 GeditMountOperationFactory  callback, +						 gpointer	             userdata); +GMountOperation +		*_gedit_document_create_mount_operation +						(GeditDocument	     *doc); + +G_END_DECLS + +#endif /* __GEDIT_DOCUMENT_H__ */ diff --git a/gedit/gedit-documents-panel.c b/gedit/gedit-documents-panel.c new file mode 100755 index 00000000..911654b2 --- /dev/null +++ b/gedit/gedit-documents-panel.c @@ -0,0 +1,828 @@ +/* + * gedit-documents-panel.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-documents-panel.h" +#include "gedit-utils.h" +#include "gedit-notebook.h" + +#include <glib/gi18n.h> + +#define GEDIT_DOCUMENTS_PANEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +						  GEDIT_TYPE_DOCUMENTS_PANEL,            \ +						  GeditDocumentsPanelPrivate)) + +struct _GeditDocumentsPanelPrivate +{ +	GeditWindow  *window; + +	GtkWidget    *treeview; +	GtkTreeModel *model; + +	guint         adding_tab : 1; +	guint         is_reodering : 1; +}; + +G_DEFINE_TYPE(GeditDocumentsPanel, gedit_documents_panel, GTK_TYPE_VBOX) + +enum +{ +	PROP_0, +	PROP_WINDOW +}; + +enum +{ +	PIXBUF_COLUMN, +	NAME_COLUMN, +	TAB_COLUMN, +	N_COLUMNS +}; + +#define MAX_DOC_NAME_LENGTH 60 + +static gchar * +tab_get_name (GeditTab *tab) +{ +	GeditDocument *doc; +	gchar *name; +	gchar *docname; +	gchar *tab_name; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + +	doc = gedit_tab_get_document (tab); + +	name = gedit_document_get_short_name_for_display (doc); + +	/* Truncate the name so it doesn't get insanely wide. */ +	docname = gedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); + +	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) +	{ +		if (gedit_document_get_readonly (doc)) +		{ +			tab_name = g_markup_printf_escaped ("<i>%s</i> [<i>%s</i>]", +							    docname, +							    _("Read-Only")); +		} +		else +		{ +			tab_name = g_markup_printf_escaped ("<i>%s</i>",  +							    docname); +		} +	} +	else +	{ +		if (gedit_document_get_readonly (doc)) +		{ +			tab_name = g_markup_printf_escaped ("%s [<i>%s</i>]", +							    docname, +							    _("Read-Only")); +		} +		else +		{ +			tab_name = g_markup_escape_text (docname, -1); +		} +	} + +	g_free (docname); +	g_free (name); + +	return tab_name; +} + +static void +get_iter_from_tab (GeditDocumentsPanel *panel, GeditTab *tab, GtkTreeIter *iter) +{ +	gint num; +	GtkWidget *nb; +	GtkTreePath *path; + +	nb = _gedit_window_get_notebook (panel->priv->window); +	num = gtk_notebook_page_num (GTK_NOTEBOOK (nb), +				     GTK_WIDGET (tab)); + +	path = gtk_tree_path_new_from_indices (num, -1); +	gtk_tree_model_get_iter (panel->priv->model, +		                 iter, +		                 path); +	gtk_tree_path_free (path); +} + +static void +window_active_tab_changed (GeditWindow         *window, +			   GeditTab            *tab, +			   GeditDocumentsPanel *panel) +{	 +	g_return_if_fail (tab != NULL); + +	if (!_gedit_window_is_removing_tabs (window)) +	{ +		GtkTreeIter iter; +		GtkTreeSelection *selection; + +		get_iter_from_tab (panel, tab, &iter); + +		if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (panel->priv->model), +						  &iter)) +		{ +			selection = gtk_tree_view_get_selection ( +					GTK_TREE_VIEW (panel->priv->treeview)); + +			gtk_tree_selection_select_iter (selection, &iter); +		} +	} +} + +static void +refresh_list (GeditDocumentsPanel *panel) +{ +	/* TODO: refresh the list only if the panel is visible */ + +	GList *tabs; +	GList *l; +	GtkWidget *nb; +	GtkListStore *list_store; +	GeditTab *active_tab; + +	/* g_debug ("refresh_list"); */ +	 +	list_store = GTK_LIST_STORE (panel->priv->model); + +	gtk_list_store_clear (list_store); + +	active_tab = gedit_window_get_active_tab (panel->priv->window); + +	nb = _gedit_window_get_notebook (panel->priv->window); + +	tabs = gtk_container_get_children (GTK_CONTAINER (nb)); +	l = tabs; + +	panel->priv->adding_tab = TRUE; +	 +	while (l != NULL) +	{	 +		GdkPixbuf *pixbuf; +		gchar *name; +		GtkTreeIter iter; + +		name = tab_get_name (GEDIT_TAB (l->data)); +		pixbuf = _gedit_tab_get_icon (GEDIT_TAB (l->data)); + +		/* Add a new row to the model */ +		gtk_list_store_append (list_store, &iter); +		gtk_list_store_set (list_store, +				    &iter, +				    PIXBUF_COLUMN, pixbuf, +				    NAME_COLUMN, name, +				    TAB_COLUMN, l->data, +				    -1); + +		g_free (name); +		if (pixbuf != NULL) +			g_object_unref (pixbuf); + +		if (l->data == active_tab) +		{ +			GtkTreeSelection *selection; + +			selection = gtk_tree_view_get_selection ( +					GTK_TREE_VIEW (panel->priv->treeview)); + +			gtk_tree_selection_select_iter (selection, &iter); +		} + +		l = g_list_next (l); +	} +	 +	panel->priv->adding_tab = FALSE; + +	g_list_free (tabs); +} + +static void +sync_name_and_icon (GeditTab            *tab, +		    GParamSpec          *pspec, +		    GeditDocumentsPanel *panel) +{ +	GdkPixbuf *pixbuf; +	gchar *name; +	GtkTreeIter iter; + +	get_iter_from_tab (panel, tab, &iter); + +	name = tab_get_name (tab); +	pixbuf = _gedit_tab_get_icon (tab); + +	gtk_list_store_set (GTK_LIST_STORE (panel->priv->model), +			    &iter, +			    PIXBUF_COLUMN, pixbuf, +			    NAME_COLUMN, name, +			    TAB_COLUMN, tab, +			    -1); + +	g_free (name); +	if (pixbuf != NULL) +		g_object_unref (pixbuf); +} + +static void +window_tab_removed (GeditWindow         *window, +		    GeditTab            *tab, +		    GeditDocumentsPanel *panel) +{ +	g_signal_handlers_disconnect_by_func (tab, +					      G_CALLBACK (sync_name_and_icon), +					      panel); + +	if (_gedit_window_is_removing_tabs (window)) +		gtk_list_store_clear (GTK_LIST_STORE (panel->priv->model)); +	else +		refresh_list (panel); +} + +static void +window_tab_added (GeditWindow         *window, +		  GeditTab            *tab, +		  GeditDocumentsPanel *panel) +{ +	GtkTreeIter iter; +	GtkTreeIter sibling; +	GdkPixbuf *pixbuf; +	gchar *name; + +	g_signal_connect (tab, +			 "notify::name", +			  G_CALLBACK (sync_name_and_icon), +			  panel); + +	g_signal_connect (tab, +			 "notify::state", +			  G_CALLBACK (sync_name_and_icon), +			  panel); + +	get_iter_from_tab (panel, tab, &sibling); + +	panel->priv->adding_tab = TRUE; +	 +	if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (panel->priv->model),  +					  &sibling)) +	{ +		gtk_list_store_insert_after (GTK_LIST_STORE (panel->priv->model), +					     &iter, +					     &sibling); +	} +	else +	{ +		GeditTab *active_tab; + +		gtk_list_store_append (GTK_LIST_STORE (panel->priv->model),  +				       &iter); + +		active_tab = gedit_window_get_active_tab (panel->priv->window); + +		if (tab == active_tab) +		{ +			GtkTreeSelection *selection; + +			selection = gtk_tree_view_get_selection ( +						GTK_TREE_VIEW (panel->priv->treeview)); + +			gtk_tree_selection_select_iter (selection, &iter); +		} +	} + +	name = tab_get_name (tab); +	pixbuf = _gedit_tab_get_icon (tab); + +	gtk_list_store_set (GTK_LIST_STORE (panel->priv->model), +			    &iter, +		            PIXBUF_COLUMN, pixbuf, +		            NAME_COLUMN, name, +		            TAB_COLUMN, tab, +		            -1); + +	g_free (name); +	if (pixbuf != NULL) +		g_object_unref (pixbuf); + +	panel->priv->adding_tab = FALSE; +} + +static void +window_tabs_reordered (GeditWindow         *window, +		       GeditDocumentsPanel *panel) +{ +	if (panel->priv->is_reodering) +		return; + +	refresh_list (panel); +} + +static void +set_window (GeditDocumentsPanel *panel, +	    GeditWindow         *window) +{ +	g_return_if_fail (panel->priv->window == NULL); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	panel->priv->window = g_object_ref (window); + +	g_signal_connect (window, +			  "tab_added", +			  G_CALLBACK (window_tab_added), +			  panel); +	g_signal_connect (window, +			  "tab_removed", +			  G_CALLBACK (window_tab_removed), +			  panel); +	g_signal_connect (window, +			  "tabs_reordered", +			  G_CALLBACK (window_tabs_reordered), +			  panel); +	g_signal_connect (window, +			  "active_tab_changed", +			  G_CALLBACK (window_active_tab_changed), +			  panel); +} + +static void +treeview_cursor_changed (GtkTreeView         *view, +			 GeditDocumentsPanel *panel) +{ +	GtkTreeIter iter; +	GtkTreeSelection *selection; +	gpointer tab; + +	selection = gtk_tree_view_get_selection ( +				GTK_TREE_VIEW (panel->priv->treeview)); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) +	{ +		gtk_tree_model_get (panel->priv->model, +				    &iter, +				    TAB_COLUMN, +				    &tab, +				    -1); + +		if (gedit_window_get_active_tab (panel->priv->window) != tab) +		{ +			gedit_window_set_active_tab (panel->priv->window, +						     GEDIT_TAB (tab)); +		} +	} +} + +static void +gedit_documents_panel_set_property (GObject      *object, +				    guint         prop_id, +				    const GValue *value, +				    GParamSpec   *pspec) +{ +	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object); + +	switch (prop_id) +	{ +		case PROP_WINDOW: +			set_window (panel, g_value_get_object (value)); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_documents_panel_get_property (GObject    *object, +				    guint       prop_id, +				    GValue     *value, +				    GParamSpec *pspec) +{ +	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object); + +	switch (prop_id) +	{ +		case PROP_WINDOW: +			g_value_set_object (value, +					    GEDIT_DOCUMENTS_PANEL_GET_PRIVATE (panel)->window); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_documents_panel_finalize (GObject *object) +{ +	/* GeditDocumentsPanel *tab = GEDIT_DOCUMENTS_PANEL (object); */ +	 +	/* TODO: disconnect signal with window */ + +	G_OBJECT_CLASS (gedit_documents_panel_parent_class)->finalize (object); +} + +static void +gedit_documents_panel_dispose (GObject *object) +{ +	GeditDocumentsPanel *panel = GEDIT_DOCUMENTS_PANEL (object); + +	if (panel->priv->window != NULL) +	{ +		g_object_unref (panel->priv->window); +		panel->priv->window = NULL; +	} + +	G_OBJECT_CLASS (gedit_documents_panel_parent_class)->dispose (object); +} + +static void  +gedit_documents_panel_class_init (GeditDocumentsPanelClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_documents_panel_finalize; +	object_class->dispose = gedit_documents_panel_dispose; +	object_class->get_property = gedit_documents_panel_get_property; +	object_class->set_property = gedit_documents_panel_set_property; + +	g_object_class_install_property (object_class, +					 PROP_WINDOW, +					 g_param_spec_object ("window", +							      "Window", +							      "The GeditWindow this GeditDocumentsPanel is associated with", +							      GEDIT_TYPE_WINDOW, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY | +							      G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof (GeditDocumentsPanelPrivate)); +} + +static GtkTreePath * +get_current_path (GeditDocumentsPanel *panel) +{ +	gint num; +	GtkWidget *nb; +	GtkTreePath *path; + +	nb = _gedit_window_get_notebook (panel->priv->window); +	num = gtk_notebook_get_current_page (GTK_NOTEBOOK (nb)); + +	path = gtk_tree_path_new_from_indices (num, -1); + +	return path; +} + +static void +menu_position (GtkMenu             *menu, +	       gint                *x, +	       gint                *y, +	       gboolean            *push_in, +	       GeditDocumentsPanel *panel) +{ +	GtkTreePath *path; +	GdkRectangle rect; +	gint wx, wy; +	GtkRequisition requisition; +	GtkWidget *w; + +	w = panel->priv->treeview; + +	path = get_current_path (panel); + +	gtk_tree_view_get_cell_area (GTK_TREE_VIEW (w), +				     path, +				     NULL, +				     &rect); + +	wx = rect.x; +	wy = rect.y; + +	gdk_window_get_origin (w->window, x, y); +	 +	gtk_widget_size_request (GTK_WIDGET (menu), &requisition); + +	if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL) +	{ +		*x += w->allocation.x + w->allocation.width - requisition.width - 10; +	} +	else +	{ +		*x += w->allocation.x + 10; +	} + +	wy = MAX (*y + 5, *y + wy + 5); +	wy = MIN (wy, *y + w->allocation.height - requisition.height - 5); +	 +	*y = wy; + +	*push_in = TRUE; +} + +static gboolean +show_popup_menu (GeditDocumentsPanel *panel, +		 GdkEventButton      *event) +{ +	GtkWidget *menu; + +	menu = gtk_ui_manager_get_widget (gedit_window_get_ui_manager (panel->priv->window), +					 "/NotebookPopup"); +	g_return_val_if_fail (menu != NULL, FALSE); + +	if (event != NULL) +	{ +		gtk_menu_popup (GTK_MENU (menu), +				NULL, +				NULL, +				NULL, +				NULL, +				event->button, +				event->time); +	} +	else +	{ +		gtk_menu_popup (GTK_MENU (menu), +				NULL, +				NULL, +				(GtkMenuPositionFunc) menu_position, +				panel, +				0, +				gtk_get_current_event_time ()); + +		gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE); +	} + +	return TRUE; +} + +static gboolean +panel_button_press_event (GtkTreeView         *treeview, +			  GdkEventButton      *event, +			  GeditDocumentsPanel *panel) +{ +	if ((GDK_BUTTON_PRESS == event->type) && (3 == event->button)) +	{ +		GtkTreePath* path = NULL; + +		if (event->window == gtk_tree_view_get_bin_window (treeview)) +		{ +			/* Change the cursor position */ +			if (gtk_tree_view_get_path_at_pos (treeview, +							   event->x, +							   event->y, +							   &path, +							   NULL, +							   NULL, +							   NULL)) +			{				 + +				gtk_tree_view_set_cursor (treeview, +							  path, +							  NULL, +							  FALSE); + +				gtk_tree_path_free (path); + +				/* A row exists at mouse position */ +				return show_popup_menu (panel, event); +			} +		} +	} + +	return FALSE; +} + +static gboolean +panel_popup_menu (GtkWidget           *treeview, +		  GeditDocumentsPanel *panel) +{ +	/* Only respond if the treeview is the actual focus */ +	if (gtk_window_get_focus (GTK_WINDOW (panel->priv->window)) == treeview) +	{ +		return show_popup_menu (panel, NULL); +	} + +	return FALSE; +} + +static gboolean +treeview_query_tooltip (GtkWidget  *widget, +			gint        x, +			gint        y, +			gboolean    keyboard_tip, +			GtkTooltip *tooltip, +			gpointer    data) +{ +	GtkTreeIter iter; +	GtkTreeView *tree_view = GTK_TREE_VIEW (widget); +	GtkTreeModel *model = gtk_tree_view_get_model (tree_view); +	GtkTreePath *path = NULL; +	gpointer *tab; +	gchar *tip; + +	if (keyboard_tip) +	{ +		gtk_tree_view_get_cursor (tree_view, &path, NULL); + +		if (path == NULL) +		{ +			return FALSE; +		} +	} +	else +	{ +		gint bin_x, bin_y; + +		gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, +								   x, y, +								   &bin_x, &bin_y); + +		if (!gtk_tree_view_get_path_at_pos (tree_view, +						    bin_x, bin_y, +						    &path, +						    NULL, NULL, NULL)) +		{ +			return FALSE; +		} +	} + +	gtk_tree_model_get_iter (model, &iter, path); +	gtk_tree_model_get (model, +			    &iter, +			    TAB_COLUMN, +			    &tab, +			    -1); + +	tip = _gedit_tab_get_tooltips (GEDIT_TAB (tab)); +	gtk_tooltip_set_markup (tooltip, tip); + +	g_free (tip); +	gtk_tree_path_free (path); + +	return TRUE; +} + +static void +treeview_row_inserted (GtkTreeModel        *tree_model, +		       GtkTreePath         *path, +		       GtkTreeIter         *iter, +		       GeditDocumentsPanel *panel) +{ +	GeditTab *tab; +	gint *indeces; +	GtkWidget *nb; +	gint old_position; +	gint new_position; +	 +	if (panel->priv->adding_tab) +		return; +		 +	tab = gedit_window_get_active_tab (panel->priv->window); +	g_return_if_fail (tab != NULL); + +	panel->priv->is_reodering = TRUE; +	 +	indeces = gtk_tree_path_get_indices (path); +	 +	/* g_debug ("New Index: %d (path: %s)", indeces[0], gtk_tree_path_to_string (path));*/ +	 +	nb = _gedit_window_get_notebook (panel->priv->window); + +	new_position = indeces[0]; +	old_position = gtk_notebook_page_num (GTK_NOTEBOOK (nb), +				    	      GTK_WIDGET (tab)); +	if (new_position > old_position) +		new_position = MAX (0, new_position - 1); +		 +	gedit_notebook_reorder_tab (GEDIT_NOTEBOOK (nb), +				    tab, +				    new_position); + +	panel->priv->is_reodering = FALSE; +} + +static void +gedit_documents_panel_init (GeditDocumentsPanel *panel) +{ +	GtkWidget 		*sw; +	GtkTreeViewColumn	*column; +	GtkCellRenderer 	*cell; +	GtkTreeSelection 	*selection; + +	panel->priv = GEDIT_DOCUMENTS_PANEL_GET_PRIVATE (panel); +	 +	panel->priv->adding_tab = FALSE; +	panel->priv->is_reodering = FALSE; +	 +	/* Create the scrolled window */ +	sw = gtk_scrolled_window_new (NULL, NULL); +	g_return_if_fail (sw != NULL); +	 +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), +					GTK_POLICY_AUTOMATIC, +					GTK_POLICY_AUTOMATIC); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), +                                             GTK_SHADOW_IN); +	gtk_widget_show (sw); +	gtk_box_pack_start (GTK_BOX (panel), sw, TRUE, TRUE, 0); +	 +	/* Create the empty model */ +	panel->priv->model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, +								 GDK_TYPE_PIXBUF, +								 G_TYPE_STRING, +								 G_TYPE_POINTER)); + +	/* Create the treeview */ +	panel->priv->treeview = gtk_tree_view_new_with_model (panel->priv->model); +	g_object_unref (G_OBJECT (panel->priv->model)); +	gtk_container_add (GTK_CONTAINER (sw), panel->priv->treeview); +	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (panel->priv->treeview), FALSE); +	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (panel->priv->treeview), TRUE); + +	g_object_set (panel->priv->treeview, "has-tooltip", TRUE, NULL); + +	gtk_widget_show (panel->priv->treeview); +	 +	column = gtk_tree_view_column_new (); +	gtk_tree_view_column_set_title (column, _("Documents")); + +	cell = gtk_cell_renderer_pixbuf_new (); +	gtk_tree_view_column_pack_start (column, cell, FALSE); +	gtk_tree_view_column_add_attribute (column, cell, "pixbuf", PIXBUF_COLUMN); +	cell = gtk_cell_renderer_text_new (); +	gtk_tree_view_column_pack_start (column, cell, TRUE); +	gtk_tree_view_column_add_attribute (column, cell, "markup", NAME_COLUMN); + +	gtk_tree_view_append_column (GTK_TREE_VIEW (panel->priv->treeview), +				     column); +				      +	selection = gtk_tree_view_get_selection ( +			GTK_TREE_VIEW (panel->priv->treeview)); + +	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); +	 +	g_signal_connect (panel->priv->treeview, +			  "cursor_changed", +			  G_CALLBACK (treeview_cursor_changed), +			  panel); +	g_signal_connect (panel->priv->treeview, +			  "button-press-event", +			  G_CALLBACK (panel_button_press_event), +			  panel); +	g_signal_connect (panel->priv->treeview, +			  "popup-menu", +			  G_CALLBACK (panel_popup_menu), +			  panel); +	g_signal_connect (panel->priv->treeview, +			  "query-tooltip", +			  G_CALLBACK (treeview_query_tooltip), +			  NULL); + +	g_signal_connect (panel->priv->model, +			  "row-inserted", +			  G_CALLBACK (treeview_row_inserted), +			  panel); +} + +GtkWidget * +gedit_documents_panel_new (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_DOCUMENTS_PANEL, +					 "window", window, +					 NULL)); +} diff --git a/gedit/gedit-documents-panel.h b/gedit/gedit-documents-panel.h new file mode 100755 index 00000000..b6191bf8 --- /dev/null +++ b/gedit/gedit-documents-panel.h @@ -0,0 +1,85 @@ +/* + * gedit-documents-panel.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_DOCUMENTS_PANEL_H__ +#define __GEDIT_DOCUMENTS_PANEL_H__ + +#include <gtk/gtk.h> + +#include <gedit/gedit-window.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_DOCUMENTS_PANEL              (gedit_documents_panel_get_type()) +#define GEDIT_DOCUMENTS_PANEL(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_DOCUMENTS_PANEL, GeditDocumentsPanel)) +#define GEDIT_DOCUMENTS_PANEL_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_DOCUMENTS_PANEL, GeditDocumentsPanelClass)) +#define GEDIT_IS_DOCUMENTS_PANEL(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_DOCUMENTS_PANEL)) +#define GEDIT_IS_DOCUMENTS_PANEL_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENTS_PANEL)) +#define GEDIT_DOCUMENTS_PANEL_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_DOCUMENTS_PANEL, GeditDocumentsPanelClass)) + +/* Private structure type */ +typedef struct _GeditDocumentsPanelPrivate GeditDocumentsPanelPrivate; + +/* + * Main object structure + */ +typedef struct _GeditDocumentsPanel GeditDocumentsPanel; + +struct _GeditDocumentsPanel  +{ +	GtkVBox vbox; + +	/*< private > */ +	GeditDocumentsPanelPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditDocumentsPanelClass GeditDocumentsPanelClass; + +struct _GeditDocumentsPanelClass  +{ +	GtkVBoxClass parent_class; +}; + +/* + * Public methods + */ +GType 		 gedit_documents_panel_get_type	(void) G_GNUC_CONST; + +GtkWidget	*gedit_documents_panel_new 	(GeditWindow *window); + +G_END_DECLS + +#endif  /* __GEDIT_DOCUMENTS_PANEL_H__  */ diff --git a/gedit/gedit-encodings-combo-box.c b/gedit/gedit-encodings-combo-box.c new file mode 100755 index 00000000..1626bb97 --- /dev/null +++ b/gedit/gedit-encodings-combo-box.c @@ -0,0 +1,468 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-encodings-combo-box.c + * This file is part of gedit + * + * Copyright (C) 2003-2005 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2003-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id: gedit-encodings-combo-box.c 6112 2008-01-23 08:26:24Z sfre $ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <gedit/gedit-encodings-combo-box.h> +#include <gedit/gedit-prefs-manager.h> +#include <gedit/dialogs/gedit-encodings-dialog.h> + +#define ENCODING_KEY "Enconding" + +#define GEDIT_ENCODINGS_COMBO_BOX_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),	\ +							GEDIT_TYPE_ENCODINGS_COMBO_BOX,	\ +							GeditEncodingsComboBoxPrivate)) + +struct _GeditEncodingsComboBoxPrivate +{ +	GtkListStore *store; +	glong changed_id; + +	guint activated_item; + +	guint save_mode : 1; +}; + +enum +{ +	NAME_COLUMN, +	ENCODING_COLUMN, +	ADD_COLUMN, +	N_COLUMNS +}; + +/* Properties */ +enum +{ +	PROP_0, +	PROP_SAVE_MODE +}; + + +G_DEFINE_TYPE(GeditEncodingsComboBox, gedit_encodings_combo_box, GTK_TYPE_COMBO_BOX) + +static void	  update_menu 		(GeditEncodingsComboBox       *combo_box); + +static void +gedit_encodings_combo_box_set_property (GObject    *object, +					guint       prop_id, +					const       GValue *value, +					GParamSpec *pspec) +{ +	GeditEncodingsComboBox *combo; + +	combo = GEDIT_ENCODINGS_COMBO_BOX (object); + +	switch (prop_id) +	{ +		case PROP_SAVE_MODE: +			combo->priv->save_mode = g_value_get_boolean (value); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_encodings_combo_box_get_property (GObject    *object, +					guint       prop_id, +					GValue 	   *value, +					GParamSpec *pspec) +{ +	GeditEncodingsComboBox *combo; + +	combo = GEDIT_ENCODINGS_COMBO_BOX (object); + +	switch (prop_id) +	{	 +		case PROP_SAVE_MODE: +			g_value_set_boolean (value, combo->priv->save_mode); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_encodings_combo_box_dispose (GObject *object) +{ +	GeditEncodingsComboBox *combo = GEDIT_ENCODINGS_COMBO_BOX (object); + +	if (combo->priv->store != NULL) +	{ +		g_object_unref (combo->priv->store); +		combo->priv->store = NULL; +	} + +	G_OBJECT_CLASS (gedit_encodings_combo_box_parent_class)->dispose (object); +} + +static void +gedit_encodings_combo_box_class_init (GeditEncodingsComboBoxClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->set_property = gedit_encodings_combo_box_set_property; +	object_class->get_property = gedit_encodings_combo_box_get_property; +	object_class->dispose = gedit_encodings_combo_box_dispose; + +	g_object_class_install_property (object_class, +					 PROP_SAVE_MODE, +					 g_param_spec_boolean ("save-mode", +							       "Save Mode", +							       "Save Mode", +							       FALSE, +							       G_PARAM_READWRITE | +							       G_PARAM_CONSTRUCT | +							       G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof (GeditEncodingsComboBoxPrivate)); +} + +static void +dialog_response_cb (GtkDialog              *dialog, +                    gint                    response_id, +                    GeditEncodingsComboBox *menu) +{ +	if (response_id == GTK_RESPONSE_OK) +	{ +		update_menu (menu); +	} + +	gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +add_or_remove (GeditEncodingsComboBox *menu, +	       GtkTreeModel           *model) +{ +	GtkTreeIter iter; +	gboolean add_item = FALSE; + +	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (menu), &iter)) +	{ +		gtk_tree_model_get (model, &iter, +				    ADD_COLUMN, &add_item, +				    -1); +	} + +	if (!add_item) +	{ +		menu->priv->activated_item = gtk_combo_box_get_active (GTK_COMBO_BOX (menu)); +	} +	else +	{ +		GtkWidget *dialog; + +		GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu)); + +#if !GTK_CHECK_VERSION (2, 18, 0) +		if (!GTK_WIDGET_TOPLEVEL (toplevel)) +#else +		if (!gtk_widget_is_toplevel (toplevel)) +#endif +			toplevel = NULL; + +		g_signal_handler_block (menu, menu->priv->changed_id); +		gtk_combo_box_set_active (GTK_COMBO_BOX (menu), +					  menu->priv->activated_item); +		g_signal_handler_unblock (menu, menu->priv->changed_id); + +		dialog = gedit_encodings_dialog_new(); + +		if (toplevel != NULL) +		{ +			GtkWindowGroup *wg; + +			gtk_window_set_transient_for (GTK_WINDOW (dialog), +						      GTK_WINDOW (toplevel)); + +			wg = GTK_WINDOW (toplevel)->group; +			if (wg == NULL) +			{ +				wg = gtk_window_group_new (); +				gtk_window_group_add_window (wg, +							     GTK_WINDOW (toplevel)); +			} + +			gtk_window_group_add_window (wg, +						     GTK_WINDOW (dialog)); +		} + +		gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + +		g_signal_connect (dialog, +				  "response", +				  G_CALLBACK (dialog_response_cb), +				  menu); + +		gtk_widget_show (dialog); +	} +} + +static gboolean +separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) +{ +	gchar *str; +	gboolean ret; + +	gtk_tree_model_get (model, iter, NAME_COLUMN, &str, -1); +	ret = (str == NULL || *str == '\0'); +	g_free (str); + +	return ret; +} + +static void +update_menu (GeditEncodingsComboBox *menu) +{ +	GtkListStore *store; +	GtkTreeIter iter; +	GSList *encodings, *l; +	gchar *str; +	const GeditEncoding *utf8_encoding; +	const GeditEncoding *current_encoding; + +	store = menu->priv->store; + +	/* Unset the previous model */ +	g_signal_handler_block (menu, menu->priv->changed_id); +	gtk_list_store_clear (store); +	gtk_combo_box_set_model (GTK_COMBO_BOX (menu), +				 NULL); + +	utf8_encoding = gedit_encoding_get_utf8 (); +	current_encoding = gedit_encoding_get_current (); + +	if (!menu->priv->save_mode) +	{ +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store, &iter, +				    NAME_COLUMN, _("Automatically Detected"), +				    ENCODING_COLUMN, NULL, +				    ADD_COLUMN, FALSE, +				    -1); + +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store, &iter, +				    NAME_COLUMN, "", +				    ENCODING_COLUMN, NULL, +				    ADD_COLUMN, FALSE, +				    -1); +	} + +	if (current_encoding != utf8_encoding) +		str = gedit_encoding_to_string (utf8_encoding); +	else +		str = g_strdup_printf (_("Current Locale (%s)"), +				       gedit_encoding_get_charset (utf8_encoding)); + +	gtk_list_store_append (store, &iter); +	gtk_list_store_set (store, &iter, +			    NAME_COLUMN, str, +			    ENCODING_COLUMN, utf8_encoding, +			    ADD_COLUMN, FALSE, +			    -1); + +	g_free (str); + +	if ((utf8_encoding != current_encoding) && +	    (current_encoding != NULL)) +	{ +		str = g_strdup_printf (_("Current Locale (%s)"), +				       gedit_encoding_get_charset (current_encoding)); + +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store, &iter, +				    NAME_COLUMN, str, +				    ENCODING_COLUMN, current_encoding, +				    ADD_COLUMN, FALSE, +				    -1); + +		g_free (str); +	} + +	encodings = gedit_prefs_manager_get_shown_in_menu_encodings (); + +	for (l = encodings; l != NULL; l = g_slist_next (l)) +	{ +		const GeditEncoding *enc = (const GeditEncoding *)l->data; + +		if ((enc != current_encoding) && +		    (enc != utf8_encoding) && +		    (enc != NULL)) +		{ +			str = gedit_encoding_to_string (enc); + +			gtk_list_store_append (store, &iter); +			gtk_list_store_set (store, &iter, +					    NAME_COLUMN, str, +					    ENCODING_COLUMN, enc, +					    ADD_COLUMN, FALSE, +					    -1); + +			g_free (str); +		} +	} + +	g_slist_free (encodings); + +	if (gedit_prefs_manager_shown_in_menu_encodings_can_set ()) +	{ +		gtk_list_store_append (store, &iter); +		/* separator */ +		gtk_list_store_set (store, &iter, +				    NAME_COLUMN, "", +				    ENCODING_COLUMN, NULL, +				    ADD_COLUMN, FALSE, +				    -1); + +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store, &iter, +				    NAME_COLUMN, _("Add or Remove..."), +				    ENCODING_COLUMN, NULL, +				    ADD_COLUMN, TRUE, +				    -1); +	} + +	/* set the model back */ +	gtk_combo_box_set_model (GTK_COMBO_BOX (menu), +				 GTK_TREE_MODEL (menu->priv->store)); +	gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0); + +	g_signal_handler_unblock (menu, menu->priv->changed_id); +} + +static void +gedit_encodings_combo_box_init (GeditEncodingsComboBox *menu) +{ +	GtkCellRenderer *text_renderer; + +	menu->priv = GEDIT_ENCODINGS_COMBO_BOX_GET_PRIVATE (menu); + +	menu->priv->store = gtk_list_store_new (N_COLUMNS, +						G_TYPE_STRING, +						G_TYPE_POINTER, +						G_TYPE_BOOLEAN); + +	/* Setup up the cells */ +	text_renderer = gtk_cell_renderer_text_new (); +	gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (menu), +				  text_renderer, TRUE); + +	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (menu), +					text_renderer, +					"text", +					NAME_COLUMN, +					NULL); + +	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu), +					      separator_func, NULL, +					      NULL); + +	menu->priv->changed_id = g_signal_connect (menu, "changed", +						   G_CALLBACK (add_or_remove), +						   menu->priv->store); + +	update_menu (menu); +} + +GtkWidget * +gedit_encodings_combo_box_new (gboolean save_mode) +{ +	return g_object_new (GEDIT_TYPE_ENCODINGS_COMBO_BOX, +			     "save_mode", save_mode, +			     NULL); +} + +const GeditEncoding * +gedit_encodings_combo_box_get_selected_encoding (GeditEncodingsComboBox *menu) +{ +	GtkTreeIter iter; + +	g_return_val_if_fail (GEDIT_IS_ENCODINGS_COMBO_BOX (menu), NULL); + +	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (menu), &iter)) +	{ +		const GeditEncoding *ret; +		GtkTreeModel *model; + +		model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu)); + +		gtk_tree_model_get (model, &iter, +				    ENCODING_COLUMN, &ret, +				    -1); + +		return ret; +	} + +	return NULL; +} + +void +gedit_encodings_combo_box_set_selected_encoding (GeditEncodingsComboBox *menu, +						 const GeditEncoding    *encoding) +{ +	GtkTreeIter iter; +	GtkTreeModel *model; +	gboolean b; +	g_return_if_fail (GEDIT_IS_ENCODINGS_COMBO_BOX (menu)); +	g_return_if_fail (GTK_IS_COMBO_BOX (menu)); + +	model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu)); +	b = gtk_tree_model_get_iter_first (model, &iter); + +	while (b) +	{ +		const GeditEncoding *enc; + +		gtk_tree_model_get (model, &iter, +				    ENCODING_COLUMN, &enc, +				    -1); + +		if (enc == encoding) +		{ +			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (menu), +						       &iter); + +			return; +		} + +		b = gtk_tree_model_iter_next (model, &iter); +	} +} diff --git a/gedit/gedit-encodings-combo-box.h b/gedit/gedit-encodings-combo-box.h new file mode 100755 index 00000000..586f8007 --- /dev/null +++ b/gedit/gedit-encodings-combo-box.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-encodings-combo-box.h + * This file is part of gedit + * + * Copyright (C) 2003-2005 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2003-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id: gedit-encodings-option-menu.h 4429 2005-12-12 17:28:04Z pborelli $ + */ + +#ifndef __GEDIT_ENCODINGS_COMBO_BOX_H__ +#define __GEDIT_ENCODINGS_COMBO_BOX_H__ + +#include <gtk/gtkoptionmenu.h> +#include <gedit/gedit-encodings.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_ENCODINGS_COMBO_BOX             (gedit_encodings_combo_box_get_type ()) +#define GEDIT_ENCODINGS_COMBO_BOX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_ENCODINGS_COMBO_BOX, GeditEncodingsComboBox)) +#define GEDIT_ENCODINGS_COMBO_BOX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_ENCODINGS_COMBO_BOX, GeditEncodingsComboBoxClass)) +#define GEDIT_IS_ENCODINGS_COMBO_BOX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_ENCODINGS_COMBO_BOX)) +#define GEDIT_IS_ENCODINGS_COMBO_BOX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_ENCODINGS_COMBO_BOX)) +#define GEDIT_ENCODINGS_COMBO_BOX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_ENCODINGS_COMBO_BOX, GeditEncodingsComboBoxClass)) + + +typedef struct _GeditEncodingsComboBox 	GeditEncodingsComboBox; +typedef struct _GeditEncodingsComboBoxClass 	GeditEncodingsComboBoxClass; + +typedef struct _GeditEncodingsComboBoxPrivate	GeditEncodingsComboBoxPrivate; + +struct _GeditEncodingsComboBox +{ +	GtkComboBox			 parent; + +	GeditEncodingsComboBoxPrivate	*priv; +}; + +struct _GeditEncodingsComboBoxClass +{ +	GtkComboBoxClass		 parent_class; +}; + +GType		     gedit_encodings_combo_box_get_type		(void) G_GNUC_CONST; + +/* Constructor */ +GtkWidget 	    *gedit_encodings_combo_box_new 			(gboolean save_mode); + +const GeditEncoding *gedit_encodings_combo_box_get_selected_encoding	(GeditEncodingsComboBox *menu); +void		     gedit_encodings_combo_box_set_selected_encoding	(GeditEncodingsComboBox *menu, +									 const GeditEncoding      *encoding); + +G_END_DECLS + +#endif /* __GEDIT_ENCODINGS_COMBO_BOX_H__ */ + + diff --git a/gedit/gedit-encodings.c b/gedit/gedit-encodings.c new file mode 100755 index 00000000..cf97bf56 --- /dev/null +++ b/gedit/gedit-encodings.c @@ -0,0 +1,473 @@ +/* + * gedit-encodings.c + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> + +#include "gedit-encodings.h" + + +struct _GeditEncoding +{ +	gint   index; +	const gchar *charset; +	const gchar *name; +}; + +/*  + * The original versions of the following tables are taken from profterm  + * + * Copyright (C) 2002 Red Hat, Inc. + */ + +typedef enum +{ + +  GEDIT_ENCODING_ISO_8859_1, +  GEDIT_ENCODING_ISO_8859_2, +  GEDIT_ENCODING_ISO_8859_3, +  GEDIT_ENCODING_ISO_8859_4, +  GEDIT_ENCODING_ISO_8859_5, +  GEDIT_ENCODING_ISO_8859_6, +  GEDIT_ENCODING_ISO_8859_7, +  GEDIT_ENCODING_ISO_8859_8, +  GEDIT_ENCODING_ISO_8859_9, +  GEDIT_ENCODING_ISO_8859_10, +  GEDIT_ENCODING_ISO_8859_13, +  GEDIT_ENCODING_ISO_8859_14, +  GEDIT_ENCODING_ISO_8859_15, +  GEDIT_ENCODING_ISO_8859_16, + +  GEDIT_ENCODING_UTF_7, +  GEDIT_ENCODING_UTF_16, +  GEDIT_ENCODING_UTF_16_BE, +  GEDIT_ENCODING_UTF_16_LE, +  GEDIT_ENCODING_UTF_32, +  GEDIT_ENCODING_UCS_2, +  GEDIT_ENCODING_UCS_4, + +  GEDIT_ENCODING_ARMSCII_8, +  GEDIT_ENCODING_BIG5, +  GEDIT_ENCODING_BIG5_HKSCS, +  GEDIT_ENCODING_CP_866, + +  GEDIT_ENCODING_EUC_JP, +  GEDIT_ENCODING_EUC_JP_MS, +  GEDIT_ENCODING_CP932, +  GEDIT_ENCODING_EUC_KR, +  GEDIT_ENCODING_EUC_TW, + +  GEDIT_ENCODING_GB18030, +  GEDIT_ENCODING_GB2312, +  GEDIT_ENCODING_GBK, +  GEDIT_ENCODING_GEOSTD8, + +  GEDIT_ENCODING_IBM_850, +  GEDIT_ENCODING_IBM_852, +  GEDIT_ENCODING_IBM_855, +  GEDIT_ENCODING_IBM_857, +  GEDIT_ENCODING_IBM_862, +  GEDIT_ENCODING_IBM_864, + +  GEDIT_ENCODING_ISO_2022_JP, +  GEDIT_ENCODING_ISO_2022_KR, +  GEDIT_ENCODING_ISO_IR_111, +  GEDIT_ENCODING_JOHAB, +  GEDIT_ENCODING_KOI8_R, +  GEDIT_ENCODING_KOI8__R, +  GEDIT_ENCODING_KOI8_U, +   +  GEDIT_ENCODING_SHIFT_JIS, +  GEDIT_ENCODING_TCVN, +  GEDIT_ENCODING_TIS_620, +  GEDIT_ENCODING_UHC, +  GEDIT_ENCODING_VISCII, + +  GEDIT_ENCODING_WINDOWS_1250, +  GEDIT_ENCODING_WINDOWS_1251, +  GEDIT_ENCODING_WINDOWS_1252, +  GEDIT_ENCODING_WINDOWS_1253, +  GEDIT_ENCODING_WINDOWS_1254, +  GEDIT_ENCODING_WINDOWS_1255, +  GEDIT_ENCODING_WINDOWS_1256, +  GEDIT_ENCODING_WINDOWS_1257, +  GEDIT_ENCODING_WINDOWS_1258, + +  GEDIT_ENCODING_LAST, + +  GEDIT_ENCODING_UTF_8, +  GEDIT_ENCODING_UNKNOWN +   +} GeditEncodingIndex; + +static const GeditEncoding utf8_encoding =  { +	GEDIT_ENCODING_UTF_8, +	"UTF-8", +	N_("Unicode") +}; + +/* initialized in gedit_encoding_lazy_init() */ +static GeditEncoding unknown_encoding = { +	GEDIT_ENCODING_UNKNOWN, +	NULL,  +	NULL  +}; + +static const GeditEncoding encodings [] = { + +  { GEDIT_ENCODING_ISO_8859_1, +    "ISO-8859-1", N_("Western") }, +  { GEDIT_ENCODING_ISO_8859_2, +   "ISO-8859-2", N_("Central European") }, +  { GEDIT_ENCODING_ISO_8859_3, +    "ISO-8859-3", N_("South European") }, +  { GEDIT_ENCODING_ISO_8859_4, +    "ISO-8859-4", N_("Baltic") }, +  { GEDIT_ENCODING_ISO_8859_5, +    "ISO-8859-5", N_("Cyrillic") }, +  { GEDIT_ENCODING_ISO_8859_6, +    "ISO-8859-6", N_("Arabic") }, +  { GEDIT_ENCODING_ISO_8859_7, +    "ISO-8859-7", N_("Greek") }, +  { GEDIT_ENCODING_ISO_8859_8, +    "ISO-8859-8", N_("Hebrew Visual") }, +  { GEDIT_ENCODING_ISO_8859_9, +    "ISO-8859-9", N_("Turkish") }, +  { GEDIT_ENCODING_ISO_8859_10, +    "ISO-8859-10", N_("Nordic") }, +  { GEDIT_ENCODING_ISO_8859_13, +    "ISO-8859-13", N_("Baltic") }, +  { GEDIT_ENCODING_ISO_8859_14, +    "ISO-8859-14", N_("Celtic") }, +  { GEDIT_ENCODING_ISO_8859_15, +    "ISO-8859-15", N_("Western") }, +  { GEDIT_ENCODING_ISO_8859_16, +    "ISO-8859-16", N_("Romanian") }, + +  { GEDIT_ENCODING_UTF_7, +    "UTF-7", N_("Unicode") }, +  { GEDIT_ENCODING_UTF_16, +    "UTF-16", N_("Unicode") }, +  { GEDIT_ENCODING_UTF_16_BE, +    "UTF-16BE", N_("Unicode") }, +  { GEDIT_ENCODING_UTF_16_LE, +    "UTF-16LE", N_("Unicode") }, +  { GEDIT_ENCODING_UTF_32, +    "UTF-32", N_("Unicode") }, +  { GEDIT_ENCODING_UCS_2, +    "UCS-2", N_("Unicode") }, +  { GEDIT_ENCODING_UCS_4, +    "UCS-4", N_("Unicode") }, + +  { GEDIT_ENCODING_ARMSCII_8, +    "ARMSCII-8", N_("Armenian") }, +  { GEDIT_ENCODING_BIG5, +    "BIG5", N_("Chinese Traditional") }, +  { GEDIT_ENCODING_BIG5_HKSCS, +    "BIG5-HKSCS", N_("Chinese Traditional") }, +  { GEDIT_ENCODING_CP_866, +    "CP866", N_("Cyrillic/Russian") }, + +  { GEDIT_ENCODING_EUC_JP, +    "EUC-JP", N_("Japanese") }, +  { GEDIT_ENCODING_EUC_JP_MS, +    "EUC-JP-MS", N_("Japanese") }, +  { GEDIT_ENCODING_CP932, +    "CP932", N_("Japanese") }, + +  { GEDIT_ENCODING_EUC_KR, +    "EUC-KR", N_("Korean") }, +  { GEDIT_ENCODING_EUC_TW, +    "EUC-TW", N_("Chinese Traditional") }, + +  { GEDIT_ENCODING_GB18030, +    "GB18030", N_("Chinese Simplified") }, +  { GEDIT_ENCODING_GB2312, +    "GB2312", N_("Chinese Simplified") }, +  { GEDIT_ENCODING_GBK, +    "GBK", N_("Chinese Simplified") }, +  { GEDIT_ENCODING_GEOSTD8, +    "GEORGIAN-ACADEMY", N_("Georgian") }, /* FIXME GEOSTD8 ? */ + +  { GEDIT_ENCODING_IBM_850, +    "IBM850", N_("Western") }, +  { GEDIT_ENCODING_IBM_852, +    "IBM852", N_("Central European") }, +  { GEDIT_ENCODING_IBM_855, +    "IBM855", N_("Cyrillic") }, +  { GEDIT_ENCODING_IBM_857, +    "IBM857", N_("Turkish") }, +  { GEDIT_ENCODING_IBM_862, +    "IBM862", N_("Hebrew") }, +  { GEDIT_ENCODING_IBM_864, +    "IBM864", N_("Arabic") }, + +  { GEDIT_ENCODING_ISO_2022_JP, +    "ISO-2022-JP", N_("Japanese") }, +  { GEDIT_ENCODING_ISO_2022_KR, +    "ISO-2022-KR", N_("Korean") }, +  { GEDIT_ENCODING_ISO_IR_111, +    "ISO-IR-111", N_("Cyrillic") }, +  { GEDIT_ENCODING_JOHAB, +    "JOHAB", N_("Korean") }, +  { GEDIT_ENCODING_KOI8_R, +    "KOI8R", N_("Cyrillic") }, +  { GEDIT_ENCODING_KOI8__R, +    "KOI8-R", N_("Cyrillic") }, +  { GEDIT_ENCODING_KOI8_U, +    "KOI8U", N_("Cyrillic/Ukrainian") }, +   +  { GEDIT_ENCODING_SHIFT_JIS, +    "SHIFT_JIS", N_("Japanese") }, +  { GEDIT_ENCODING_TCVN, +    "TCVN", N_("Vietnamese") }, +  { GEDIT_ENCODING_TIS_620, +    "TIS-620", N_("Thai") }, +  { GEDIT_ENCODING_UHC, +    "UHC", N_("Korean") }, +  { GEDIT_ENCODING_VISCII, +    "VISCII", N_("Vietnamese") }, + +  { GEDIT_ENCODING_WINDOWS_1250, +    "WINDOWS-1250", N_("Central European") }, +  { GEDIT_ENCODING_WINDOWS_1251, +    "WINDOWS-1251", N_("Cyrillic") }, +  { GEDIT_ENCODING_WINDOWS_1252, +    "WINDOWS-1252", N_("Western") }, +  { GEDIT_ENCODING_WINDOWS_1253, +    "WINDOWS-1253", N_("Greek") }, +  { GEDIT_ENCODING_WINDOWS_1254, +    "WINDOWS-1254", N_("Turkish") }, +  { GEDIT_ENCODING_WINDOWS_1255, +    "WINDOWS-1255", N_("Hebrew") }, +  { GEDIT_ENCODING_WINDOWS_1256, +    "WINDOWS-1256", N_("Arabic") }, +  { GEDIT_ENCODING_WINDOWS_1257, +    "WINDOWS-1257", N_("Baltic") }, +  { GEDIT_ENCODING_WINDOWS_1258, +    "WINDOWS-1258", N_("Vietnamese") } +}; + +static void +gedit_encoding_lazy_init (void) +{ +	static gboolean initialized = FALSE; +	const gchar *locale_charset; + +	if (initialized) +		return; + +	if (g_get_charset (&locale_charset) == FALSE) +	{ +		unknown_encoding.charset = g_strdup (locale_charset); +	} + +	initialized = TRUE; +} + +const GeditEncoding * +gedit_encoding_get_from_charset (const gchar *charset) +{ +	gint i; + +	g_return_val_if_fail (charset != NULL, NULL); + +	gedit_encoding_lazy_init (); + +	if (charset == NULL) +		return NULL; + +	if (g_ascii_strcasecmp (charset, "UTF-8") == 0) +		return gedit_encoding_get_utf8 (); + +	i = 0;  +	while (i < GEDIT_ENCODING_LAST) +	{ +		if (g_ascii_strcasecmp (charset, encodings[i].charset) == 0) +			return &encodings[i]; +       +		++i; +	} + +	if (unknown_encoding.charset != NULL) +	{ +		if (g_ascii_strcasecmp (charset, unknown_encoding.charset) == 0) +			return &unknown_encoding; +	} + +	return NULL; +} + +const GeditEncoding * +gedit_encoding_get_from_index (gint idx) +{ +	g_return_val_if_fail (idx >= 0, NULL); + +	if (idx >= GEDIT_ENCODING_LAST) +		return NULL; + +	gedit_encoding_lazy_init (); + +	return &encodings[idx]; +} + +const GeditEncoding * +gedit_encoding_get_utf8 (void) +{ +	gedit_encoding_lazy_init (); + +	return &utf8_encoding; +} + +const GeditEncoding * +gedit_encoding_get_current (void) +{ +	static gboolean initialized = FALSE; +	static const GeditEncoding *locale_encoding = NULL; + +	const gchar *locale_charset; + +	gedit_encoding_lazy_init (); + +	if (initialized != FALSE) +		return locale_encoding; + +	if (g_get_charset (&locale_charset) == FALSE)  +	{ +		g_return_val_if_fail (locale_charset != NULL, &utf8_encoding); +		 +		locale_encoding = gedit_encoding_get_from_charset (locale_charset); +	} +	else +	{ +		locale_encoding = &utf8_encoding; +	} +	 +	if (locale_encoding == NULL) +	{ +		locale_encoding = &unknown_encoding; +	} + +	g_return_val_if_fail (locale_encoding != NULL, NULL); + +	initialized = TRUE; + +	return locale_encoding; +} + +gchar * +gedit_encoding_to_string (const GeditEncoding* enc) +{ +	g_return_val_if_fail (enc != NULL, NULL); +	 +	gedit_encoding_lazy_init (); + +	g_return_val_if_fail (enc->charset != NULL, NULL); + +	if (enc->name != NULL) +	{ +	    	return g_strdup_printf ("%s (%s)", _(enc->name), enc->charset); +	} +	else +	{ +		if (g_ascii_strcasecmp (enc->charset, "ANSI_X3.4-1968") == 0) +			return g_strdup_printf ("US-ASCII (%s)", enc->charset); +		else +			return g_strdup (enc->charset); +	} +} + +const gchar * +gedit_encoding_get_charset (const GeditEncoding* enc) +{ +	g_return_val_if_fail (enc != NULL, NULL); + +	gedit_encoding_lazy_init (); + +	g_return_val_if_fail (enc->charset != NULL, NULL); + +	return enc->charset; +} + +const gchar * +gedit_encoding_get_name (const GeditEncoding* enc) +{ +	g_return_val_if_fail (enc != NULL, NULL); + +	gedit_encoding_lazy_init (); + +	return (enc->name == NULL) ? _("Unknown") : _(enc->name); +} + +/* These are to make language bindings happy. Since Encodings are + * const, copy() just returns the same pointer and fres() doesn't + * do nothing */ + +GeditEncoding * +gedit_encoding_copy (const GeditEncoding *enc) +{ +	g_return_val_if_fail (enc != NULL, NULL); + +	return (GeditEncoding *) enc; +} + +void  +gedit_encoding_free (GeditEncoding *enc) +{ +	g_return_if_fail (enc != NULL); +} + +/** + * gedit_encoding_get_type: + *  + * Retrieves the GType object which is associated with the + * #GeditEncoding class. + *  + * Return value: the GType associated with #GeditEncoding. + **/ +GType  +gedit_encoding_get_type (void) +{ +	static GType our_type = 0; + +	if (!our_type) +		our_type = g_boxed_type_register_static ( +			"GeditEncoding", +			(GBoxedCopyFunc) gedit_encoding_copy, +			(GBoxedFreeFunc) gedit_encoding_free); + +	return our_type; +}  + diff --git a/gedit/gedit-encodings.h b/gedit/gedit-encodings.h new file mode 100755 index 00000000..7e0aa8d2 --- /dev/null +++ b/gedit/gedit-encodings.h @@ -0,0 +1,62 @@ +/* + * gedit-encodings.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_ENCODINGS_H__ +#define __GEDIT_ENCODINGS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _GeditEncoding GeditEncoding; + +#define GEDIT_TYPE_ENCODING     (gedit_encoding_get_type ()) + +GType              	 gedit_encoding_get_type (void) G_GNUC_CONST; + +const GeditEncoding	*gedit_encoding_get_from_charset (const gchar         *charset); +const GeditEncoding	*gedit_encoding_get_from_index	 (gint                 index); + +gchar 			*gedit_encoding_to_string	 (const GeditEncoding *enc); + +const gchar		*gedit_encoding_get_name	 (const GeditEncoding *enc); +const gchar		*gedit_encoding_get_charset	 (const GeditEncoding *enc); + +const GeditEncoding 	*gedit_encoding_get_utf8	 (void); +const GeditEncoding 	*gedit_encoding_get_current	 (void); + +/* These should not be used, they are just to make python bindings happy */ +GeditEncoding		*gedit_encoding_copy		 (const GeditEncoding *enc); +void               	 gedit_encoding_free		 (GeditEncoding       *enc); + +G_END_DECLS + +#endif  /* __GEDIT_ENCODINGS_H__ */ diff --git a/gedit/gedit-enum-types.c.template b/gedit/gedit-enum-types.c.template new file mode 100755 index 00000000..7a67ac79 --- /dev/null +++ b/gedit/gedit-enum-types.c.template @@ -0,0 +1,39 @@ +/*** BEGIN file-header ***/ +#include "gedit-enum-types.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ +	static GType the_type = 0; +	 +	if (the_type == 0) +	{ +		static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ +			{ @VALUENAME@, +			  "@VALUENAME@", +			  "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ +			{ 0, NULL, NULL } +		}; +		the_type = g_@type@_register_static ( +				g_intern_static_string ("@EnumName@"), +				values); +	} +	return the_type; +} + +/*** END value-tail ***/ diff --git a/gedit/gedit-enum-types.h.template b/gedit/gedit-enum-types.h.template new file mode 100755 index 00000000..78f39ce8 --- /dev/null +++ b/gedit/gedit-enum-types.h.template @@ -0,0 +1,27 @@ +/*** BEGIN file-header ***/ +#ifndef __GEDIT_ENUM_TYPES_H__ +#define __GEDIT_ENUM_TYPES_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* Enumerations from "@filename@" */ + +/*** END file-production ***/ + +/*** BEGIN enumeration-production ***/ +#define GEDIT_TYPE_@ENUMSHORT@	(@enum_name@_get_type()) +GType @enum_name@_get_type	(void) G_GNUC_CONST; + +/*** END enumeration-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __GEDIT_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + diff --git a/gedit/gedit-file-chooser-dialog.c b/gedit/gedit-file-chooser-dialog.c new file mode 100755 index 00000000..94858338 --- /dev/null +++ b/gedit/gedit-file-chooser-dialog.c @@ -0,0 +1,560 @@ +/* + * gedit-file-chooser-dialog.c + * This file is part of gedit + * + * Copyright (C) 2005-2007 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +/* TODO: Override set_extra_widget */ +/* TODO: add encoding property */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-file-chooser-dialog.h" +#include "gedit-encodings-combo-box.h" +#include "gedit-language-manager.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-debug.h" +#include "gedit-enum-types.h" + +#define GEDIT_FILE_CHOOSER_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_FILE_CHOOSER_DIALOG, GeditFileChooserDialogPrivate)) + +#define ALL_FILES		_("All Files") +#define ALL_TEXT_FILES		_("All Text Files") + +struct _GeditFileChooserDialogPrivate +{ +	GtkWidget *option_menu; +	GtkWidget *extra_widget; + +	GtkWidget *newline_label; +	GtkWidget *newline_combo; +	GtkListStore *newline_store; +}; + +G_DEFINE_TYPE(GeditFileChooserDialog, gedit_file_chooser_dialog, GTK_TYPE_FILE_CHOOSER_DIALOG) + +static void +gedit_file_chooser_dialog_class_init (GeditFileChooserDialogClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	g_type_class_add_private (object_class, sizeof(GeditFileChooserDialogPrivate)); +} + +static void +create_option_menu (GeditFileChooserDialog *dialog) +{ +	GtkWidget *label; +	GtkWidget *menu; + +	label = gtk_label_new_with_mnemonic (_("C_haracter Encoding:")); +	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + +	menu = gedit_encodings_combo_box_new ( +		gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu); + +	gtk_box_pack_start (GTK_BOX (dialog->priv->extra_widget), +	                    label, +	                    FALSE, +	                    TRUE, +	                    0); + +	gtk_box_pack_start (GTK_BOX (dialog->priv->extra_widget), +	                    menu, +	                    TRUE, +	                    TRUE, +	                    0); + +	gtk_widget_show (label); +	gtk_widget_show (menu); + +	dialog->priv->option_menu = menu; +} + +static void +update_newline_visibility (GeditFileChooserDialog *dialog) +{ +	if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE) +	{ +		gtk_widget_show (dialog->priv->newline_label); +		gtk_widget_show (dialog->priv->newline_combo); +	} +	else +	{ +		gtk_widget_hide (dialog->priv->newline_label); +		gtk_widget_hide (dialog->priv->newline_combo); +	} +} + +static void +newline_combo_append (GtkComboBox              *combo, +                      GtkListStore             *store, +                      GtkTreeIter              *iter, +                      const gchar              *label, +                      GeditDocumentNewlineType  newline_type) +{ +	gtk_list_store_append (store, iter); +	gtk_list_store_set (store, iter, 0, label, 1, newline_type, -1); + +	if (newline_type == GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT) +	{ +		gtk_combo_box_set_active_iter (combo, iter); +	} +} + +static void +create_newline_combo (GeditFileChooserDialog *dialog) +{ +	GtkWidget *label, *combo; +	GtkListStore *store; +	GtkCellRenderer *renderer; +	GtkTreeIter iter; + +	label = gtk_label_new_with_mnemonic (_("L_ine Ending:")); +	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + +	store = gtk_list_store_new (2, G_TYPE_STRING, GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE); +	combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); +	renderer = gtk_cell_renderer_text_new (); + +	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), +	                            renderer, +	                            TRUE); + +	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), +	                               renderer, +	                               "text", +	                               0); + +	newline_combo_append (GTK_COMBO_BOX (combo), +	                      store, +	                      &iter, +	                      _("Unix/Linux"), +	                      GEDIT_DOCUMENT_NEWLINE_TYPE_LF); + +	newline_combo_append (GTK_COMBO_BOX (combo), +	                      store, +	                      &iter, +	                      _("Mac OS Classic"), +	                      GEDIT_DOCUMENT_NEWLINE_TYPE_CR); + +	newline_combo_append (GTK_COMBO_BOX (combo), +	                      store, +	                      &iter, +	                      _("Windows"), +	                      GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + +	gtk_box_pack_start (GTK_BOX (dialog->priv->extra_widget), +	                    label, +	                    FALSE, +	                    TRUE, +	                    0); + +	gtk_box_pack_start (GTK_BOX (dialog->priv->extra_widget), +	                    combo, +	                    TRUE, +	                    TRUE, +	                    0); + +	dialog->priv->newline_combo = combo; +	dialog->priv->newline_label = label; +	dialog->priv->newline_store = store; + +	update_newline_visibility (dialog); +} + +static void +create_extra_widget (GeditFileChooserDialog *dialog) +{ +	dialog->priv->extra_widget = gtk_hbox_new (FALSE, 6); + +	gtk_widget_show (dialog->priv->extra_widget); + +	create_option_menu (dialog); +	create_newline_combo (dialog); + +	gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), +					   dialog->priv->extra_widget); + +} + +static void +action_changed (GeditFileChooserDialog *dialog, +		GParamSpec	       *pspec, +		gpointer		data) +{ +	GtkFileChooserAction action; + +	action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)); + +	switch (action) +	{ +		case GTK_FILE_CHOOSER_ACTION_OPEN: +			g_object_set (dialog->priv->option_menu, +				      "save_mode", FALSE, +				      NULL); +			gtk_widget_show (dialog->priv->option_menu); +			break; +		case GTK_FILE_CHOOSER_ACTION_SAVE: +			g_object_set (dialog->priv->option_menu, +				      "save_mode", TRUE, +				      NULL); +			gtk_widget_show (dialog->priv->option_menu); +			break; +		default: +			gtk_widget_hide (dialog->priv->option_menu); +	} + +	update_newline_visibility (dialog); +} + +static void +filter_changed (GeditFileChooserDialog *dialog, +		GParamSpec	       *pspec, +		gpointer		data) +{ +	GtkFileFilter *filter; + +	if (!gedit_prefs_manager_active_file_filter_can_set ()) +		return; + +	filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)); +	if (filter != NULL) +	{ +		const gchar *name; +		gint id = 0; + +		name = gtk_file_filter_get_name (filter); +		g_return_if_fail (name != NULL); + +		if (strcmp (name, ALL_TEXT_FILES) == 0) +			id = 1; + +		gedit_debug_message (DEBUG_COMMANDS, "Active filter: %s (%d)", name, id); + +		gedit_prefs_manager_set_active_file_filter (id); +	} +} + +/* FIXME: use globs too - Paolo (Aug. 27, 2007) */ +static gboolean +all_text_files_filter (const GtkFileFilterInfo *filter_info, +		       gpointer                 data) +{ +	static GSList *known_mime_types = NULL; +	GSList *mime_types; + +	if (known_mime_types == NULL) +	{ +		GtkSourceLanguageManager *lm; +		const gchar * const *languages; + +		lm = gedit_get_language_manager (); +		languages = gtk_source_language_manager_get_language_ids (lm); + +		while ((languages != NULL) && (*languages != NULL)) +		{ +			gchar **mime_types; +			gint i; +			GtkSourceLanguage *lang; + +			lang = gtk_source_language_manager_get_language (lm, *languages); +			g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (lang), FALSE); +			++languages; + +			mime_types = gtk_source_language_get_mime_types (lang); +			if (mime_types == NULL) +				continue; + +			for (i = 0; mime_types[i] != NULL; i++) +			{ +				if (!g_content_type_is_a (mime_types[i], "text/plain")) +				{ +					gedit_debug_message (DEBUG_COMMANDS, +							     "Mime-type %s is not related to text/plain", +							     mime_types[i]); + +					known_mime_types = g_slist_prepend (known_mime_types, +									    g_strdup (mime_types[i])); +				} +			} + +			g_strfreev (mime_types); +		} + +		/* known_mime_types always has "text/plain" as first item" */ +		known_mime_types = g_slist_prepend (known_mime_types, g_strdup ("text/plain")); +	} + +	/* known mime_types contains "text/plain" and then the list of mime-types unrelated to "text/plain" +	 * that gedit recognizes */ + +	if (filter_info->mime_type == NULL) +		return FALSE; + +	/* +	 * The filter is matching: +	 * - the mime-types beginning with "text/" +	 * - the mime-types inheriting from a known mime-type (note the text/plain is +	 *   the first known mime-type) +	 */ + +	if (strncmp (filter_info->mime_type, "text/", 5) == 0) +		return TRUE; + +	mime_types = known_mime_types; +	while (mime_types != NULL) +	{ +		if (g_content_type_is_a (filter_info->mime_type, (const gchar*)mime_types->data)) +			return TRUE; + +		mime_types = g_slist_next (mime_types); +	} + +	return FALSE; +} + +static void +gedit_file_chooser_dialog_init (GeditFileChooserDialog *dialog) +{ +	dialog->priv = GEDIT_FILE_CHOOSER_DIALOG_GET_PRIVATE (dialog); +} + +static GtkWidget * +gedit_file_chooser_dialog_new_valist (const gchar          *title, +				      GtkWindow            *parent, +				      GtkFileChooserAction  action, +				      const GeditEncoding  *encoding, +				      const gchar          *first_button_text, +				      va_list               varargs) +{ +	GtkWidget *result; +	const char *button_text = first_button_text; +	gint response_id; +	GtkFileFilter *filter; +	gint active_filter; + +	g_return_val_if_fail (parent != NULL, NULL); + +	result = g_object_new (GEDIT_TYPE_FILE_CHOOSER_DIALOG, +			       "title", title, +			       "file-system-backend", NULL, +			       "local-only", FALSE, +			       "action", action, +			       "select-multiple", action == GTK_FILE_CHOOSER_ACTION_OPEN, +			       NULL); + +	create_extra_widget (GEDIT_FILE_CHOOSER_DIALOG (result)); + +	g_signal_connect (result, +			  "notify::action", +			  G_CALLBACK (action_changed), +			  NULL); + +	if (encoding != NULL) +		gedit_encodings_combo_box_set_selected_encoding ( +				GEDIT_ENCODINGS_COMBO_BOX (GEDIT_FILE_CHOOSER_DIALOG (result)->priv->option_menu), +				encoding); + +	active_filter = gedit_prefs_manager_get_active_file_filter (); +	gedit_debug_message (DEBUG_COMMANDS, "Active filter: %d", active_filter); + +	/* Filters */ +	filter = gtk_file_filter_new (); + +	gtk_file_filter_set_name (filter, ALL_FILES); +	gtk_file_filter_add_pattern (filter, "*"); +	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (result), filter); + +	if (active_filter != 1) +	{ +		/* Make this filter the default */ +		gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (result), filter); +	} + +	filter = gtk_file_filter_new (); +	gtk_file_filter_set_name (filter, ALL_TEXT_FILES); +	gtk_file_filter_add_custom (filter, +				    GTK_FILE_FILTER_MIME_TYPE, +				    all_text_files_filter, +				    NULL, +				    NULL); +	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (result), filter); + +	if (active_filter == 1) +	{ +		/* Make this filter the default */ +		gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (result), filter); +	} + +	g_signal_connect (result, +			  "notify::filter", +			  G_CALLBACK (filter_changed), +			  NULL); + +	gtk_window_set_transient_for (GTK_WINDOW (result), parent); +	gtk_window_set_destroy_with_parent (GTK_WINDOW (result), TRUE); + +	while (button_text) +	{ +		response_id = va_arg (varargs, gint); + +		gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id); +		if ((response_id == GTK_RESPONSE_OK) || +		    (response_id == GTK_RESPONSE_ACCEPT) || +		    (response_id == GTK_RESPONSE_YES) || +		    (response_id == GTK_RESPONSE_APPLY)) +			gtk_dialog_set_default_response (GTK_DIALOG (result), response_id); + +		button_text = va_arg (varargs, const gchar *); +	} + +	return result; +} + +/** + * gedit_file_chooser_dialog_new: + * @title: Title of the dialog, or %NULL + * @parent: Transient parent of the dialog, or %NULL + * @action: Open or save mode for the dialog + * @first_button_text: stock ID or text to go in the first button, or %NULL + * @Varargs: response ID for the first button, then additional (button, id) pairs, ending with %NULL + * + * Creates a new #GeditFileChooserDialog.  This function is analogous to + * gtk_dialog_new_with_buttons(). + * + * Return value: a new #GeditFileChooserDialog + * + **/ +GtkWidget * +gedit_file_chooser_dialog_new (const gchar          *title, +			       GtkWindow            *parent, +			       GtkFileChooserAction  action, +			       const GeditEncoding  *encoding, +			       const gchar          *first_button_text, +			       ...) +{ +	GtkWidget *result; +	va_list varargs; + +	va_start (varargs, first_button_text); +	result = gedit_file_chooser_dialog_new_valist (title, parent, action, +						       encoding, first_button_text, +						       varargs); +	va_end (varargs); + +	return result; +} + +void +gedit_file_chooser_dialog_set_encoding (GeditFileChooserDialog *dialog, +					const GeditEncoding    *encoding) +{ +	g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); +	g_return_if_fail (GEDIT_IS_ENCODINGS_COMBO_BOX (dialog->priv->option_menu)); + +	gedit_encodings_combo_box_set_selected_encoding ( +				GEDIT_ENCODINGS_COMBO_BOX (dialog->priv->option_menu), +				encoding); +} + +const GeditEncoding * +gedit_file_chooser_dialog_get_encoding (GeditFileChooserDialog *dialog) +{ +	g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog), NULL); +	g_return_val_if_fail (GEDIT_IS_ENCODINGS_COMBO_BOX (dialog->priv->option_menu), NULL); +	g_return_val_if_fail ((gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_OPEN || +			       gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE), NULL); + +	return gedit_encodings_combo_box_get_selected_encoding ( +				GEDIT_ENCODINGS_COMBO_BOX (dialog->priv->option_menu)); +} + +void +gedit_file_chooser_dialog_set_newline_type (GeditFileChooserDialog  *dialog, +					    GeditDocumentNewlineType newline_type) +{ +	GtkTreeIter iter; +	GtkTreeModel *model; + +	g_return_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog)); +	g_return_if_fail (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE); + +	model = GTK_TREE_MODEL (dialog->priv->newline_store); + +	if (!gtk_tree_model_get_iter_first (model, &iter)) +	{ +		return; +	} + +	do +	{ +		GeditDocumentNewlineType nt; + +		gtk_tree_model_get (model, &iter, 1, &nt, -1); + +		if (newline_type == nt) +		{ +			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (dialog->priv->newline_combo), +			                               &iter); +			break; +		} +	} while (gtk_tree_model_iter_next (model, &iter)); +} + +GeditDocumentNewlineType +gedit_file_chooser_dialog_get_newline_type (GeditFileChooserDialog *dialog) +{ +	GtkTreeIter iter; +	GeditDocumentNewlineType newline_type; + +	g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER_DIALOG (dialog), GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT); +	g_return_val_if_fail (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) == GTK_FILE_CHOOSER_ACTION_SAVE, +	                      GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT); + +	gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->priv->newline_combo), +	                               &iter); + +	gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->newline_store), +	                    &iter, +	                    1, +	                    &newline_type, +	                    -1); + +	return newline_type; +} diff --git a/gedit/gedit-file-chooser-dialog.h b/gedit/gedit-file-chooser-dialog.h new file mode 100755 index 00000000..090045c5 --- /dev/null +++ b/gedit/gedit-file-chooser-dialog.h @@ -0,0 +1,89 @@ +/* + * gedit-file-chooser-dialog.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_FILE_CHOOSER_DIALOG_H__ +#define __GEDIT_FILE_CHOOSER_DIALOG_H__ + +#include <gtk/gtk.h> + +#include <gedit/gedit-encodings.h> +#include <gedit/gedit-enum-types.h> +#include <gedit/gedit-document.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_FILE_CHOOSER_DIALOG             (gedit_file_chooser_dialog_get_type ()) +#define GEDIT_FILE_CHOOSER_DIALOG(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_FILE_CHOOSER_DIALOG, GeditFileChooserDialog)) +#define GEDIT_FILE_CHOOSER_DIALOG_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_FILE_CHOOSER_DIALOG, GeditFileChooserDialogClass)) +#define GEDIT_IS_FILE_CHOOSER_DIALOG(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_FILE_CHOOSER_DIALOG)) +#define GEDIT_IS_FILE_CHOOSER_DIALOG_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_FILE_CHOOSER_DIALOG)) +#define GEDIT_FILE_CHOOSER_DIALOG_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_FILE_CHOOSER_DIALOG, GeditFileChooserDialogClass)) + +typedef struct _GeditFileChooserDialog      GeditFileChooserDialog; +typedef struct _GeditFileChooserDialogClass GeditFileChooserDialogClass; + +typedef struct _GeditFileChooserDialogPrivate GeditFileChooserDialogPrivate; + +struct _GeditFileChooserDialogClass +{ +	GtkFileChooserDialogClass parent_class; +}; + +struct _GeditFileChooserDialog +{ +	GtkFileChooserDialog parent_instance; + +	GeditFileChooserDialogPrivate *priv; +}; + +GType		 gedit_file_chooser_dialog_get_type	(void) G_GNUC_CONST; + +GtkWidget	*gedit_file_chooser_dialog_new		(const gchar            *title, +							 GtkWindow              *parent, +							 GtkFileChooserAction    action, +							 const GeditEncoding    *encoding, +							 const gchar            *first_button_text, +							 ...); + +void		 gedit_file_chooser_dialog_set_encoding (GeditFileChooserDialog *dialog, +							 const GeditEncoding    *encoding); + +const GeditEncoding +		*gedit_file_chooser_dialog_get_encoding (GeditFileChooserDialog *dialog); + +void		 gedit_file_chooser_dialog_set_newline_type (GeditFileChooserDialog  *dialog, +							     GeditDocumentNewlineType newline_type); + +GeditDocumentNewlineType +		 gedit_file_chooser_dialog_get_newline_type (GeditFileChooserDialog *dialog); + +G_END_DECLS + +#endif /* __GEDIT_FILE_CHOOSER_DIALOG_H__ */ diff --git a/gedit/gedit-gio-document-loader.c b/gedit/gedit-gio-document-loader.c new file mode 100755 index 00000000..73cfe113 --- /dev/null +++ b/gedit/gedit-gio-document-loader.c @@ -0,0 +1,708 @@ +/* + * gedit-gio-document-loader.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005-2008. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include "gedit-gio-document-loader.h" +#include "gedit-document-output-stream.h" +#include "gedit-smart-charset-converter.h" +#include "gedit-prefs-manager.h" +#include "gedit-debug.h" +#include "gedit-utils.h" + +#ifndef ENABLE_GVFS_METADATA +#include "gedit-metadata-manager.h" +#endif + +typedef struct +{ +	GeditGioDocumentLoader *loader; +	GCancellable 	       *cancellable; + +	gssize			read; +	gboolean		tried_mount; +} AsyncData; + +#define READ_CHUNK_SIZE 8192 +#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \ +				G_FILE_ATTRIBUTE_STANDARD_TYPE "," \ +				G_FILE_ATTRIBUTE_TIME_MODIFIED "," \ +				G_FILE_ATTRIBUTE_STANDARD_SIZE "," \ +				G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," \ +				GEDIT_METADATA_ATTRIBUTE_ENCODING + +#define GEDIT_GIO_DOCUMENT_LOADER_GET_PRIVATE(object) \ +				(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +				 GEDIT_TYPE_GIO_DOCUMENT_LOADER,   \ +				 GeditGioDocumentLoaderPrivate)) + +static void	    gedit_gio_document_loader_load		(GeditDocumentLoader *loader); +static gboolean     gedit_gio_document_loader_cancel		(GeditDocumentLoader *loader); +static goffset      gedit_gio_document_loader_get_bytes_read	(GeditDocumentLoader *loader); + +static void open_async_read (AsyncData *async); + +struct _GeditGioDocumentLoaderPrivate +{ +	/* Info on the current file */ +	GFile            *gfile; + +	goffset           bytes_read; + +	/* Handle for remote files */ +	GCancellable 	 *cancellable; +	GInputStream	 *stream; +	GOutputStream    *output; +	GeditSmartCharsetConverter *converter; + +	gchar             buffer[READ_CHUNK_SIZE]; + +	GError           *error; +}; + +G_DEFINE_TYPE(GeditGioDocumentLoader, gedit_gio_document_loader, GEDIT_TYPE_DOCUMENT_LOADER) + +static void +gedit_gio_document_loader_dispose (GObject *object) +{ +	GeditGioDocumentLoaderPrivate *priv; + +	priv = GEDIT_GIO_DOCUMENT_LOADER (object)->priv; + +	if (priv->cancellable != NULL) +	{ +		g_cancellable_cancel (priv->cancellable); +		g_object_unref (priv->cancellable); +		priv->cancellable = NULL; +	} + +	if (priv->stream != NULL) +	{ +		g_object_unref (priv->stream); +		priv->stream = NULL; +	} + +	if (priv->output != NULL) +	{ +		g_object_unref (priv->output); +		priv->output = NULL; +	} + +	if (priv->converter != NULL) +	{ +		g_object_unref (priv->converter); +		priv->converter = NULL; +	} + +	if (priv->gfile != NULL) +	{ +		g_object_unref (priv->gfile); +		priv->gfile = NULL; +	} + +	if (priv->error != NULL) +	{ +		g_error_free (priv->error); +		priv->error = NULL; +	} + +	G_OBJECT_CLASS (gedit_gio_document_loader_parent_class)->dispose (object); +} + +static void +gedit_gio_document_loader_class_init (GeditGioDocumentLoaderClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GeditDocumentLoaderClass *loader_class = GEDIT_DOCUMENT_LOADER_CLASS (klass); + +	object_class->dispose = gedit_gio_document_loader_dispose; + +	loader_class->load = gedit_gio_document_loader_load; +	loader_class->cancel = gedit_gio_document_loader_cancel; +	loader_class->get_bytes_read = gedit_gio_document_loader_get_bytes_read; + +	g_type_class_add_private (object_class, sizeof(GeditGioDocumentLoaderPrivate)); +} + +static void +gedit_gio_document_loader_init (GeditGioDocumentLoader *gvloader) +{ +	gvloader->priv = GEDIT_GIO_DOCUMENT_LOADER_GET_PRIVATE (gvloader); + +	gvloader->priv->converter = NULL; +	gvloader->priv->error = NULL; +} + +static AsyncData * +async_data_new (GeditGioDocumentLoader *gvloader) +{ +	AsyncData *async; +	 +	async = g_slice_new (AsyncData); +	async->loader = gvloader; +	async->cancellable = g_object_ref (gvloader->priv->cancellable); +	async->tried_mount = FALSE; +	 +	return async; +} + +static void +async_data_free (AsyncData *async) +{ +	g_object_unref (async->cancellable); +	g_slice_free (AsyncData, async); +} + +static const GeditEncoding * +get_metadata_encoding (GeditDocumentLoader *loader) +{ +	const GeditEncoding *enc = NULL; + +#ifndef ENABLE_GVFS_METADATA +	gchar *charset; +	const gchar *uri; + +	uri = gedit_document_loader_get_uri (loader); + +	charset = gedit_metadata_manager_get (uri, "encoding"); + +	if (charset == NULL) +		return NULL; + +	enc = gedit_encoding_get_from_charset (charset); + +	g_free (charset); +#else +	GFileInfo *info; + +	info = gedit_document_loader_get_info (loader); + +	/* check if the encoding was set in the metadata */ +	if (g_file_info_has_attribute (info, GEDIT_METADATA_ATTRIBUTE_ENCODING)) +	{ +		const gchar *charset; + +		charset = g_file_info_get_attribute_string (info, +							    GEDIT_METADATA_ATTRIBUTE_ENCODING); + +		if (charset == NULL) +			return NULL; +		 +		enc = gedit_encoding_get_from_charset (charset); +	} +#endif + +	return enc; +} + +static void +remote_load_completed_or_failed (GeditGioDocumentLoader *loader, AsyncData *async) +{ +	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (loader), +				       TRUE, +				       loader->priv->error); + +	if (async) +		async_data_free (async); +} + +static void +async_failed (AsyncData *async, GError *error) +{ +	g_propagate_error (&async->loader->priv->error, error); +	remote_load_completed_or_failed (async->loader, async); +} + +static void +close_input_stream_ready_cb (GInputStream *stream, +			     GAsyncResult  *res, +			     AsyncData     *async) +{ +	GError *error = NULL; + +	gedit_debug (DEBUG_LOADER); + +	/* check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} +	 +	gedit_debug_message (DEBUG_SAVER, "Finished closing input stream"); +	 +	if (!g_input_stream_close_finish (stream, res, &error)) +	{ +		gedit_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message); + +		async_failed (async, error); +		return; +	} + +	gedit_debug_message (DEBUG_SAVER, "Close output stream"); +	if (!g_output_stream_close (async->loader->priv->output, +				    async->cancellable, &error)) +	{ +		async_failed (async, error); +		return; +	} + +	remote_load_completed_or_failed (async->loader, async); +} + +static void +write_complete (AsyncData *async) +{ +	GeditDocumentLoader *loader; + +	loader = GEDIT_DOCUMENT_LOADER (async->loader); + +	if (async->loader->priv->stream) +		g_input_stream_close_async (G_INPUT_STREAM (async->loader->priv->stream), +					    G_PRIORITY_HIGH, +					    async->cancellable, +					    (GAsyncReadyCallback)close_input_stream_ready_cb, +					    async); +} + +/* prototype, because they call each other... isn't C lovely */ +static void	read_file_chunk		(AsyncData *async); + +static void +write_file_chunk (AsyncData *async) +{ +	GeditGioDocumentLoader *gvloader; +	gssize bytes_written; +	GError *error = NULL; + +	gvloader = async->loader; + +	/* we use sync methods on doc stream since it is in memory. Using async +	   would be racy and we can endup with invalidated iters */ +	bytes_written = g_output_stream_write (G_OUTPUT_STREAM (gvloader->priv->output), +					       gvloader->priv->buffer, +					       async->read, +					       async->cancellable, +					       &error); + +	gedit_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written); +	if (bytes_written == -1) +	{ +		gedit_debug_message (DEBUG_SAVER, "Write error: %s", error->message); +		async_failed (async, error); +		return; +	} + +	/* note that this signal blocks the read... check if it isn't +	 * a performance problem +	 */ +	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader), +				       FALSE, +				       NULL); + +	read_file_chunk (async); +} + +static void +async_read_cb (GInputStream *stream, +	       GAsyncResult *res, +	       AsyncData    *async) +{ +	gedit_debug (DEBUG_LOADER); +	GeditGioDocumentLoader *gvloader; +	GError *error = NULL; + +	gedit_debug (DEBUG_LOADER); + +	/* manually check cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} + +	gvloader = async->loader; + +	async->read = g_input_stream_read_finish (stream, res, &error); + +	/* error occurred */ +	if (async->read == -1) +	{ +		async_failed (async, error); +		return; +	} + +	/* Check for the extremely unlikely case where the file size overflows. */ +	if (gvloader->priv->bytes_read + async->read < gvloader->priv->bytes_read) +	{ +		g_set_error (&gvloader->priv->error, +			     GEDIT_DOCUMENT_ERROR, +			     GEDIT_DOCUMENT_ERROR_TOO_BIG, +			     "File too big"); + +		async_failed (async, gvloader->priv->error); +		return; +	} + +	/* Bump the size. */ +	gvloader->priv->bytes_read += async->read; + +	/* end of the file, we are done! */ +	if (async->read == 0) +	{ +		GeditDocumentLoader *loader; + +		loader = GEDIT_DOCUMENT_LOADER (gvloader); + +		loader->auto_detected_encoding = +			gedit_smart_charset_converter_get_guessed (gvloader->priv->converter); + +		loader->auto_detected_newline_type = +			gedit_document_output_stream_detect_newline_type (GEDIT_DOCUMENT_OUTPUT_STREAM (gvloader->priv->output)); + +		/* Check if we needed some fallback char, if so, check if there was +		   a previous error and if not set a fallback used error */ +		/* FIXME Uncomment this when we want to manage conversion fallback */ +		/*if ((gedit_smart_charset_converter_get_num_fallbacks (gvloader->priv->converter) != 0) && +		    gvloader->priv->error == NULL) +		{ +			g_set_error_literal (&gvloader->priv->error, +					     GEDIT_DOCUMENT_ERROR, +					     GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK, +					     "There was a conversion error and it was " +					     "needed to use a fallback char"); +		}*/ + +		write_complete (async); + +		return; +	} + +	write_file_chunk (async); +} + +static void +read_file_chunk (AsyncData *async) +{ +	GeditGioDocumentLoader *gvloader; +	 +	gvloader = async->loader; + +	g_input_stream_read_async (G_INPUT_STREAM (gvloader->priv->stream), +				   gvloader->priv->buffer, +				   READ_CHUNK_SIZE, +				   G_PRIORITY_HIGH, +				   async->cancellable, +				   (GAsyncReadyCallback) async_read_cb, +				   async); +} + +static GSList * +get_candidate_encodings (GeditGioDocumentLoader *gvloader) +{ +	const GeditEncoding *metadata; +	GSList *encodings = NULL; + +	encodings = gedit_prefs_manager_get_auto_detected_encodings (); + +	metadata = get_metadata_encoding (GEDIT_DOCUMENT_LOADER (gvloader)); +	if (metadata != NULL) +	{ +		encodings = g_slist_prepend (encodings, (gpointer)metadata); +	} + +	return encodings; +} + +static void +finish_query_info (AsyncData *async) +{ +	GeditGioDocumentLoader *gvloader; +	GeditDocumentLoader *loader; +	GInputStream *conv_stream; +	GFileInfo *info; +	GSList *candidate_encodings; +	 +	gvloader = async->loader; +	loader = GEDIT_DOCUMENT_LOADER (gvloader); +	info = loader->info; + +	/* if it's not a regular file, error out... */ +	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) && +	    g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR) +	{ +		g_set_error (&gvloader->priv->error, +			     G_IO_ERROR, +			     G_IO_ERROR_NOT_REGULAR_FILE, +			     "Not a regular file"); + +		remote_load_completed_or_failed (gvloader, async); + +		return; +	} + +	/* Get the candidate encodings */ +	if (loader->encoding == NULL) +	{ +		candidate_encodings = get_candidate_encodings (gvloader); +	} +	else +	{ +		candidate_encodings = g_slist_prepend (NULL, (gpointer) loader->encoding); +	} + +	gvloader->priv->converter = gedit_smart_charset_converter_new (candidate_encodings); +	g_slist_free (candidate_encodings); +	 +	conv_stream = g_converter_input_stream_new (gvloader->priv->stream, +						    G_CONVERTER (gvloader->priv->converter)); +	g_object_unref (gvloader->priv->stream); + +	gvloader->priv->stream = conv_stream; + +	/* Output stream */ +	gvloader->priv->output = gedit_document_output_stream_new (loader->document); + +	/* start reading */ +	read_file_chunk (async); +} + +static void +query_info_cb (GFile        *source, +	       GAsyncResult *res, +	       AsyncData    *async) +{ +	GeditGioDocumentLoader *gvloader; +	GFileInfo *info; +	GError *error = NULL; + +	gedit_debug (DEBUG_LOADER); + +	/* manually check the cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	}	 + +	gvloader = async->loader; + +	/* finish the info query */ +	info = g_file_query_info_finish (gvloader->priv->gfile, +	                                 res, +	                                 &error); + +	if (info == NULL) +	{ +		/* propagate the error and clean up */ +		async_failed (async, error); +		return; +	} + +	GEDIT_DOCUMENT_LOADER (gvloader)->info = info; +	 +	finish_query_info (async); +} + +static void +mount_ready_callback (GFile        *file, +		      GAsyncResult *res, +		      AsyncData    *async) +{ +	GError *error = NULL; +	gboolean mounted; + +	gedit_debug (DEBUG_LOADER); + +	/* manual check for cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} + +	mounted = g_file_mount_enclosing_volume_finish (file, res, &error); +	 +	if (!mounted) +	{ +		async_failed (async, error); +	} +	else +	{ +		/* try again to open the file for reading */ +		open_async_read (async); +	} +} + +static void +recover_not_mounted (AsyncData *async) +{ +	GeditDocument *doc; +	GMountOperation *mount_operation; + +	gedit_debug (DEBUG_LOADER); + +	doc = gedit_document_loader_get_document (GEDIT_DOCUMENT_LOADER (async->loader)); +	mount_operation = _gedit_document_create_mount_operation (doc); + +	async->tried_mount = TRUE; +	g_file_mount_enclosing_volume (async->loader->priv->gfile, +				       G_MOUNT_MOUNT_NONE, +				       mount_operation, +				       async->cancellable, +				       (GAsyncReadyCallback) mount_ready_callback, +				       async); + +	g_object_unref (mount_operation); +} + +static void +async_read_ready_callback (GObject      *source, +			   GAsyncResult *res, +		           AsyncData    *async) +{ +	GError *error = NULL; +	GeditGioDocumentLoader *gvloader; +	 +	gedit_debug (DEBUG_LOADER); + +	/* manual check for cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} + +	gvloader = async->loader; +	 +	gvloader->priv->stream = G_INPUT_STREAM (g_file_read_finish (gvloader->priv->gfile, +								     res, &error)); + +	if (!gvloader->priv->stream) +	{		 +		if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount) +		{ +			recover_not_mounted (async); +			g_error_free (error); +			return; +		} +		 +		/* Propagate error */ +		g_propagate_error (&gvloader->priv->error, error); +		gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader), +					       TRUE, +					       gvloader->priv->error); + +		async_data_free (async); +		return; +	} + +	/* get the file info: note we cannot use  +	 * g_file_input_stream_query_info_async since it is not able to get the +	 * content type etc, beside it is not supported by gvfs. +	 * Using the file instead of the stream is slightly racy, but for +	 * loading this is not too bad... +	 */ +	g_file_query_info_async (gvloader->priv->gfile, +				 REMOTE_QUERY_ATTRIBUTES, +                                 G_FILE_QUERY_INFO_NONE, +				 G_PRIORITY_HIGH, +				 async->cancellable, +				 (GAsyncReadyCallback) query_info_cb, +				 async); +} + +static void +open_async_read (AsyncData *async) +{ +	g_file_read_async (async->loader->priv->gfile,  +	                   G_PRIORITY_HIGH, +	                   async->cancellable, +	                   (GAsyncReadyCallback) async_read_ready_callback, +	                   async); +} + +static void +gedit_gio_document_loader_load (GeditDocumentLoader *loader) +{ +	GeditGioDocumentLoader *gvloader = GEDIT_GIO_DOCUMENT_LOADER (loader); +	AsyncData *async; +	 +	gedit_debug (DEBUG_LOADER); + +	/* make sure no load operation is currently running */ +	g_return_if_fail (gvloader->priv->cancellable == NULL); + +	gvloader->priv->gfile = g_file_new_for_uri (loader->uri); + +	/* loading start */ +	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader), +				       FALSE, +				       NULL); + +	gvloader->priv->cancellable = g_cancellable_new (); +	async = async_data_new (gvloader); +	 +	open_async_read (async); +} + +static goffset +gedit_gio_document_loader_get_bytes_read (GeditDocumentLoader *loader) +{ +	return GEDIT_GIO_DOCUMENT_LOADER (loader)->priv->bytes_read; +} + +static gboolean +gedit_gio_document_loader_cancel (GeditDocumentLoader *loader) +{ +	GeditGioDocumentLoader *gvloader = GEDIT_GIO_DOCUMENT_LOADER (loader); + +	if (gvloader->priv->cancellable == NULL) +		return FALSE; + +	g_cancellable_cancel (gvloader->priv->cancellable); + +	g_set_error (&gvloader->priv->error, +		     G_IO_ERROR, +		     G_IO_ERROR_CANCELLED, +		     "Operation cancelled"); + +	remote_load_completed_or_failed (gvloader, NULL); + +	return TRUE; +} diff --git a/gedit/gedit-gio-document-loader.h b/gedit/gedit-gio-document-loader.h new file mode 100755 index 00000000..78c9e8ad --- /dev/null +++ b/gedit/gedit-gio-document-loader.h @@ -0,0 +1,79 @@ +/* + * gedit-gio-document-loader.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005-2008. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_GIO_DOCUMENT_LOADER_H__ +#define __GEDIT_GIO_DOCUMENT_LOADER_H__ + +#include <gedit/gedit-document.h> +#include "gedit-document-loader.h" + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_GIO_DOCUMENT_LOADER              (gedit_gio_document_loader_get_type()) +#define GEDIT_GIO_DOCUMENT_LOADER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_GIO_DOCUMENT_LOADER, GeditGioDocumentLoader)) +#define GEDIT_GIO_DOCUMENT_LOADER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_GIO_DOCUMENT_LOADER, GeditGioDocumentLoaderClass)) +#define GEDIT_IS_GIO_DOCUMENT_LOADER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_GIO_DOCUMENT_LOADER)) +#define GEDIT_IS_GIO_DOCUMENT_LOADER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_GIO_DOCUMENT_LOADER)) +#define GEDIT_GIO_DOCUMENT_LOADER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_GIO_DOCUMENT_LOADER, GeditGioDocumentLoaderClass)) + +/* Private structure type */ +typedef struct _GeditGioDocumentLoaderPrivate GeditGioDocumentLoaderPrivate; + +/* + * Main object structure + */ +typedef struct _GeditGioDocumentLoader GeditGioDocumentLoader; + +struct _GeditGioDocumentLoader +{ +	GeditDocumentLoader loader; + +	/*< private > */ +	GeditGioDocumentLoaderPrivate *priv; +}; + +/* + * Class definition + */ +typedef GeditDocumentLoaderClass GeditGioDocumentLoaderClass; + +/* + * Public methods + */ +GType 		 	 gedit_gio_document_loader_get_type	(void) G_GNUC_CONST; + +G_END_DECLS + +#endif  /* __GEDIT_GIO_DOCUMENT_LOADER_H__  */ diff --git a/gedit/gedit-gio-document-saver.c b/gedit/gedit-gio-document-saver.c new file mode 100755 index 00000000..f1f63b62 --- /dev/null +++ b/gedit/gedit-gio-document-saver.c @@ -0,0 +1,775 @@ +/* + * gedit-gio-document-saver.c + * This file is part of gedit + * + * Copyright (C) 2005-2006 - Paolo Borelli and Paolo Maggi + * Copyright (C) 2007 - Paolo Borelli, Paolo Maggi, Steve Frécinaux + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005-2006. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <glib.h> +#include <gio/gio.h> +#include <string.h> + +#include "gedit-gio-document-saver.h" +#include "gedit-document-input-stream.h" +#include "gedit-debug.h" + +#define WRITE_CHUNK_SIZE 8192 + +typedef struct +{ +	GeditGioDocumentSaver *saver; +	gchar 		       buffer[WRITE_CHUNK_SIZE]; +	GCancellable 	      *cancellable; +	gboolean	       tried_mount; +	gssize		       written; +	gssize		       read; +	GError                *error; +} AsyncData; + +#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \ +				G_FILE_ATTRIBUTE_TIME_MODIFIED +				 +#define GEDIT_GIO_DOCUMENT_SAVER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +							  GEDIT_TYPE_GIO_DOCUMENT_SAVER, \ +							  GeditGioDocumentSaverPrivate)) + +static void	     gedit_gio_document_saver_save		    (GeditDocumentSaver *saver, +								     GTimeVal           *old_mtime); +static goffset	     gedit_gio_document_saver_get_file_size	    (GeditDocumentSaver *saver); +static goffset	     gedit_gio_document_saver_get_bytes_written	    (GeditDocumentSaver *saver); + + +static void 	    check_modified_async 			    (AsyncData          *async); + +struct _GeditGioDocumentSaverPrivate +{ +	GTimeVal		  old_mtime; + +	goffset			  size; +	goffset			  bytes_written; + +	GFile			 *gfile; +	GCancellable		 *cancellable; +	GOutputStream		 *stream; +	GInputStream		 *input; +	 +	GError                   *error; +}; + +G_DEFINE_TYPE(GeditGioDocumentSaver, gedit_gio_document_saver, GEDIT_TYPE_DOCUMENT_SAVER) + +static void +gedit_gio_document_saver_dispose (GObject *object) +{ +	GeditGioDocumentSaverPrivate *priv = GEDIT_GIO_DOCUMENT_SAVER (object)->priv; + +	if (priv->cancellable != NULL) +	{ +		g_cancellable_cancel (priv->cancellable); +		g_object_unref (priv->cancellable); +		priv->cancellable = NULL; +	} + +	if (priv->gfile != NULL) +	{ +		g_object_unref (priv->gfile); +		priv->gfile = NULL; +	} + +	if (priv->error != NULL) +	{ +		g_error_free (priv->error); +		priv->error = NULL; +	} + +	if (priv->stream != NULL) +	{ +		g_object_unref (priv->stream); +		priv->stream = NULL; +	} + +	if (priv->input != NULL) +	{ +		g_object_unref (priv->input); +		priv->input = NULL; +	} + +	G_OBJECT_CLASS (gedit_gio_document_saver_parent_class)->dispose (object); +} + +static AsyncData * +async_data_new (GeditGioDocumentSaver *gvsaver) +{ +	AsyncData *async; +	 +	async = g_slice_new (AsyncData); +	async->saver = gvsaver; +	async->cancellable = g_object_ref (gvsaver->priv->cancellable); + +	async->tried_mount = FALSE; +	async->written = 0; +	async->read = 0; + +	async->error = NULL; + +	return async; +} + +static void +async_data_free (AsyncData *async) +{ +	g_object_unref (async->cancellable); + +	if (async->error) +	{ +		g_error_free (async->error); +	} + +	g_slice_free (AsyncData, async); +} + +static void  +gedit_gio_document_saver_class_init (GeditGioDocumentSaverClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GeditDocumentSaverClass *saver_class = GEDIT_DOCUMENT_SAVER_CLASS (klass); + +	object_class->dispose = gedit_gio_document_saver_dispose; + +	saver_class->save = gedit_gio_document_saver_save; +	saver_class->get_file_size = gedit_gio_document_saver_get_file_size; +	saver_class->get_bytes_written = gedit_gio_document_saver_get_bytes_written; + +	g_type_class_add_private (object_class, sizeof(GeditGioDocumentSaverPrivate)); +} + +static void +gedit_gio_document_saver_init (GeditGioDocumentSaver *gvsaver) +{ +	gvsaver->priv = GEDIT_GIO_DOCUMENT_SAVER_GET_PRIVATE (gvsaver); + +	gvsaver->priv->cancellable = g_cancellable_new (); +	gvsaver->priv->error = NULL; +} + +static void +remote_save_completed_or_failed (GeditGioDocumentSaver *gvsaver,  +				 AsyncData 	       *async) +{ +	gedit_document_saver_saving (GEDIT_DOCUMENT_SAVER (gvsaver), +				     TRUE, +				     gvsaver->priv->error); + +	if (async) +		async_data_free (async); +} + +static void +async_failed (AsyncData *async, +	      GError    *error) +{ +	g_propagate_error (&async->saver->priv->error, error); +	remote_save_completed_or_failed (async->saver, async); +} + +/* BEGIN NOTE: + * + * This fixes an issue in GOutputStream that applies the atomic replace + * save strategy. The stream moves the written file to the original file + * when the stream is closed. However, there is no way currently to tell + * the stream that the save should be aborted (there could be a + * conversion error). The patch explicitly closes the output stream + * in all these cases with a GCancellable in the cancelled state, causing + * the output stream to close, but not move the file. This makes use + * of an implementation detail in the local gio file stream and should be + * properly fixed by adding the appropriate API in gio. Until then, at least + * we prevent data corruption for now. + * + * Relevant bug reports: + * + * Bug 615110 - write file ignore encoding errors (gedit) + * https://bugzilla.mate.org/show_bug.cgi?id=615110 + * + * Bug 602412 - g_file_replace does not restore original file when there is + *              errors while writing (glib/gio) + * https://bugzilla.mate.org/show_bug.cgi?id=602412 + */ +static void +cancel_output_stream_ready_cb (GOutputStream *stream, +                               GAsyncResult  *result, +                               AsyncData     *async) +{ +	GError *error; + +	g_output_stream_close_finish (stream, result, NULL); + +	/* check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable) || async->error == NULL) +	{ +		async_data_free (async); +		return; +	} + +	error = async->error; +	async->error = NULL; + +	async_failed (async, error); +} + +static void +cancel_output_stream (AsyncData *async) +{ +	GCancellable *cancellable; + +	gedit_debug_message (DEBUG_SAVER, "Cancel output stream"); + +	cancellable = g_cancellable_new (); +	g_cancellable_cancel (cancellable); + +	g_output_stream_close_async (async->saver->priv->stream, +				     G_PRIORITY_HIGH, +				     cancellable, +				     (GAsyncReadyCallback)cancel_output_stream_ready_cb, +				     async); + +	g_object_unref (cancellable); +} + +static void +cancel_output_stream_and_fail (AsyncData *async, +                               GError    *error) +{ + +	gedit_debug_message (DEBUG_SAVER, "Cancel output stream and fail"); + +	g_propagate_error (&async->error, error); +	cancel_output_stream (async); +} + +/* + * END NOTE + */ + +static void +remote_get_info_cb (GFile        *source, +		    GAsyncResult *res, +		    AsyncData    *async) +{ +	GeditGioDocumentSaver *saver; +	GFileInfo *info; +	GError *error = NULL; + +	gedit_debug (DEBUG_SAVER); + +	/* check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} +	 +	saver = async->saver; + +	gedit_debug_message (DEBUG_SAVER, "Finished query info on file"); +	info = g_file_query_info_finish (source, res, &error); + +	if (info != NULL) +	{ +		if (GEDIT_DOCUMENT_SAVER (saver)->info != NULL) +			g_object_unref (GEDIT_DOCUMENT_SAVER (saver)->info); + +		GEDIT_DOCUMENT_SAVER (saver)->info = info; +	} +	else +	{ +		gedit_debug_message (DEBUG_SAVER, "Query info failed: %s", error->message); +		g_propagate_error (&saver->priv->error, error); +	} + +	remote_save_completed_or_failed (saver, async); +} + +static void +close_async_ready_get_info_cb (GOutputStream *stream, +			       GAsyncResult  *res, +			       AsyncData     *async) +{ +	GError *error = NULL; + +	gedit_debug (DEBUG_SAVER); + +	/* check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} +	 +	gedit_debug_message (DEBUG_SAVER, "Finished closing stream"); +	 +	if (!g_output_stream_close_finish (stream, res, &error)) +	{ +		gedit_debug_message (DEBUG_SAVER, "Closing stream error: %s", error->message); + +		async_failed (async, error); +		return; +	} + +	/* get the file info: note we cannot use  +	 * g_file_output_stream_query_info_async since it is not able to get the +	 * content type etc, beside it is not supported by gvfs. +	 * I'm not sure this is actually necessary, can't we just use +	 * g_content_type_guess (since we have the file name and the data) +	 */ +	gedit_debug_message (DEBUG_SAVER, "Query info on file"); +	g_file_query_info_async (async->saver->priv->gfile, +			         REMOTE_QUERY_ATTRIBUTES, +			         G_FILE_QUERY_INFO_NONE, +			         G_PRIORITY_HIGH, +			         async->cancellable, +			         (GAsyncReadyCallback) remote_get_info_cb, +			         async); +} + +static void +write_complete (AsyncData *async) +{ +	GError *error = NULL; + +	/* first we close the input stream */ +	gedit_debug_message (DEBUG_SAVER, "Close input stream"); +	if (!g_input_stream_close (async->saver->priv->input, +				   async->cancellable, &error)) +	{ +		gedit_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message); +		cancel_output_stream_and_fail (async, error); +		return; +	} + +	/* now we close the output stream */ +	gedit_debug_message (DEBUG_SAVER, "Close output stream"); +	g_output_stream_close_async (async->saver->priv->stream, +				     G_PRIORITY_HIGH, +				     async->cancellable, +				     (GAsyncReadyCallback)close_async_ready_get_info_cb, +				     async); +} + +/* prototype, because they call each other... isn't C lovely */ +static void read_file_chunk (AsyncData *async); +static void write_file_chunk (AsyncData *async); + +static void +async_write_cb (GOutputStream *stream, +		GAsyncResult  *res, +		AsyncData     *async) +{ +	GeditGioDocumentSaver *gvsaver; +	gssize bytes_written; +	GError *error = NULL; + +	gedit_debug (DEBUG_SAVER); + +	/* Check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		cancel_output_stream (async); +		return; +	} + +	bytes_written = g_output_stream_write_finish (stream, res, &error); + +	gedit_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written); + +	if (bytes_written == -1) +	{ +		gedit_debug_message (DEBUG_SAVER, "Write error: %s", error->message); +		cancel_output_stream_and_fail (async, error); +		return; +	} + +	gvsaver = async->saver; +	async->written += bytes_written; + +	/* write again */ +	if (async->written != async->read) +	{ +		write_file_chunk (async); +		return; +	} + +	/* note that this signal blocks the write... check if it isn't +	 * a performance problem +	 */ +	gedit_document_saver_saving (GEDIT_DOCUMENT_SAVER (gvsaver), +				     FALSE, +				     NULL); + +	read_file_chunk (async); +} + +static void +write_file_chunk (AsyncData *async) +{ +	GeditGioDocumentSaver *gvsaver; + +	gedit_debug (DEBUG_SAVER); + +	gvsaver = async->saver; + +	g_output_stream_write_async (G_OUTPUT_STREAM (gvsaver->priv->stream), +				     async->buffer + async->written, +				     async->read - async->written, +				     G_PRIORITY_HIGH, +				     async->cancellable, +				     (GAsyncReadyCallback) async_write_cb, +				     async); +} + +static void +read_file_chunk (AsyncData *async) +{ +	GeditGioDocumentSaver *gvsaver; +	GeditDocumentInputStream *dstream; +	GError *error = NULL; + +	gedit_debug (DEBUG_SAVER); + +	gvsaver = async->saver; +	async->written = 0; + +	/* we use sync methods on doc stream since it is in memory. Using async +	   would be racy and we can endup with invalidated iters */ +	async->read = g_input_stream_read (gvsaver->priv->input, +					   async->buffer, +					   WRITE_CHUNK_SIZE, +					   async->cancellable, +					   &error); + +	if (error != NULL) +	{ +		cancel_output_stream_and_fail (async, error); +		return; +	} + +	/* Check if we finished reading and writing */ +	if (async->read == 0) +	{ +		write_complete (async); +		return; +	} + +	/* Get how many chars have been read */ +	dstream = GEDIT_DOCUMENT_INPUT_STREAM (gvsaver->priv->input); +	gvsaver->priv->bytes_written = gedit_document_input_stream_tell (dstream); + +	write_file_chunk (async); +} + +static void +async_replace_ready_callback (GFile        *source, +			      GAsyncResult *res, +			      AsyncData    *async) +{ +	GeditGioDocumentSaver *gvsaver; +	GeditDocumentSaver *saver; +	GCharsetConverter *converter; +	GFileOutputStream *file_stream; +	GError *error = NULL; + +	gedit_debug (DEBUG_SAVER); + +	/* Check cancelled state manually */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} +	 +	gvsaver = async->saver; +	saver = GEDIT_DOCUMENT_SAVER (gvsaver); +	file_stream = g_file_replace_finish (source, res, &error); +	 +	/* handle any error that might occur */ +	if (!file_stream) +	{ +		gedit_debug_message (DEBUG_SAVER, "Opening file failed: %s", error->message); +		async_failed (async, error); +		return; +	} + +	/* FIXME: manage converter error? */ +	gedit_debug_message (DEBUG_SAVER, "Encoding charset: %s", +			     gedit_encoding_get_charset (saver->encoding)); + +	if (saver->encoding != gedit_encoding_get_utf8 ()) +	{ +		converter = g_charset_converter_new (gedit_encoding_get_charset (saver->encoding), +						     "UTF-8", +						     NULL); +		gvsaver->priv->stream = g_converter_output_stream_new (G_OUTPUT_STREAM (file_stream), +								       G_CONVERTER (converter)); + +		g_object_unref (file_stream); +		g_object_unref (converter); +	} +	else +	{ +		gvsaver->priv->stream = G_OUTPUT_STREAM (file_stream); +	} +	 +	gvsaver->priv->input = gedit_document_input_stream_new (GTK_TEXT_BUFFER (saver->document), +								saver->newline_type); + +	gvsaver->priv->size = gedit_document_input_stream_get_total_size (GEDIT_DOCUMENT_INPUT_STREAM (gvsaver->priv->input)); + +	read_file_chunk (async); +} + +static void +begin_write (AsyncData *async) +{ +	GeditGioDocumentSaver *gvsaver; +	GeditDocumentSaver *saver; +	gboolean backup; + +	gedit_debug_message (DEBUG_SAVER, "Start replacing file contents"); + +	/* For remote files we simply use g_file_replace_async. There is no +	 * backup as of yet +	 */ +	gvsaver = async->saver; +	saver = GEDIT_DOCUMENT_SAVER (gvsaver); + +	/* Do not make backups for remote files so they do not clutter remote systems */ +	backup = (saver->keep_backup && gedit_document_is_local (saver->document)); + +	gedit_debug_message (DEBUG_SAVER, "File contents size: %" G_GINT64_FORMAT, gvsaver->priv->size); +	gedit_debug_message (DEBUG_SAVER, "Calling replace_async"); +	gedit_debug_message (DEBUG_SAVER, backup ? "Keep backup" : "Discard backup"); + +	g_file_replace_async (gvsaver->priv->gfile, +			      NULL, +			      backup, +			      G_FILE_CREATE_NONE, +			      G_PRIORITY_HIGH, +			      async->cancellable, +			      (GAsyncReadyCallback) async_replace_ready_callback, +			      async); +} + +static void +mount_ready_callback (GFile        *file, +		      GAsyncResult *res, +		      AsyncData    *async) +{ +	GError *error = NULL; +	gboolean mounted; + +	gedit_debug (DEBUG_SAVER); + +	/* manual check for cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} + +	mounted = g_file_mount_enclosing_volume_finish (file, res, &error); +	 +	if (!mounted) +	{ +		async_failed (async, error); +	} +	else +	{ +		/* try again to get the modified state */ +		check_modified_async (async); +	} +} + +static void +recover_not_mounted (AsyncData *async) +{ +	GeditDocument *doc; +	GMountOperation *mount_operation; +	 +	gedit_debug (DEBUG_LOADER); + +	doc = gedit_document_saver_get_document (GEDIT_DOCUMENT_SAVER (async->saver)); +	mount_operation = _gedit_document_create_mount_operation (doc); + +	async->tried_mount = TRUE; +	g_file_mount_enclosing_volume (async->saver->priv->gfile, +				       G_MOUNT_MOUNT_NONE, +				       mount_operation, +				       async->cancellable, +				       (GAsyncReadyCallback) mount_ready_callback, +				       async); + +	g_object_unref (mount_operation); +} + +static void +check_modification_callback (GFile        *source, +			     GAsyncResult *res, +			     AsyncData    *async) +{ +	GeditGioDocumentSaver *gvsaver; +	GError *error = NULL; +	GFileInfo *info; + +	gedit_debug (DEBUG_SAVER); + +	/* manually check cancelled state */ +	if (g_cancellable_is_cancelled (async->cancellable)) +	{ +		async_data_free (async); +		return; +	} +	 +	gvsaver = async->saver; +	info = g_file_query_info_finish (source, res, &error); +	if (info == NULL) +	{ +		if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount) +		{ +			recover_not_mounted (async); +			g_error_free (error); +			return; +		} +		 +		/* it's perfectly fine if the file doesn't exist yet */ +		if (error->code != G_IO_ERROR_NOT_FOUND) +		{ +			gedit_debug_message (DEBUG_SAVER, "Error getting modification: %s", error->message); + +			async_failed (async, error); +			return; +		} +	} + +	/* check if the mtime is > what we know about it (if we have it) */ +	if (info != NULL && g_file_info_has_attribute (info, +				       G_FILE_ATTRIBUTE_TIME_MODIFIED)) +	{ +		GTimeVal mtime; +		GTimeVal old_mtime; + +		g_file_info_get_modification_time (info, &mtime); +		old_mtime = gvsaver->priv->old_mtime; + +		if ((old_mtime.tv_sec > 0 || old_mtime.tv_usec > 0) && +		    (mtime.tv_sec != old_mtime.tv_sec || mtime.tv_usec != old_mtime.tv_usec) && +		    (GEDIT_DOCUMENT_SAVER (gvsaver)->flags & GEDIT_DOCUMENT_SAVE_IGNORE_MTIME) == 0) +		{ +			gedit_debug_message (DEBUG_SAVER, "File is externally modified"); +			g_set_error (&gvsaver->priv->error, +				     GEDIT_DOCUMENT_ERROR, +				     GEDIT_DOCUMENT_ERROR_EXTERNALLY_MODIFIED, +				     "Externally modified"); + +			remote_save_completed_or_failed (gvsaver, async); +			g_object_unref (info); + +			return; +		} +	} + +	if (info != NULL) +		g_object_unref (info); + +	/* modification check passed, start write */ +	begin_write (async); +} + +static void +check_modified_async (AsyncData *async) +{ +	gedit_debug_message (DEBUG_SAVER, "Check externally modified"); + +	g_file_query_info_async (async->saver->priv->gfile,  +				 G_FILE_ATTRIBUTE_TIME_MODIFIED, +				 G_FILE_QUERY_INFO_NONE, +				 G_PRIORITY_HIGH, +				 async->cancellable, +				 (GAsyncReadyCallback) check_modification_callback, +				 async); +} + +static gboolean +save_remote_file_real (GeditGioDocumentSaver *gvsaver) +{ +	AsyncData *async; + +	gedit_debug_message (DEBUG_SAVER, "Starting gio save"); +	 +	/* First find out if the file is modified externally. This requires +	 * a stat, but I don't think we can do this any other way +	 */ +	async = async_data_new (gvsaver); +	 +	check_modified_async (async); +	 +	/* return false to stop timeout */ +	return FALSE; +} + +static void +gedit_gio_document_saver_save (GeditDocumentSaver *saver, +			       GTimeVal           *old_mtime) +{ +	GeditGioDocumentSaver *gvsaver = GEDIT_GIO_DOCUMENT_SAVER (saver); + +	gvsaver->priv->old_mtime = *old_mtime; +	gvsaver->priv->gfile = g_file_new_for_uri (saver->uri); + +	/* saving start */ +	gedit_document_saver_saving (saver, FALSE, NULL); + +	g_timeout_add_full (G_PRIORITY_HIGH, +			    0, +			    (GSourceFunc) save_remote_file_real, +			    gvsaver, +			    NULL); +} + +static goffset +gedit_gio_document_saver_get_file_size (GeditDocumentSaver *saver) +{ +	return GEDIT_GIO_DOCUMENT_SAVER (saver)->priv->size; +} + +static goffset +gedit_gio_document_saver_get_bytes_written (GeditDocumentSaver *saver) +{ +	return GEDIT_GIO_DOCUMENT_SAVER (saver)->priv->bytes_written; +} diff --git a/gedit/gedit-gio-document-saver.h b/gedit/gedit-gio-document-saver.h new file mode 100755 index 00000000..18e05df1 --- /dev/null +++ b/gedit/gedit-gio-document-saver.h @@ -0,0 +1,76 @@ +/* + * gedit-gio-document-saver.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyrhing (C) 2007 - Paolo Maggi, Steve Frécinaux + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + */ + +#ifndef __GEDIT_GIO_DOCUMENT_SAVER_H__ +#define __GEDIT_GIO_DOCUMENT_SAVER_H__ + +#include <gedit/gedit-document-saver.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_GIO_DOCUMENT_SAVER              (gedit_gio_document_saver_get_type()) +#define GEDIT_GIO_DOCUMENT_SAVER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_GIO_DOCUMENT_SAVER, GeditGioDocumentSaver)) +#define GEDIT_GIO_DOCUMENT_SAVER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_GIO_DOCUMENT_SAVER, GeditGioDocumentSaverClass)) +#define GEDIT_IS_GIO_DOCUMENT_SAVER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_GIO_DOCUMENT_SAVER)) +#define GEDIT_IS_GIO_DOCUMENT_SAVER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_GIO_DOCUMENT_SAVER)) +#define GEDIT_GIO_DOCUMENT_SAVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_GIO_DOCUMENT_SAVER, GeditGioDocumentSaverClass)) + +/* Private structure type */ +typedef struct _GeditGioDocumentSaverPrivate GeditGioDocumentSaverPrivate; + +/* + * Main object structure + */ +typedef struct _GeditGioDocumentSaver GeditGioDocumentSaver; + +struct _GeditGioDocumentSaver  +{ +	GeditDocumentSaver saver; + +	/*< private > */ +	GeditGioDocumentSaverPrivate *priv; +}; + +/* + * Class definition + */ +typedef GeditDocumentSaverClass GeditGioDocumentSaverClass; + +/* + * Public methods + */ +GType 		 	 gedit_gio_document_saver_get_type	(void) G_GNUC_CONST; + +G_END_DECLS + +#endif  /* __GEDIT_GIO_DOCUMENT_SAVER_H__  */ diff --git a/gedit/gedit-help.c b/gedit/gedit-help.c new file mode 100755 index 00000000..8ddf6a96 --- /dev/null +++ b/gedit/gedit-help.c @@ -0,0 +1,122 @@ +/* + * gedit-help.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-help.h" + +#include <glib/gi18n.h> +#include <string.h> +#include <gtk/gtk.h> + +#ifdef OS_OSX +#include "osx/gedit-osx.h" +#endif + +gboolean     +gedit_help_display (GtkWindow   *parent, +		    const gchar *name, /* "gedit" if NULL */ +		    const gchar *link_id) +{ +	GError *error = NULL; +	gboolean ret; +	gchar *link; +	 +	g_return_val_if_fail ((parent == NULL) || GTK_IS_WINDOW (parent), FALSE); + +#ifdef OS_OSX +	if (name == NULL || strcmp(name, "gedit.xml") == NULL || strcmp(name, "gedit") == 0) +	{ +		return gedit_osx_show_help (link_id); +	} +	else +	{ +		return FALSE; +	} +#endif + +	if (name == NULL) +		name = "gedit"; +	else if (strcmp (name, "gedit.xml") == 0) +	{ +		g_warning ("%s: Using \"gedit.xml\" for the help name is deprecated, use \"gedit\" or simply NULL instead", G_STRFUNC); +		 +		name = "gedit"; +	} + +#ifndef G_OS_WIN32 +	if (link_id) +		link = g_strdup_printf ("ghelp:%s?%s", name, link_id); +	else +		link = g_strdup_printf ("ghelp:%s", name); +#else +	if (link_id) +		link = g_strdup_printf ("http://library.mate.org/users/gedit/stable/%s", +					link_id); +	else +		link = g_strdup ("http://library.mate.org/users/gedit/stable/"); +#endif + +	ret = gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (parent)), +	                    link,  +			    GDK_CURRENT_TIME,  +			    &error); + +	g_free (link); + +	if (error != NULL) +	{ +		GtkWidget *dialog; + +		dialog = gtk_message_dialog_new (parent, +						 GTK_DIALOG_DESTROY_WITH_PARENT, +						 GTK_MESSAGE_ERROR, +						 GTK_BUTTONS_CLOSE,  +						 _("There was an error displaying the help.")); + +		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), +							  "%s", error->message); + +		g_signal_connect (G_OBJECT (dialog), +				  "response", +				  G_CALLBACK (gtk_widget_destroy), +				  NULL); + +		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + +		gtk_widget_show (dialog); + +		g_error_free (error); +	} + +	return ret; +} diff --git a/gedit/gedit-help.h b/gedit/gedit-help.h new file mode 100755 index 00000000..ae59afdc --- /dev/null +++ b/gedit/gedit-help.h @@ -0,0 +1,44 @@ +/* + * gedit-help.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_HELP_H__ +#define __GEDIT_HELP_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +gboolean	gedit_help_display (GtkWindow   *parent, +				    const gchar *name, /* "gedit" if NULL */ +				    const gchar *link_id); +				     +G_END_DECLS + +#endif /* __GEDIT_HELP_H__ */ diff --git a/gedit/gedit-history-entry.c b/gedit/gedit-history-entry.c new file mode 100755 index 00000000..999d4097 --- /dev/null +++ b/gedit/gedit-history-entry.c @@ -0,0 +1,632 @@ +/* + * gedit-history-entry.c + * This file is part of gedit + * + * Copyright (C) 2006 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2006. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +#include "gedit-history-entry.h" + +enum { +	PROP_0, +	PROP_HISTORY_ID, +	PROP_HISTORY_LENGTH +}; + +#define MIN_ITEM_LEN 3 + +#define GEDIT_HISTORY_ENTRY_HISTORY_LENGTH_DEFAULT 10 + +#define GEDIT_HISTORY_ENTRY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +						GEDIT_TYPE_HISTORY_ENTRY, \ +						GeditHistoryEntryPrivate)) + +struct _GeditHistoryEntryPrivate +{ +	gchar              *history_id; +	guint               history_length; +	 +	GtkEntryCompletion *completion; +	 +	MateConfClient        *mateconf_client; +}; + +G_DEFINE_TYPE(GeditHistoryEntry, gedit_history_entry, GTK_TYPE_COMBO_BOX_ENTRY) + +static void +gedit_history_entry_set_property (GObject      *object, +				  guint         prop_id, +				  const GValue *value, +				  GParamSpec   *spec) +{ +	GeditHistoryEntry *entry; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (object)); + +	entry = GEDIT_HISTORY_ENTRY (object); + +	switch (prop_id) { +	case PROP_HISTORY_ID: +		entry->priv->history_id = g_value_dup_string (value); +		break; +	case PROP_HISTORY_LENGTH: +		gedit_history_entry_set_history_length (entry, +						     g_value_get_uint (value)); +		break; +	default: +		break; +	} +} + +static void +gedit_history_entry_get_property (GObject    *object, +				  guint       prop_id, +				  GValue     *value, +				  GParamSpec *spec) +{ +	GeditHistoryEntryPrivate *priv; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (object)); + +	priv = GEDIT_HISTORY_ENTRY (object)->priv; + +	switch (prop_id) { +	case PROP_HISTORY_ID: +		g_value_set_string (value, priv->history_id); +		break; +	case PROP_HISTORY_LENGTH: +		g_value_set_uint (value, priv->history_length); +		break; +	default: +		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); +	} +} + +static void +gedit_history_entry_destroy (GtkObject *object) +{ +	gedit_history_entry_set_enable_completion (GEDIT_HISTORY_ENTRY (object), +						   FALSE); + +	GTK_OBJECT_CLASS (gedit_history_entry_parent_class)->destroy (object); +} + +static void +gedit_history_entry_finalize (GObject *object) +{ +	GeditHistoryEntryPrivate *priv; + +	priv = GEDIT_HISTORY_ENTRY (object)->priv; +	 +	g_free (priv->history_id); + +	if (priv->mateconf_client != NULL) +	{ +		g_object_unref (G_OBJECT (priv->mateconf_client)); +		priv->mateconf_client = NULL; +	} + +	G_OBJECT_CLASS (gedit_history_entry_parent_class)->finalize (object); +} + +static void  +gedit_history_entry_class_init (GeditHistoryEntryClass *klass) +{ +	GObjectClass   *object_class = G_OBJECT_CLASS (klass); +	GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); +	 +	object_class->set_property = gedit_history_entry_set_property; +	object_class->get_property = gedit_history_entry_get_property; +	object_class->finalize = gedit_history_entry_finalize; +	gtkobject_class->destroy = gedit_history_entry_destroy; +	 +	g_object_class_install_property (object_class, +					 PROP_HISTORY_ID, +					 g_param_spec_string ("history-id", +							      "History ID", +							      "History ID", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_HISTORY_LENGTH, +					 g_param_spec_uint ("history-length", +							    "Max History Length", +							    "Max History Length", +							    0, +							    G_MAXUINT, +							    GEDIT_HISTORY_ENTRY_HISTORY_LENGTH_DEFAULT, +							    G_PARAM_READWRITE | +							    G_PARAM_STATIC_STRINGS)); + +	/* TODO: Add enable-completion property */ + +	g_type_class_add_private (object_class, sizeof(GeditHistoryEntryPrivate)); +} + +static GtkListStore * +get_history_store (GeditHistoryEntry *entry) +{ +	GtkTreeModel *store; + +	store = gtk_combo_box_get_model (GTK_COMBO_BOX (entry)); +	g_return_val_if_fail (GTK_IS_LIST_STORE (store), NULL); + +	return (GtkListStore *) store; +} + +static char * +get_history_key (GeditHistoryEntry *entry) +{ +	gchar *tmp; +	gchar *key; + +	/* +	 * We store the data under /apps/mate-settings/ +	 * like the old MateEntry did. Maybe we should +	 * consider moving it to the /gedit MateConf prefix... +	 * Or maybe we should just switch away from MateConf. +	 */ + +	tmp = mateconf_escape_key (entry->priv->history_id, -1); +	key = g_strconcat ("/apps/mate-settings/", +			   "gedit", +			   "/history-", +			   tmp, +			   NULL); +	g_free (tmp); + +	return key; +} + +static GSList * +get_history_list (GeditHistoryEntry *entry) +{ +	GtkListStore *store; +	GtkTreeIter iter; +	gboolean valid; +	GSList *list = NULL; + +	store = get_history_store (entry); + +	valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), +					       &iter); + +	while (valid) +	{ +		gchar *str; + +		gtk_tree_model_get (GTK_TREE_MODEL (store), +				    &iter, +				    0, &str, +				    -1); + +		list = g_slist_prepend (list, str); + +		valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), +						  &iter); +	} + +	return g_slist_reverse (list); +} + +static void +gedit_history_entry_save_history (GeditHistoryEntry *entry) +{ +	GSList *mateconf_items; +	gchar *key; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); + +	mateconf_items = get_history_list (entry); +	key = get_history_key (entry); + +	mateconf_client_set_list (entry->priv->mateconf_client, +			      key, +			      MATECONF_VALUE_STRING, +			      mateconf_items, +			      NULL); + +	g_slist_foreach (mateconf_items, (GFunc) g_free, NULL); +	g_slist_free (mateconf_items); +	g_free (key); +} + +static gboolean +remove_item (GtkListStore *store, +	     const gchar  *text) +{ +	GtkTreeIter iter; + +	g_return_val_if_fail (text != NULL, FALSE); + +	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) +		return FALSE; + +	do +	{ +		gchar *item_text; + +		gtk_tree_model_get (GTK_TREE_MODEL (store), +				    &iter, +				    0, +				    &item_text, +				    -1); + +		if (item_text != NULL && +		    strcmp (item_text, text) == 0) +		{ +			gtk_list_store_remove (store, &iter); +			g_free (item_text); +			return TRUE; +		} + +		g_free (item_text); + +	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + +	return FALSE; +} + +static void +clamp_list_store (GtkListStore *store, +		  guint         max) +{ +	GtkTreePath *path; +	GtkTreeIter iter; + +	/* -1 because TreePath counts from 0 */ +	path = gtk_tree_path_new_from_indices (max - 1, -1); + +	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) +	{ +		while (1) +		{ +			if (!gtk_list_store_remove (store, &iter)) +				break; +		} +	} + +	gtk_tree_path_free (path); +} + +static void +insert_history_item (GeditHistoryEntry *entry, +		     const gchar       *text, +		     gboolean           prepend) +{ +	GtkListStore *store; +	GtkTreeIter iter; + +	if (g_utf8_strlen (text, -1) <= MIN_ITEM_LEN) +		return; +		 +	store = get_history_store (entry); + +	/* remove the text from the store if it was already +	 * present. If it wasn't, clamp to max history - 1 +	 * before inserting the new row, otherwise appending +	 * would not work */ + +	if (!remove_item (store, text)) +		clamp_list_store (store, +				  entry->priv->history_length - 1); + +	if (prepend) +		gtk_list_store_insert (store, &iter, 0); +	else +		gtk_list_store_append (store, &iter); + +	gtk_list_store_set (store, +			    &iter, +			    0, +			    text, +			    -1); + +	gedit_history_entry_save_history (entry); +} + +void +gedit_history_entry_prepend_text (GeditHistoryEntry *entry, +				  const gchar       *text) +{ +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); +	g_return_if_fail (text != NULL); + +	insert_history_item (entry, text, TRUE); +} + +void +gedit_history_entry_append_text (GeditHistoryEntry *entry, +				 const gchar       *text) +{ +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); +	g_return_if_fail (text != NULL); + +	insert_history_item (entry, text, FALSE); +} + +static void +gedit_history_entry_load_history (GeditHistoryEntry *entry) +{ +	GSList *mateconf_items, *l; +	GtkListStore *store; +	GtkTreeIter iter; +	gchar *key; +	guint i; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); + +	store = get_history_store (entry); +	key = get_history_key (entry); + +	mateconf_items = mateconf_client_get_list (entry->priv->mateconf_client, +					     key, +					     MATECONF_VALUE_STRING, +					     NULL); + +	gtk_list_store_clear (store); + +	for (l = mateconf_items, i = 0; +	     l != NULL && i < entry->priv->history_length; +	     l = l->next, i++) +	{ +		gtk_list_store_append (store, &iter); +		gtk_list_store_set (store,  +				    &iter, +				    0, +				    l->data, +				    -1); +	} + +	g_slist_foreach (mateconf_items, (GFunc) g_free, NULL); +	g_slist_free (mateconf_items); +	g_free (key); +} + +void +gedit_history_entry_clear (GeditHistoryEntry *entry) +{ +	GtkListStore *store; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); + +	store = get_history_store (entry); +	gtk_list_store_clear (store); + +	gedit_history_entry_save_history (entry); +} + +static void +gedit_history_entry_init (GeditHistoryEntry *entry) +{ +	GeditHistoryEntryPrivate *priv; + +	priv = GEDIT_HISTORY_ENTRY_GET_PRIVATE (entry); +	entry->priv = priv; + +	priv->history_id = NULL; +	priv->history_length = GEDIT_HISTORY_ENTRY_HISTORY_LENGTH_DEFAULT; + +	priv->completion = NULL; +	 +	priv->mateconf_client = mateconf_client_get_default (); +} + +void +gedit_history_entry_set_history_length (GeditHistoryEntry *entry, +					guint              history_length) +{ +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); +	g_return_if_fail (history_length > 0); + +	entry->priv->history_length = history_length; + +	/* TODO: update if we currently have more items than max */ +} + +guint +gedit_history_entry_get_history_length (GeditHistoryEntry *entry) +{ +	g_return_val_if_fail (GEDIT_IS_HISTORY_ENTRY (entry), 0); + +	return entry->priv->history_length; +} + +gchar * +gedit_history_entry_get_history_id (GeditHistoryEntry *entry) +{ +	g_return_val_if_fail (GEDIT_IS_HISTORY_ENTRY (entry), NULL); + +	return g_strdup (entry->priv->history_id); +} + +void +gedit_history_entry_set_enable_completion (GeditHistoryEntry *entry, +					   gboolean           enable) +{ +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); +	 +	if (enable) +	{ +		if (entry->priv->completion != NULL) +			return; +		 +		entry->priv->completion = gtk_entry_completion_new (); +		gtk_entry_completion_set_model (entry->priv->completion,  +						GTK_TREE_MODEL (get_history_store (entry))); +		 +		/* Use model column 0 as the text column */ +		gtk_entry_completion_set_text_column (entry->priv->completion, 0); + +		gtk_entry_completion_set_minimum_key_length (entry->priv->completion, +							     MIN_ITEM_LEN); + +		gtk_entry_completion_set_popup_completion (entry->priv->completion, FALSE); +		gtk_entry_completion_set_inline_completion (entry->priv->completion, TRUE); +	 +		/* Assign the completion to the entry */ +		gtk_entry_set_completion (GTK_ENTRY (gedit_history_entry_get_entry(entry)),  +					  entry->priv->completion); +	} +	else +	{ +		if (entry->priv->completion == NULL) +			return; + +		gtk_entry_set_completion (GTK_ENTRY (gedit_history_entry_get_entry (entry)),  +					  NULL); +		 +		g_object_unref (entry->priv->completion); +		 +		entry->priv->completion = NULL; +	} +} +							  +gboolean +gedit_history_entry_get_enable_completion (GeditHistoryEntry *entry) +{ +	g_return_val_if_fail (GEDIT_IS_HISTORY_ENTRY (entry), FALSE); +	 +	return entry->priv->completion != NULL; +} + +GtkWidget * +gedit_history_entry_new (const gchar *history_id, +			 gboolean     enable_completion) +{ +	GtkWidget *ret; +	GtkListStore *store; + +	g_return_val_if_fail (history_id != NULL, NULL); + +	/* Note that we are setting the model, so +	 * user must be careful to always manipulate +	 * data in the history through gedit_history_entry_ +	 * functions. +	 */ + +	store = gtk_list_store_new (1, G_TYPE_STRING); + +	ret = g_object_new (GEDIT_TYPE_HISTORY_ENTRY, +			    "history-id", history_id, +	                    "model", store, +			    "text-column", 0, +	                    NULL); + +	g_object_unref (store); + +	/* loading has to happen after the model +	 * has been set. However the model is not a  +	 * G_PARAM_CONSTRUCT property of GtkComboBox +	 * so we cannot do this in the constructor. +	 * For now we simply do here since this widget is  +	 * not bound to other programming languages. +	 * A maybe better alternative is to override the  +	 * model property of combobox and mark CONTRUCT_ONLY. +	 * This would also ensure that the model cannot be +	 * set explicitely at a later time. +	 */ +	gedit_history_entry_load_history (GEDIT_HISTORY_ENTRY (ret)); + +	gedit_history_entry_set_enable_completion (GEDIT_HISTORY_ENTRY (ret), +						   enable_completion); +						    +	return ret; +} + +/* + * Utility function to get the editable text entry internal widget. + * I would prefer to not expose this implementation detail and  + * simply make the GeditHistoryEntry widget implement the  + * GtkEditable interface. Unfortunately both GtkEditable and + * GtkComboBox have a "changed" signal and I am not sure how to + * handle the conflict. + */ +GtkWidget * +gedit_history_entry_get_entry (GeditHistoryEntry *entry) +{ +	g_return_val_if_fail (GEDIT_IS_HISTORY_ENTRY (entry), NULL); + +	return gtk_bin_get_child (GTK_BIN (entry)); +} + +static void +escape_cell_data_func (GtkTreeViewColumn           *col, +		       GtkCellRenderer             *renderer, +		       GtkTreeModel                *model, +		       GtkTreeIter                 *iter, +		       GeditHistoryEntryEscapeFunc  escape_func) +{ +	gchar *str; +	gchar *escaped; + +	gtk_tree_model_get (model, iter, 0, &str, -1); +	escaped = escape_func (str); +	g_object_set (renderer, "text", escaped, NULL); + +	g_free (str); +	g_free (escaped); +} + +void +gedit_history_entry_set_escape_func (GeditHistoryEntry           *entry, +				     GeditHistoryEntryEscapeFunc  escape_func) +{ +	GList *cells; + +	g_return_if_fail (GEDIT_IS_HISTORY_ENTRY (entry)); + +	cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (entry)); + +	/* We only have one cell renderer */ +	g_return_if_fail (cells->data != NULL && cells->next == NULL); + +	if (escape_func != NULL) +		gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry), +						    GTK_CELL_RENDERER (cells->data), +						    (GtkCellLayoutDataFunc) escape_cell_data_func, +						    escape_func, +						    NULL); +	else +		gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry), +						    GTK_CELL_RENDERER (cells->data), +						    NULL, +						    NULL, +						    NULL); + +	g_list_free (cells); +} diff --git a/gedit/gedit-history-entry.h b/gedit/gedit-history-entry.h new file mode 100755 index 00000000..59e810c2 --- /dev/null +++ b/gedit/gedit-history-entry.h @@ -0,0 +1,96 @@ +/* + * gedit-history-entry.h + * This file is part of gedit + * + * Copyright (C) 2006 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2006. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_HISTORY_ENTRY_H__ +#define __GEDIT_HISTORY_ENTRY_H__ + + +G_BEGIN_DECLS + +#define GEDIT_TYPE_HISTORY_ENTRY             (gedit_history_entry_get_type ()) +#define GEDIT_HISTORY_ENTRY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_HISTORY_ENTRY, GeditHistoryEntry)) +#define GEDIT_HISTORY_ENTRY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_HISTORY_ENTRY, GeditHistoryEntryClass)) +#define GEDIT_IS_HISTORY_ENTRY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_HISTORY_ENTRY)) +#define GEDIT_IS_HISTORY_ENTRY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_HISTORY_ENTRY)) +#define GEDIT_HISTORY_ENTRY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_HISTORY_ENTRY, GeditHistoryEntryClass)) + + +typedef struct _GeditHistoryEntry        GeditHistoryEntry; +typedef struct _GeditHistoryEntryClass   GeditHistoryEntryClass; +typedef struct _GeditHistoryEntryPrivate GeditHistoryEntryPrivate; + +struct _GeditHistoryEntryClass +{ +	GtkComboBoxEntryClass parent_class; +}; + +struct _GeditHistoryEntry +{ +	GtkComboBoxEntry parent_instance; + +	GeditHistoryEntryPrivate *priv; +}; + +GType		 gedit_history_entry_get_type	(void) G_GNUC_CONST; + +GtkWidget	*gedit_history_entry_new		(const gchar       *history_id, +							 gboolean           enable_completion); + +void		 gedit_history_entry_prepend_text	(GeditHistoryEntry *entry, +							 const gchar       *text); + +void		 gedit_history_entry_append_text	(GeditHistoryEntry *entry, +							 const gchar       *text); + +void		 gedit_history_entry_clear		(GeditHistoryEntry *entry); + +void		 gedit_history_entry_set_history_length	(GeditHistoryEntry *entry, +							 guint              max_saved); + +guint		 gedit_history_entry_get_history_length	(GeditHistoryEntry *gentry); + +gchar		*gedit_history_entry_get_history_id	(GeditHistoryEntry *entry); + +void             gedit_history_entry_set_enable_completion  +							(GeditHistoryEntry *entry, +							 gboolean           enable); +							  +gboolean         gedit_history_entry_get_enable_completion  +							(GeditHistoryEntry *entry); + +GtkWidget	*gedit_history_entry_get_entry		(GeditHistoryEntry *entry); + +typedef gchar * (* GeditHistoryEntryEscapeFunc) (const gchar *str); +void		gedit_history_entry_set_escape_func	(GeditHistoryEntry *entry, +							 GeditHistoryEntryEscapeFunc escape_func); + +G_END_DECLS + +#endif /* __GEDIT_HISTORY_ENTRY_H__ */ diff --git a/gedit/gedit-io-error-message-area.c b/gedit/gedit-io-error-message-area.c new file mode 100755 index 00000000..e116a15b --- /dev/null +++ b/gedit/gedit-io-error-message-area.c @@ -0,0 +1,1288 @@ +/* + * gedit-io-error-message-area.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ +  +/* + * Verbose error reporting for file I/O operations (load, save, revert, create) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gedit-utils.h" +#include "gedit-document.h" +#include "gedit-io-error-message-area.h" +#include "gedit-prefs-manager.h" +#include <gedit/gedit-encodings-combo-box.h> + +#if !GTK_CHECK_VERSION (2, 17, 1) +#include "gedit-message-area.h" +#endif + +#define MAX_URI_IN_DIALOG_LENGTH 50 + +static gboolean +is_recoverable_error (const GError *error) +{ +	gboolean is_recoverable = FALSE; + +	if (error->domain == G_IO_ERROR) +	{ +		switch (error->code) { +		case G_IO_ERROR_PERMISSION_DENIED: +		case G_IO_ERROR_NOT_FOUND: +		case G_IO_ERROR_HOST_NOT_FOUND: +		case G_IO_ERROR_TIMED_OUT: +		case G_IO_ERROR_NOT_MOUNTABLE_FILE: +		case G_IO_ERROR_NOT_MOUNTED: +		case G_IO_ERROR_BUSY: +			is_recoverable = TRUE; +		} +	} + +	return is_recoverable; +} + +static gboolean +is_gio_error (const GError *error, +	      gint          code) +{ +	return error->domain == G_IO_ERROR && error->code == code; +} + +static void +set_contents (GtkWidget *area, +	      GtkWidget *contents) +{ +#if !GTK_CHECK_VERSION (2, 17, 1) +	gedit_message_area_set_contents (GEDIT_MESSAGE_AREA (area), +					 contents); +#else +	GtkWidget *content_area; +	 +	content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (area)); +	gtk_container_add (GTK_CONTAINER (content_area), contents); +#endif +} + +#if GTK_CHECK_VERSION (2, 17, 1) +static void +info_bar_add_stock_button_with_text (GtkInfoBar  *infobar, +				     const gchar *text, +				     const gchar *stock_id, +				     gint         response_id) +{ +	GtkWidget *button; +	GtkWidget *image; + +	button = gtk_info_bar_add_button (infobar, text, response_id); +	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); +	gtk_button_set_image (GTK_BUTTON (button), image); +} +#endif + +static void +set_message_area_text_and_icon (GtkWidget   *message_area, +				const gchar *icon_stock_id, +				const gchar *primary_text, +				const gchar *secondary_text) +{ +	GtkWidget *hbox_content; +	GtkWidget *image; +	GtkWidget *vbox; +	gchar *primary_markup; +	gchar *secondary_markup; +	GtkWidget *primary_label; +	GtkWidget *secondary_label; + +	hbox_content = gtk_hbox_new (FALSE, 8); + +	image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG); +	gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + +	primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); +	primary_label = gtk_label_new (primary_markup); +	g_free (primary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); +	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	if (secondary_text != NULL) +	{ +		secondary_markup = g_strdup_printf ("<small>%s</small>", +						    secondary_text); +		secondary_label = gtk_label_new (secondary_markup); +		g_free (secondary_markup); +		gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); +		GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); +		gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); +		gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +		gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); +		gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); +	} + +	gtk_widget_show_all (hbox_content); +	set_contents (message_area, hbox_content); +} + +static GtkWidget * +create_io_loading_error_message_area (const gchar *primary_text, +				      const gchar *secondary_text, +				      gboolean     recoverable_error) +{ +	GtkWidget *message_area; + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new_with_buttons ( +					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, +					NULL); +#else +	message_area = gtk_info_bar_new_with_buttons ( +					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, +					NULL); +	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +				       GTK_MESSAGE_ERROR); +#endif + +	set_message_area_text_and_icon (message_area, +					"gtk-dialog-error", +					primary_text, +					secondary_text); + +	if (recoverable_error) +	{ +#if !GTK_CHECK_VERSION (2, 17, 1) +		gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (message_area), +							       _("_Retry"), +							       GTK_STOCK_REFRESH, +							       GTK_RESPONSE_OK); +#else +		info_bar_add_stock_button_with_text (GTK_INFO_BAR (message_area), +						     _("_Retry"), +						     GTK_STOCK_REFRESH, +						     GTK_RESPONSE_OK); +#endif +	} + +	return message_area; +} + +static gboolean +parse_gio_error (gint          code, +	         gchar       **error_message,  +	         gchar       **message_details,  +	         const gchar  *uri,  +	         const gchar  *uri_for_display) +{ +	gboolean ret = TRUE; + +	switch (code) +	{ +	case G_IO_ERROR_NOT_FOUND: +	case G_IO_ERROR_NOT_DIRECTORY: +		*error_message = g_strdup_printf (_("Could not find the file %s."), +						  uri_for_display); +		*message_details = g_strdup (_("Please check that you typed the " +				      	       "location correctly and try again.")); +		break; +	case G_IO_ERROR_NOT_SUPPORTED: +		{ +			gchar *scheme_string; +			gchar *scheme_markup; +			 +			scheme_string = g_uri_parse_scheme (uri); + +			if ((scheme_string != NULL) && g_utf8_validate (scheme_string, -1, NULL)) +			{ +				scheme_markup = g_markup_printf_escaped ("<i>%s:</i>", scheme_string); + +				/* Translators: %s is a URI scheme (like for example http:, ftp:, etc.) */ +				*message_details = g_strdup_printf (_("gedit cannot handle %s locations."), +								   scheme_markup); +				g_free (scheme_markup); +			} +			else +			{ +				*message_details = g_strdup (_("gedit cannot handle this location.")); +			}	 + +			g_free (scheme_string); +		} +		break; + +	case G_IO_ERROR_NOT_MOUNTABLE_FILE: +		*message_details = g_strdup (_("The location of the file cannot be mounted.")); +		break; +	 +	case G_IO_ERROR_NOT_MOUNTED: +		*message_details = g_strdup( _("The location of the file cannot be accessed because it is not mounted.")); + +		break;	 +	case G_IO_ERROR_IS_DIRECTORY: +		*error_message = g_strdup_printf (_("%s is a directory."), +						 uri_for_display); +		*message_details = g_strdup (_("Please check that you typed the " +					      "location correctly and try again.")); +		break; +		 +	case G_IO_ERROR_INVALID_FILENAME: +		*error_message = g_strdup_printf (_("%s is not a valid location."), +						 uri_for_display); +		*message_details = g_strdup (_("Please check that you typed the " +					      "location correctly and try again.")); +		break; +		 +	case G_IO_ERROR_HOST_NOT_FOUND: +		/* This case can be hit for user-typed strings like "foo" due to +		 * the code that guesses web addresses when there's no initial "/". +		 * But this case is also hit for legitimate web addresses when +		 * the proxy is set up wrong. +		 */ +		{ +			gchar *hn = NULL; + +			if (gedit_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) +			{ +				if (hn != NULL) +				{ +					gchar *host_markup; +					gchar *host_name; + +					host_name = gedit_utils_make_valid_utf8 (hn); +					g_free (hn); + +					host_markup = g_markup_printf_escaped ("<i>%s</i>", host_name); +					g_free (host_name); + +					/* Translators: %s is a host name */ +					*message_details = g_strdup_printf ( +						_("Host %s could not be found. " +						"Please check that your proxy settings " +						"are correct and try again."), +						host_markup); + +					g_free (host_markup); +				} +			} +			 +			if (!*message_details) +			{ +				/* use the same string as INVALID_HOST */ +				*message_details = g_strdup_printf ( +					_("Hostname was invalid. " +					  "Please check that you typed the location " +					  "correctly and try again.")); +			} +		} +		break; + +	case G_IO_ERROR_NOT_REGULAR_FILE: +		*message_details = g_strdup_printf (_("%s is not a regular file."), +						   uri_for_display); +		break; + +	case G_IO_ERROR_TIMED_OUT: +		*message_details = g_strdup (_("Connection timed out. Please try again.")); +		break; + +	default: +		ret = FALSE; +		break; +	} + +	return ret; +} + +static gboolean +parse_gedit_error (gint          code, +	           gchar       **error_message,  +	           gchar       **message_details,  +	           const gchar  *uri,  +	           const gchar  *uri_for_display) +{ +	gboolean ret = TRUE; + +	switch (code) +	{ +	case GEDIT_DOCUMENT_ERROR_TOO_BIG: +		*message_details = g_strdup (_("The file is too big.")); +		break; + +	default: +		ret = FALSE; +		break; +	} + +	return ret; +} + +static void +parse_error (const GError *error,  +	     gchar       **error_message,  +	     gchar       **message_details,  +	     const gchar  *uri,  +	     const gchar  *uri_for_display) +{ +	gboolean ret = FALSE; + +	if (error->domain == G_IO_ERROR) +	{ +		ret = parse_gio_error (error->code, +				       error_message, +				       message_details, +				       uri, +				       uri_for_display); +	} +	else if (error->domain == GEDIT_DOCUMENT_ERROR) +	{ +		ret = parse_gedit_error (error->code, +					 error_message, +					 message_details, +					 uri, +					 uri_for_display); +	} +	 +	if (!ret) +	{ +		g_warning ("Hit unhandled case %d (%s) in %s.",  +			   error->code, error->message, G_STRFUNC);	 +		*message_details = g_strdup_printf (_("Unexpected error: %s"),  +						   error->message); +	} +} + +GtkWidget * +gedit_unrecoverable_reverting_error_message_area_new (const gchar  *uri, +						      const GError *error) +{ +	gchar *error_message = NULL; +	gchar *message_details = NULL; +	gchar *full_formatted_uri; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	GtkWidget *message_area; + +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail ((error->domain == GEDIT_DOCUMENT_ERROR) ||  +			      (error->domain == G_IO_ERROR), NULL); + +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH);								 +	g_free (full_formatted_uri); + +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +	if (is_gio_error (error, G_IO_ERROR_NOT_FOUND)) +	{ +		message_details = g_strdup (_("gedit cannot find the file. " +					      "Perhaps it has recently been deleted.")); +	} +	else +	{ +		parse_error (error, &error_message, &message_details, uri, uri_for_display); +	} + +	if (error_message == NULL) +	{ +		error_message = g_strdup_printf (_("Could not revert the file %s."), +						 uri_for_display); +	} + +	message_area = create_io_loading_error_message_area (error_message, +							     message_details, +							     FALSE); + +	g_free (uri_for_display); +	g_free (error_message); +	g_free (message_details); + +	return message_area; +} + +static void +create_combo_box (GtkWidget *message_area, GtkWidget *vbox) +{ +	GtkWidget *hbox; +	GtkWidget *label; +	GtkWidget *menu; +	gchar *label_markup; + +	hbox = gtk_hbox_new (FALSE, 6); + +	label_markup = g_strdup_printf ("<small>%s</small>", +					_("Ch_aracter Encoding:")); +	label = gtk_label_new_with_mnemonic (label_markup); +	g_free (label_markup); +	gtk_label_set_use_markup (GTK_LABEL (label), TRUE); +	menu = gedit_encodings_combo_box_new (TRUE); +	g_object_set_data (G_OBJECT (message_area),  +			   "gedit-message-area-encoding-menu",  +			   menu); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu); +	gtk_box_pack_start (GTK_BOX (hbox), +			    label, +			    FALSE, +			    FALSE, +			    0); + +	gtk_box_pack_start (GTK_BOX (hbox), +			    menu, +			    FALSE, +			    FALSE, +			    0); + +	gtk_widget_show_all (hbox); +	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); +} + +static GtkWidget * +create_conversion_error_message_area (const gchar *primary_text, +				      const gchar *secondary_text, +				      gboolean     edit_anyway) +{ +	GtkWidget *message_area; +	GtkWidget *hbox_content; +	GtkWidget *image; +	GtkWidget *vbox; +	gchar *primary_markup; +	gchar *secondary_markup; +	GtkWidget *primary_label; +	GtkWidget *secondary_label; + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new (); + +	gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (message_area), +						       _("_Retry"), +						       GTK_STOCK_REDO, +						       GTK_RESPONSE_OK); + +	if (edit_anyway) +	{ +		gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +					       _("Edit Any_way"), +					       GTK_RESPONSE_YES); +		gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +					       _("D_on't Edit"), +					       GTK_RESPONSE_NO); +	} +	else +	{ +		gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +					       GTK_STOCK_CANCEL, +					       GTK_RESPONSE_CANCEL); +	} +#else +	message_area = gtk_info_bar_new (); + +	info_bar_add_stock_button_with_text (GTK_INFO_BAR (message_area), +					     _("_Retry"), +					     GTK_STOCK_REDO, +					     GTK_RESPONSE_OK); + +	if (edit_anyway) +	{ +		gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +		/* Translators: the access key chosen for this string should be +		 different from other main menu access keys (Open, Edit, View...) */ +					 _("Edit Any_way"), +					 GTK_RESPONSE_YES); +		gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +		/* Translators: the access key chosen for this string should be +		 different from other main menu access keys (Open, Edit, View...) */ +					 _("D_on't Edit"), +					 GTK_RESPONSE_NO); +		gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +					       GTK_MESSAGE_WARNING); +	} +	else +	{ +		gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +					 GTK_STOCK_CANCEL, +					 GTK_RESPONSE_CANCEL); +		gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +					       GTK_MESSAGE_ERROR); +	} +#endif + +	hbox_content = gtk_hbox_new (FALSE, 8); + +	image = gtk_image_new_from_stock ("gtk-dialog-error", GTK_ICON_SIZE_DIALOG); +	gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); +   +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); +	 +	primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); +	primary_label = gtk_label_new (primary_markup); +	g_free (primary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); +	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	if (secondary_text != NULL) +	{ +		secondary_markup = g_strdup_printf ("<small>%s</small>", +						    secondary_text); +		secondary_label = gtk_label_new (secondary_markup); +		g_free (secondary_markup); +		gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); +		GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); +		gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); +		gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +		gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); +		gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); +	} + +	create_combo_box (message_area, vbox); +	gtk_widget_show_all (hbox_content); +	set_contents (message_area, hbox_content); + +	return message_area; +} + +GtkWidget * +gedit_io_loading_error_message_area_new (const gchar         *uri, +					 const GeditEncoding *encoding, +					 const GError        *error) +{ +	gchar *error_message = NULL; +	gchar *message_details = NULL; +	gchar *full_formatted_uri; +	gchar *encoding_name; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	GtkWidget *message_area; +	gboolean edit_anyway = FALSE; +	gboolean convert_error = FALSE; +	 +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail ((error->domain == G_CONVERT_ERROR) || +			      (error->domain == GEDIT_DOCUMENT_ERROR) || +			      (error->domain == G_IO_ERROR), NULL); +	 +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri, +								MAX_URI_IN_DIALOG_LENGTH); +	g_free (full_formatted_uri); +	 +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +	if (encoding != NULL) +		encoding_name = gedit_encoding_to_string (encoding); +	else +		encoding_name = g_strdup ("UTF-8"); + +	if (is_gio_error (error, G_IO_ERROR_TOO_MANY_LINKS)) +	{ +		message_details = g_strdup (_("The number of followed links is limited and the actual file could not be found within this limit.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_PERMISSION_DENIED)) +	{ +		message_details = g_strdup (_("You do not have the permissions necessary to open the file.")); +	} +	else if ((is_gio_error (error, G_IO_ERROR_INVALID_DATA) && encoding == NULL) || +	         (error->domain == GEDIT_DOCUMENT_ERROR && +	         error->code == GEDIT_DOCUMENT_ERROR_ENCODING_AUTO_DETECTION_FAILED)) +	{ +		message_details = g_strconcat (_("gedit has not been able to detect " +					         "the character encoding."), "\n", +					       _("Please check that you are not trying to open a binary file."), "\n", +					       _("Select a character encoding from the menu and try again."), NULL); +		convert_error = TRUE; +	} +	else if (error->domain == GEDIT_DOCUMENT_ERROR && +	         error->code == GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK) +	{ +		error_message = g_strdup_printf (_("There was a problem opening the file %s."), +						 uri_for_display); +		message_details = g_strconcat (_("The file you opened has some invalid characters. " +					       "If you continue editing this file you could make this " +					       "document useless."), "\n", +					       _("You can also choose another character encoding and try again."), +					       NULL); +		edit_anyway = TRUE; +		convert_error = TRUE; +	} +	else if (is_gio_error (error, G_IO_ERROR_INVALID_DATA) && encoding != NULL) +	{ +		error_message = g_strdup_printf (_("Could not open the file %s using the %s character encoding."), +						 uri_for_display,  +						 encoding_name); +		message_details = g_strconcat (_("Please check that you are not trying to open a binary file."), "\n", +					       _("Select a different character encoding from the menu and try again."), NULL); +		convert_error = TRUE; +	} +	else +	{ +		parse_error (error, &error_message, &message_details, uri, uri_for_display); +	} + +	if (error_message == NULL) +	{ +		error_message = g_strdup_printf (_("Could not open the file %s."), +						 uri_for_display); +	} + +	if (convert_error) +	{ +		message_area = create_conversion_error_message_area (error_message, +								     message_details, +								     edit_anyway); +	} +	else +	{ +		message_area = create_io_loading_error_message_area (error_message, +								     message_details, +								     is_recoverable_error (error)); +	} + +	g_free (uri_for_display); +	g_free (encoding_name); +	g_free (error_message); +	g_free (message_details); +	 +	return message_area; +} + +GtkWidget * +gedit_conversion_error_while_saving_message_area_new ( +						const gchar         *uri, +						const GeditEncoding *encoding, +				    		const GError        *error) +{ +	gchar *error_message = NULL; +	gchar *message_details = NULL; +	gchar *full_formatted_uri; +	gchar *encoding_name; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	GtkWidget *message_area; +	 +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail (error->domain == G_CONVERT_ERROR || +	                      error->domain == G_IO_ERROR, NULL); +	g_return_val_if_fail (encoding != NULL, NULL); +	 +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH);								 +	g_free (full_formatted_uri); +	 +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +	encoding_name = gedit_encoding_to_string (encoding); +	 +	error_message = g_strdup_printf (_("Could not save the file %s using the %s character encoding."), +					 uri_for_display,  +					 encoding_name); +	message_details = g_strconcat (_("The document contains one or more characters that cannot be encoded " +					 "using the specified character encoding."), "\n", +				       _("Select a different character encoding from the menu and try again."), NULL); +	 +	message_area = create_conversion_error_message_area ( +								error_message, +								message_details, +								FALSE); + +	g_free (uri_for_display); +	g_free (encoding_name); +	g_free (error_message); +	g_free (message_details); +	 +	return message_area; +} + +const GeditEncoding * +gedit_conversion_error_message_area_get_encoding (GtkWidget *message_area) +{ +	gpointer menu; + +#if !GTK_CHECK_VERSION (2, 17, 1) +	g_return_val_if_fail (GEDIT_IS_MESSAGE_AREA (message_area), NULL); +#else +	g_return_val_if_fail (GTK_IS_INFO_BAR (message_area), NULL); +#endif + +	menu = g_object_get_data (G_OBJECT (message_area),  +				  "gedit-message-area-encoding-menu");	 +	g_return_val_if_fail (menu, NULL); +	 +	return gedit_encodings_combo_box_get_selected_encoding +					(GEDIT_ENCODINGS_COMBO_BOX (menu)); +} + +GtkWidget * +gedit_file_already_open_warning_message_area_new (const gchar *uri) +{ +	GtkWidget *message_area; +	GtkWidget *hbox_content; +	GtkWidget *image; +	GtkWidget *vbox; +	gchar *primary_markup; +	gchar *secondary_markup; +	GtkWidget *primary_label; +	GtkWidget *secondary_label; +	gchar *primary_text; +	const gchar *secondary_text; +	gchar *full_formatted_uri; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	 +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH);								 +	g_free (full_formatted_uri); +	 +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new (); +	gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +				       _("Edit Any_way"), +				       GTK_RESPONSE_YES); +	gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +				       _("D_on't Edit"), +				       GTK_RESPONSE_CANCEL); +#else +	message_area = gtk_info_bar_new (); +	gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +	/* Translators: the access key chosen for this string should be +	 different from other main menu access keys (Open, Edit, View...) */ +				 _("Edit Any_way"), +				 GTK_RESPONSE_YES); +	gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +	/* Translators: the access key chosen for this string should be +	 different from other main menu access keys (Open, Edit, View...) */ +				 _("D_on't Edit"), +				 GTK_RESPONSE_CANCEL); +	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +				       GTK_MESSAGE_WARNING); +#endif + +	hbox_content = gtk_hbox_new (FALSE, 8); + +	image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG); +	gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + +	primary_text = g_strdup_printf (_("This file (%s) is already open in another gedit window."), uri_for_display); +	g_free (uri_for_display); +	 +	primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); +	g_free (primary_text); +	primary_label = gtk_label_new (primary_markup); +	g_free (primary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); +	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	secondary_text = _("gedit opened this instance of the file in a non-editable way. " +			   "Do you want to edit it anyway?"); +	secondary_markup = g_strdup_printf ("<small>%s</small>", +					    secondary_text); +	secondary_label = gtk_label_new (secondary_markup); +	g_free (secondary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); +	GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); +	gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + +	gtk_widget_show_all (hbox_content); +	set_contents (message_area, hbox_content); + +	return message_area; +} + +GtkWidget * +gedit_externally_modified_saving_error_message_area_new ( +						const gchar  *uri, +						const GError *error) +{ +	GtkWidget *message_area; +	GtkWidget *hbox_content; +	GtkWidget *image; +	GtkWidget *vbox; +	gchar *primary_markup; +	gchar *secondary_markup; +	GtkWidget *primary_label; +	GtkWidget *secondary_label; +	gchar *primary_text; +	const gchar *secondary_text; +	gchar *full_formatted_uri; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; + +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail (error->domain == GEDIT_DOCUMENT_ERROR, NULL); +	g_return_val_if_fail (error->code == GEDIT_DOCUMENT_ERROR_EXTERNALLY_MODIFIED, NULL); + +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH);								 +	g_free (full_formatted_uri); + +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new (); +	gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (message_area), +						       _("S_ave Anyway"), +						       GTK_STOCK_SAVE, +						       GTK_RESPONSE_YES); +	gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +				       _("D_on't Save"), +				       GTK_RESPONSE_CANCEL); +#else +	message_area = gtk_info_bar_new (); +	 +	info_bar_add_stock_button_with_text (GTK_INFO_BAR (message_area), +					     _("S_ave Anyway"), +					     GTK_STOCK_SAVE, +					     GTK_RESPONSE_YES); +	gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +				 _("D_on't Save"), +				 GTK_RESPONSE_CANCEL); +	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +				       GTK_MESSAGE_WARNING); +#endif + +	hbox_content = gtk_hbox_new (FALSE, 8); + +	image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG); +	gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + +	// FIXME: review this message, it's not clear since for the user the "modification" +	// could be interpreted as the changes he made in the document. beside "reading" is +	// not accurate (since last load/save) +	primary_text = g_strdup_printf (_("The file %s has been modified since reading it."), +					uri_for_display); +	g_free (uri_for_display); + +	primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); +	g_free (primary_text); +	primary_label = gtk_label_new (primary_markup); +	g_free (primary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); +	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	secondary_text = _("If you save it, all the external changes could be lost. Save it anyway?"); +	secondary_markup = g_strdup_printf ("<small>%s</small>", +					    secondary_text); +	secondary_label = gtk_label_new (secondary_markup); +	g_free (secondary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); +	GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); +	gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + +	gtk_widget_show_all (hbox_content); +	set_contents (message_area, hbox_content); + +	return message_area; +} + +GtkWidget * +gedit_no_backup_saving_error_message_area_new (const gchar  *uri, +					       const GError *error) +{ +	GtkWidget *message_area; +	GtkWidget *hbox_content; +	GtkWidget *image; +	GtkWidget *vbox; +	gchar *primary_markup; +	gchar *secondary_markup; +	GtkWidget *primary_label; +	GtkWidget *secondary_label; +	gchar *primary_text; +	const gchar *secondary_text; +	gchar *full_formatted_uri; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; + +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail (((error->domain == GEDIT_DOCUMENT_ERROR && +			        error->code == GEDIT_DOCUMENT_ERROR_CANT_CREATE_BACKUP) || +			       (error->domain == G_IO_ERROR && +			        error->code == G_IO_ERROR_CANT_CREATE_BACKUP)), NULL); + +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH);								 +	g_free (full_formatted_uri); + +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new (); +	gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (message_area), +						       _("S_ave Anyway"), +						       GTK_STOCK_SAVE, +						       GTK_RESPONSE_YES); +	gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +				       _("D_on't Save"), +				       GTK_RESPONSE_CANCEL); +#else +	message_area = gtk_info_bar_new (); +	 +	info_bar_add_stock_button_with_text (GTK_INFO_BAR (message_area), +					     _("S_ave Anyway"), +					     GTK_STOCK_SAVE, +					     GTK_RESPONSE_YES); +	gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +				 _("D_on't Save"), +				 GTK_RESPONSE_CANCEL); +	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +				       GTK_MESSAGE_WARNING); +#endif + +	hbox_content = gtk_hbox_new (FALSE, 8); + +	image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG); +	gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0); +	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0); + +	// FIXME: review this messages + +	if (gedit_prefs_manager_get_create_backup_copy ()) +		primary_text = g_strdup_printf (_("Could not create a backup file while saving %s"), +						uri_for_display); +	else +		primary_text = g_strdup_printf (_("Could not create a temporary backup file while saving %s"), +						uri_for_display); + +	g_free (uri_for_display); + +	primary_markup = g_strdup_printf ("<b>%s</b>", primary_text); +	g_free (primary_text); +	primary_label = gtk_label_new (primary_markup); +	g_free (primary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5); +	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS); +	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE); + +	secondary_text = _("gedit could not back up the old copy of the file before saving the new one. " +			   "You can ignore this warning and save the file anyway, but if an error " +			   "occurs while saving, you could lose the old copy of the file. Save anyway?"); +	secondary_markup = g_strdup_printf ("<small>%s</small>", +					    secondary_text); +	secondary_label = gtk_label_new (secondary_markup); +	g_free (secondary_markup); +	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0); +	GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS); +	gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE); +	gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5); + +	gtk_widget_show_all (hbox_content); +	set_contents (message_area, hbox_content); + +	return message_area; +} + +GtkWidget * +gedit_unrecoverable_saving_error_message_area_new (const gchar  *uri, +						   const GError *error) +{ +	gchar *error_message = NULL; +	gchar *message_details = NULL; +	gchar *full_formatted_uri; +	gchar *scheme_string; +	gchar *scheme_markup; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	GtkWidget *message_area; + +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (error != NULL, NULL); +	g_return_val_if_fail ((error->domain == GEDIT_DOCUMENT_ERROR) ||  +			      (error->domain == G_IO_ERROR), NULL); + +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH); +	g_free (full_formatted_uri); + +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +	if (is_gio_error (error, G_IO_ERROR_NOT_SUPPORTED)) +	{ +		scheme_string = g_uri_parse_scheme (uri); + +		if ((scheme_string != NULL) && g_utf8_validate (scheme_string, -1, NULL)) +		{ +			scheme_markup = g_markup_printf_escaped ("<i>%s:</i>", scheme_string); + +			/* Translators: %s is a URI scheme (like for example http:, ftp:, etc.) */ +			message_details = g_strdup_printf (_("gedit cannot handle %s locations in write mode. " +							     "Please check that you typed the " +							     "location correctly and try again."), +							   scheme_markup); +			g_free (scheme_markup); +		} +		else +		{ +			message_details = g_strdup (_("gedit cannot handle this location in write mode. " +						      "Please check that you typed the " +						      "location correctly and try again.")); +		} + +		g_free (scheme_string); +	} +	else if (is_gio_error (error, G_IO_ERROR_INVALID_FILENAME)) +	{ +		message_details = g_strdup (_("%s is not a valid location. "  +					      "Please check that you typed the " +					      "location correctly and try again.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_PERMISSION_DENIED)) +	{ +		message_details = g_strdup (_("You do not have the permissions necessary to save the file. " +					      "Please check that you typed the " +					      "location correctly and try again.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_NO_SPACE)) +	{ +		message_details = g_strdup (_("There is not enough disk space to save the file. " +					      "Please free some disk space and try again.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_READ_ONLY)) +	{ +		message_details = g_strdup (_("You are trying to save the file on a read-only disk. " +					      "Please check that you typed the location " +					      "correctly and try again.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_EXISTS)) +	{ +		message_details = g_strdup (_("A file with the same name already exists. " +					      "Please use a different name.")); +	} +	else if (is_gio_error (error, G_IO_ERROR_FILENAME_TOO_LONG)) +	{ +		message_details = g_strdup (_("The disk where you are trying to save the file has " +					      "a limitation on length of the file names. " +					      "Please use a shorter name.")); +	} +	else if (error->domain == GEDIT_DOCUMENT_ERROR && +		 error->code == GEDIT_DOCUMENT_ERROR_TOO_BIG) +	{ +		message_details = g_strdup (_("The disk where you are trying to save the file has " +					      "a limitation on file sizes. Please try saving " +					      "a smaller file or saving it to a disk that does not " +					      "have this limitation.")); +	} +	else +	{ +		parse_error (error,  +			     &error_message,  +			     &message_details,  +			     uri,  +			     uri_for_display); +	} + +	if (error_message == NULL) +	{ +		error_message = g_strdup_printf (_("Could not save the file %s."), +						 uri_for_display); +	} + +	message_area = create_io_loading_error_message_area (error_message, +							     message_details, +							     FALSE); + +	g_free (uri_for_display); +	g_free (error_message); +	g_free (message_details); + +	return message_area; +} + +GtkWidget * +gedit_externally_modified_message_area_new (const gchar *uri, +					    gboolean     document_modified) +{ +	gchar *full_formatted_uri; +	gchar *uri_for_display; +	gchar *temp_uri_for_display; +	const gchar *primary_text; +	const gchar *secondary_text; +	GtkWidget *message_area; + +	g_return_val_if_fail (uri != NULL, NULL); + +	full_formatted_uri = gedit_utils_uri_for_display (uri); + +	/* Truncate the URI so it doesn't get insanely wide. Note that even +	 * though the dialog uses wrapped text, if the URI doesn't contain +	 * white space then the text-wrapping code is too stupid to wrap it. +	 */ +	temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,  +								MAX_URI_IN_DIALOG_LENGTH); +	g_free (full_formatted_uri); + +	uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display); +	g_free (temp_uri_for_display); + +	// FIXME: review this message, it's not clear since for the user the "modification" +	// could be interpreted as the changes he made in the document. beside "reading" is +	// not accurate (since last load/save) +	primary_text = g_strdup_printf (_("The file %s changed on disk."), +					uri_for_display); +	g_free (uri_for_display); + +	if (document_modified) +		secondary_text = _("Do you want to drop your changes and reload the file?"); +	else +		secondary_text = _("Do you want to reload the file?"); + +#if !GTK_CHECK_VERSION (2, 17, 1) +	message_area = gedit_message_area_new (); +	 +	gedit_message_area_add_stock_button_with_text (GEDIT_MESSAGE_AREA (message_area), +						       _("_Reload"), +						       GTK_STOCK_REFRESH, +						       GTK_RESPONSE_OK); + +	gedit_message_area_add_button (GEDIT_MESSAGE_AREA (message_area), +				       GTK_STOCK_CANCEL, +				       GTK_RESPONSE_CANCEL); +#else +	message_area = gtk_info_bar_new (); +	 +	info_bar_add_stock_button_with_text (GTK_INFO_BAR (message_area), +					     _("_Reload"), +					     GTK_STOCK_REFRESH, +					     GTK_RESPONSE_OK); +	gtk_info_bar_add_button (GTK_INFO_BAR (message_area), +				 GTK_STOCK_CANCEL, +				 GTK_RESPONSE_CANCEL); +	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area), +				       GTK_MESSAGE_WARNING); +#endif + +	set_message_area_text_and_icon (message_area, +					"gtk-dialog-warning", +					primary_text, +					secondary_text); + +	return message_area; +} + diff --git a/gedit/gedit-io-error-message-area.h b/gedit/gedit-io-error-message-area.h new file mode 100755 index 00000000..69a4d90e --- /dev/null +++ b/gedit/gedit-io-error-message-area.h @@ -0,0 +1,68 @@ +/* + * gedit-io-error-message-area.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_IO_ERROR_MESSAGE_AREA_H__ +#define __GEDIT_IO_ERROR_MESSAGE_AREA_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +GtkWidget	*gedit_io_loading_error_message_area_new		 (const gchar         *uri, +									  const GeditEncoding *encoding, +									  const GError        *error); + +GtkWidget	*gedit_unrecoverable_reverting_error_message_area_new	 (const gchar         *uri, +									  const GError        *error); + +GtkWidget	*gedit_conversion_error_while_saving_message_area_new	 (const gchar         *uri, +									  const GeditEncoding *encoding, +									  const GError        *error); + +const GeditEncoding  +		*gedit_conversion_error_message_area_get_encoding	 (GtkWidget           *message_area); + +GtkWidget	*gedit_file_already_open_warning_message_area_new	 (const gchar         *uri); + +GtkWidget	*gedit_externally_modified_saving_error_message_area_new (const gchar         *uri, +									  const GError        *error); + +GtkWidget	*gedit_no_backup_saving_error_message_area_new		 (const gchar         *uri, +									  const GError        *error); + +GtkWidget	*gedit_unrecoverable_saving_error_message_area_new	 (const gchar         *uri, +									  const GError        *error); + +GtkWidget	*gedit_externally_modified_message_area_new		 (const gchar         *uri, +									  gboolean             document_modified); + +G_END_DECLS + +#endif  /* __GEDIT_IO_ERROR_MESSAGE_AREA_H__  */ diff --git a/gedit/gedit-language-manager.c b/gedit/gedit-language-manager.c new file mode 100755 index 00000000..a402e4a5 --- /dev/null +++ b/gedit/gedit-language-manager.c @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-languages-manager.c + * This file is part of gedit + * + * Copyright (C) 2003-2006 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2003-2006. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#include <string.h> + +#include "gedit-language-manager.h" +#include "gedit-prefs-manager.h" +#include "gedit-utils.h" +#include "gedit-debug.h" + +static GtkSourceLanguageManager *language_manager = NULL; + +GtkSourceLanguageManager * +gedit_get_language_manager (void) +{ +	if (language_manager == NULL) +	{ +		language_manager = gtk_source_language_manager_new (); +	} + +	return language_manager; +} + +static gint +language_compare (gconstpointer a, gconstpointer b) +{ +	GtkSourceLanguage *lang_a = (GtkSourceLanguage *)a; +	GtkSourceLanguage *lang_b = (GtkSourceLanguage *)b; +	const gchar *name_a = gtk_source_language_get_name (lang_a); +	const gchar *name_b = gtk_source_language_get_name (lang_b); + +	return g_utf8_collate (name_a, name_b); +} + +GSList * +gedit_language_manager_list_languages_sorted (GtkSourceLanguageManager *lm, +					      gboolean                  include_hidden) +{ +	GSList *languages = NULL; +	const gchar * const *ids; + +	ids = gtk_source_language_manager_get_language_ids (lm); +	if (ids == NULL) +		return NULL; + +	while (*ids != NULL) +	{ +		GtkSourceLanguage *lang; + +		lang = gtk_source_language_manager_get_language (lm, *ids); +		g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (lang), NULL); +		++ids; + +		if (include_hidden || !gtk_source_language_get_hidden (lang)) +		{ +			languages = g_slist_prepend (languages, lang); +		} +	} + +	return g_slist_sort (languages, (GCompareFunc)language_compare); +} + diff --git a/gedit/gedit-language-manager.h b/gedit/gedit-language-manager.h new file mode 100755 index 00000000..02438d32 --- /dev/null +++ b/gedit/gedit-language-manager.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-languages-manager.h + * This file is part of gedit + * + * Copyright (C) 2003-2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2003-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_LANGUAGES_MANAGER_H__ +#define __GEDIT_LANGUAGES_MANAGER_H__ + +#include <gtksourceview/gtksourcelanguagemanager.h> + +G_BEGIN_DECLS + +GtkSourceLanguageManager	*gedit_get_language_manager	(void); + +GSList				*gedit_language_manager_list_languages_sorted +								(GtkSourceLanguageManager	*lm, +								 gboolean			 include_hidden); + +G_END_DECLS + +#endif /* __GEDIT_LANGUAGES_MANAGER_H__ */ diff --git a/gedit/gedit-marshal.list b/gedit/gedit-marshal.list new file mode 100755 index 00000000..d2882947 --- /dev/null +++ b/gedit/gedit-marshal.list @@ -0,0 +1,13 @@ +BOOLEAN:NONE +BOOLEAN:OBJECT +VOID:BOOLEAN +VOID:BOOLEAN,POINTER +VOID:BOXED,BOXED +VOID:OBJECT +VOID:POINTER +VOID:STRING,BOXED,FLAGS +VOID:STRING,BOXED,INT,BOOLEAN +VOID:UINT,POINTER +VOID:UINT64,UINT64 +VOID:VOID +VOID:INT,INT diff --git a/gedit/gedit-message-area.c b/gedit/gedit-message-area.c new file mode 100755 index 00000000..09240515 --- /dev/null +++ b/gedit/gedit-message-area.c @@ -0,0 +1,626 @@ +/* + * gedit-message-area.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +/* TODO: Style properties */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include "gedit-message-area.h" + +#define GEDIT_MESSAGE_AREA_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +					       GEDIT_TYPE_MESSAGE_AREA, \ +					       GeditMessageAreaPrivate)) + +struct _GeditMessageAreaPrivate +{ +	GtkWidget *main_hbox; + +	GtkWidget *contents; +	GtkWidget *action_area; + +	gboolean changing_style; +}; + +typedef struct _ResponseData ResponseData; + +struct _ResponseData +{ +	gint response_id; +}; + +enum { +	RESPONSE, +	CLOSE, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE(GeditMessageArea, gedit_message_area, GTK_TYPE_HBOX) + + +static void +gedit_message_area_finalize (GObject *object) +{ +	/* +	GeditMessageArea *message_area = GEDIT_MESSAGE_AREA (object); +	*/ + +	G_OBJECT_CLASS (gedit_message_area_parent_class)->finalize (object); +} + +static ResponseData * +get_response_data (GtkWidget *widget, +		   gboolean   create) +{ +	ResponseData *ad = g_object_get_data (G_OBJECT (widget), +                                       	      "gedit-message-area-response-data"); + +	if (ad == NULL && create) +	{ +		ad = g_new (ResponseData, 1); + +		g_object_set_data_full (G_OBJECT (widget), +					"gedit-message-area-response-data", +					ad, +					g_free); +    	} + +	return ad; +} + +static GtkWidget * +find_button (GeditMessageArea *message_area, +	     gint              response_id) +{ +	GList *children, *tmp_list; +	GtkWidget *child = NULL; + +	children = gtk_container_get_children ( +			GTK_CONTAINER (message_area->priv->action_area)); + +	for (tmp_list = children; tmp_list; tmp_list = tmp_list->next) +	{ +		ResponseData *rd = get_response_data (tmp_list->data, FALSE); + +		if (rd && rd->response_id == response_id) +		{ +			child = tmp_list->data; +			break; +		} +	} + +	g_list_free (children); + +	return child; +} + +static void +gedit_message_area_close (GeditMessageArea *message_area) +{ +	if (!find_button (message_area, GTK_RESPONSE_CANCEL)) +		return; + +	/* emit response signal */ +	gedit_message_area_response (GEDIT_MESSAGE_AREA (message_area), +				     GTK_RESPONSE_CANCEL); +} + +static gboolean +paint_message_area (GtkWidget      *widget, +		    GdkEventExpose *event, +		    gpointer        user_data) +{ +	gtk_paint_flat_box (widget->style, +			    widget->window, +			    GTK_STATE_NORMAL, +			    GTK_SHADOW_OUT, +			    NULL, +			    widget, +			    "tooltip", +			    widget->allocation.x + 1, +			    widget->allocation.y + 1, +			    widget->allocation.width - 2, +			    widget->allocation.height - 2); + +	return FALSE; +} + +static void +gedit_message_area_class_init (GeditMessageAreaClass *klass) +{ +	GObjectClass *object_class; +	GtkBindingSet *binding_set; + +	object_class = G_OBJECT_CLASS (klass); +	object_class->finalize = gedit_message_area_finalize; + +	klass->close = gedit_message_area_close; + +	g_type_class_add_private (object_class, sizeof(GeditMessageAreaPrivate)); + +	signals[RESPONSE] = g_signal_new ("response", +					  G_OBJECT_CLASS_TYPE (klass), +					  G_SIGNAL_RUN_LAST, +					  G_STRUCT_OFFSET (GeditMessageAreaClass, response), +					  NULL, NULL, +					  g_cclosure_marshal_VOID__INT, +					  G_TYPE_NONE, 1, +					  G_TYPE_INT); + +	signals[CLOSE] =  g_signal_new ("close", +					G_OBJECT_CLASS_TYPE (klass), +					G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +					G_STRUCT_OFFSET (GeditMessageAreaClass, close), +		  			NULL, NULL, +		  			g_cclosure_marshal_VOID__VOID, +					G_TYPE_NONE, 0); + +	binding_set = gtk_binding_set_by_class (klass); + +	gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "close", 0); +} + +static void +style_set (GtkWidget        *widget, +	   GtkStyle         *prev_style, +	   GeditMessageArea *message_area) +{ +	GtkWidget *window; +	GtkStyle *style; + +	if (message_area->priv->changing_style) +		return; + +	/* This is a hack needed to use the tooltip background color */ +	window = gtk_window_new (GTK_WINDOW_POPUP); +	gtk_widget_set_name (window, "gtk-tooltip"); +	gtk_widget_ensure_style (window); +	style = gtk_widget_get_style (window); + +	message_area->priv->changing_style = TRUE; +	gtk_widget_set_style (GTK_WIDGET (message_area), style); +	message_area->priv->changing_style = FALSE; + +	gtk_widget_destroy (window); + +	gtk_widget_queue_draw (GTK_WIDGET (message_area)); +} + +static void +gedit_message_area_init (GeditMessageArea *message_area) +{ +	message_area->priv = GEDIT_MESSAGE_AREA_GET_PRIVATE (message_area); + +	message_area->priv->main_hbox = gtk_hbox_new (FALSE, 16); /* FIXME: use style properties */ +	gtk_widget_show (message_area->priv->main_hbox); +	gtk_container_set_border_width (GTK_CONTAINER (message_area->priv->main_hbox), +					8); /* FIXME: use style properties */ + +	message_area->priv->action_area = gtk_vbox_new (TRUE, 10); /* FIXME: use style properties */ +	gtk_widget_show (message_area->priv->action_area); +	gtk_box_pack_end (GTK_BOX (message_area->priv->main_hbox), +			    message_area->priv->action_area, +			    FALSE, +			    TRUE, +			    0); + +	gtk_box_pack_start (GTK_BOX (message_area), +			    message_area->priv->main_hbox, +			    TRUE, +			    TRUE, +			    0); + +	gtk_widget_set_app_paintable (GTK_WIDGET (message_area), TRUE); + +	g_signal_connect (message_area, +			  "expose-event", +			  G_CALLBACK (paint_message_area), +			  NULL); + +	/* Note that we connect to style-set on one of the internal +	 * widgets, not on the message area itself, since gtk does +	 * not deliver any further style-set signals for a widget on +	 * which the style has been forced with gtk_widget_set_style() */ +	g_signal_connect (message_area->priv->main_hbox, +			  "style-set", +			  G_CALLBACK (style_set), +			  message_area); +} + +static gint +get_response_for_widget (GeditMessageArea *message_area, +			 GtkWidget        *widget) +{ +	ResponseData *rd; + +	rd = get_response_data (widget, FALSE); +	if (!rd) +		return GTK_RESPONSE_NONE; +	else +		return rd->response_id; +} + +static void +action_widget_activated (GtkWidget *widget, GeditMessageArea *message_area) +{ +	gint response_id; + +	response_id = get_response_for_widget (message_area, widget); + +	gedit_message_area_response (message_area, response_id); +} + +void +gedit_message_area_add_action_widget (GeditMessageArea *message_area, +				      GtkWidget        *child, +				      gint              response_id) +{ +	ResponseData *ad; +	guint signal_id; + +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); +	g_return_if_fail (GTK_IS_WIDGET (child)); + +	ad = get_response_data (child, TRUE); + +	ad->response_id = response_id; + +	if (GTK_IS_BUTTON (child)) +		signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); +	else +		signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal; + +	if (signal_id) +	{ +		GClosure *closure; + +		closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), +						 G_OBJECT (message_area)); + +		g_signal_connect_closure_by_id (child, +						signal_id, +						0, +						closure, +						FALSE); +	} +	else +		g_warning ("Only 'activatable' widgets can be packed into the action area of a GeditMessageArea"); + +	if (response_id != GTK_RESPONSE_HELP) +		gtk_box_pack_start (GTK_BOX (message_area->priv->action_area), +				    child, +				    FALSE, +				    FALSE, +				    0); +	else +		gtk_box_pack_end (GTK_BOX (message_area->priv->action_area), +				    child, +				    FALSE, +				    FALSE, +				    0); +} + +/** + * gedit_message_area_set_contents: + * @message_area: a #GeditMessageArea + * @contents: widget you want to add to the contents area + * + * Adds the @contents widget to the contents area of #GeditMessageArea. + */ +void +gedit_message_area_set_contents	(GeditMessageArea *message_area, +				 GtkWidget        *contents) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); +	g_return_if_fail (GTK_IS_WIDGET (contents)); + +  	message_area->priv->contents = contents; +	gtk_box_pack_start (GTK_BOX (message_area->priv->main_hbox), +			    message_area->priv->contents, +			    TRUE, +			    TRUE, +			    0); +} + +/** + * gedit_message_area_add_button: + * @message_area: a #GeditMessageArea + * @button_text: text of button, or stock ID + * @response_id: response ID for the button + *  + * Adds a button with the given text (or a stock button, if button_text is a stock ID) + * and sets things up so that clicking the button will emit the "response" signal + * with the given response_id. The button is appended to the end of the message area's + * action area. The button widget is returned, but usually you don't need it. + * + * Returns: the button widget that was added + */ +GtkWidget* +gedit_message_area_add_button (GeditMessageArea *message_area, +			       const gchar      *button_text, +			       gint              response_id) +{ +	GtkWidget *button; + +	g_return_val_if_fail (GEDIT_IS_MESSAGE_AREA (message_area), NULL); +	g_return_val_if_fail (button_text != NULL, NULL); + +	button = gtk_button_new_from_stock (button_text); + +	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + +	gtk_widget_show (button); + +	gedit_message_area_add_action_widget (message_area, +					      button, +					      response_id); + +	return button; +} + +static void +add_buttons_valist (GeditMessageArea *message_area, +		    const gchar      *first_button_text, +		    va_list           args) +{ +	const gchar* text; +	gint response_id; + +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + +	if (first_button_text == NULL) +		return; + +	text = first_button_text; +	response_id = va_arg (args, gint); + +	while (text != NULL) +	{ +		gedit_message_area_add_button (message_area, +					       text, +					       response_id); + +		text = va_arg (args, gchar*); +		if (text == NULL) +        		break; + +		response_id = va_arg (args, int); +	} +} + +/** + * gedit_message_area_add_buttons: + * @message_area: a #GeditMessageArea + * @first_button_text: button text or stock ID + * @...: response ID for first button, then more text-response_id pairs + * + * Adds more buttons, same as calling gedit_message_area_add_button() repeatedly. + * The variable argument list should be NULL-terminated as with + * gedit_message_area_new_with_buttons(). Each button must have both text and response ID. + */ +void +gedit_message_area_add_buttons (GeditMessageArea *message_area, +				const gchar      *first_button_text, +				...) +{ +	va_list args; + +	va_start (args, first_button_text); + +	add_buttons_valist (message_area, +                            first_button_text, +                            args); + +	va_end (args); +} + +/** + * gedit_message_area_new: + *  + * Creates a new #GeditMessageArea object. + *  + * Returns: a new #GeditMessageArea object + */ +GtkWidget * +gedit_message_area_new (void) +{ +	return g_object_new (GEDIT_TYPE_MESSAGE_AREA, NULL); +} + +/** + * gedit_message_area_new_with_buttons: + * @first_button_text: stock ID or text to go in first button, or NULL + * @...: response ID for first button, then additional buttons, ending with NULL + *  + * Creates a new #GeditMessageArea with buttons. Button text/response ID pairs  + * should be listed, with a NULL pointer ending the list. Button text can be either + * a stock ID such as GTK_STOCK_OK, or some arbitrary text. A response ID can be any + * positive number, or one of the values in the GtkResponseType enumeration. If  + * the user clicks one of these dialog buttons, GeditMessageArea will emit the "response" + * signal with the corresponding response ID. + * + * Returns: a new #GeditMessageArea + */ +GtkWidget * +gedit_message_area_new_with_buttons (const gchar *first_button_text, +                                     ...) +{ +	GeditMessageArea *message_area; +	va_list args; + +	message_area = GEDIT_MESSAGE_AREA (gedit_message_area_new ()); + +	va_start (args, first_button_text); + +	add_buttons_valist (message_area, +			    first_button_text, +			    args); + +	va_end (args); + +	return GTK_WIDGET (message_area); +} + +/** + * gedit_message_area_set_response_sensitive: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * @setting: TRUE for sensitive + * + * Calls gtk_widget_set_sensitive (widget, setting) for each widget in the dialog's + * action area with the given response_id. A convenient way to sensitize/desensitize + * dialog buttons. + */ +void +gedit_message_area_set_response_sensitive (GeditMessageArea *message_area, +					   gint              response_id, +					   gboolean          setting) +{ +	GList *children; +	GList *tmp_list; + +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + +	children = gtk_container_get_children (GTK_CONTAINER (message_area->priv->action_area)); + +	tmp_list = children; +	while (tmp_list != NULL) +	{ +		GtkWidget *widget = tmp_list->data; +		ResponseData *rd = get_response_data (widget, FALSE); + +		if (rd && rd->response_id == response_id) +			gtk_widget_set_sensitive (widget, setting); + +		tmp_list = g_list_next (tmp_list); +	} + +	g_list_free (children); +} + +/** + * gedit_message_area_set_default_response: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * + * Sets the last widget in the message area's action area with the given response_id + * as the default widget for the dialog. Pressing "Enter" normally activates the + * default widget. + */ +void +gedit_message_area_set_default_response (GeditMessageArea *message_area, +					 gint              response_id) +{ +	GList *children; +	GList *tmp_list; + +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + +	children = gtk_container_get_children (GTK_CONTAINER (message_area->priv->action_area)); + +	tmp_list = children; +	while (tmp_list != NULL) +	{ +		GtkWidget *widget = tmp_list->data; +		ResponseData *rd = get_response_data (widget, FALSE); + +		if (rd && rd->response_id == response_id) +		gtk_widget_grab_default (widget); + +		tmp_list = g_list_next (tmp_list); +	} + +	g_list_free (children); +} + +/** + * gedit_message_area_set_default_response: + * @message_area: a #GeditMessageArea + * @response_id: a response ID + * + * Emits the 'response' signal with the given @response_id. + */ +void +gedit_message_area_response (GeditMessageArea *message_area, +			     gint              response_id) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_AREA (message_area)); + +	g_signal_emit (message_area, +		       signals[RESPONSE], +		       0, +		       response_id); +} + +/** + * gedit_message_area_add_stock_button_with_text: + * @message_area: a #GeditMessageArea + * @text: the text to visualize in the button + * @stock_id: the stock ID of the button + * @response_id: a response ID + * + * Same as gedit_message_area_add_button() but with a specific text. + */ +GtkWidget * +gedit_message_area_add_stock_button_with_text (GeditMessageArea *message_area, +				    	       const gchar      *text, +				    	       const gchar      *stock_id, +				    	       gint              response_id) +{ +	GtkWidget *button; + +	g_return_val_if_fail (GEDIT_IS_MESSAGE_AREA (message_area), NULL); +	g_return_val_if_fail (text != NULL, NULL); +	g_return_val_if_fail (stock_id != NULL, NULL); + +	button = gtk_button_new_with_mnemonic (text); +        gtk_button_set_image (GTK_BUTTON (button), +                              gtk_image_new_from_stock (stock_id, +                                                        GTK_ICON_SIZE_BUTTON)); + +	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + +	gtk_widget_show (button); + +	gedit_message_area_add_action_widget (message_area, +					      button, +					      response_id); + +	return button; +} + diff --git a/gedit/gedit-message-area.h b/gedit/gedit-message-area.h new file mode 100755 index 00000000..01dfff0c --- /dev/null +++ b/gedit/gedit-message-area.h @@ -0,0 +1,129 @@ +/* + * gedit-message-area.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_MESSAGE_AREA_H__ +#define __GEDIT_MESSAGE_AREA_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_MESSAGE_AREA              (gedit_message_area_get_type()) +#define GEDIT_MESSAGE_AREA(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_MESSAGE_AREA, GeditMessageArea)) +#define GEDIT_MESSAGE_AREA_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_MESSAGE_AREA, GeditMessageAreaClass)) +#define GEDIT_IS_MESSAGE_AREA(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_MESSAGE_AREA)) +#define GEDIT_IS_MESSAGE_AREA_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_MESSAGE_AREA)) +#define GEDIT_MESSAGE_AREA_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_MESSAGE_AREA, GeditMessageAreaClass)) + +/* Private structure type */ +typedef struct _GeditMessageAreaPrivate GeditMessageAreaPrivate; + +/* + * Main object structure + */ +typedef struct _GeditMessageArea GeditMessageArea; + +struct _GeditMessageArea  +{ +	GtkHBox parent; + +	/*< private > */ +	GeditMessageAreaPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditMessageAreaClass GeditMessageAreaClass; + +struct _GeditMessageAreaClass  +{ +	GtkHBoxClass parent_class; + +	/* Signals */ +	void (* response) (GeditMessageArea *message_area, gint response_id); + +	/* Keybinding signals */ +	void (* close)    (GeditMessageArea *message_area); + +	/* Padding for future expansion */ +	void (*_gedit_reserved1) (void); +	void (*_gedit_reserved2) (void);	 +}; + +/* + * Public methods + */ +GType 		 gedit_message_area_get_type 		(void) G_GNUC_CONST; + +GtkWidget	*gedit_message_area_new      		(void); + +GtkWidget	*gedit_message_area_new_with_buttons	(const gchar      *first_button_text, +                                        		 ...); + +void		 gedit_message_area_set_contents	(GeditMessageArea *message_area, +                                             		 GtkWidget        *contents); +                              		  +void		 gedit_message_area_add_action_widget	(GeditMessageArea *message_area, +                                         		 GtkWidget        *child, +                                         		 gint              response_id); +                                         		  +GtkWidget	*gedit_message_area_add_button        	(GeditMessageArea *message_area, +                                         		 const gchar      *button_text, +                                         		 gint              response_id); +             		  +GtkWidget	*gedit_message_area_add_stock_button_with_text  +							(GeditMessageArea *message_area,  +				    			 const gchar      *text,  +				    			 const gchar      *stock_id,  +				    			 gint              response_id); + +void       	 gedit_message_area_add_buttons 	(GeditMessageArea *message_area, +                                         		 const gchar      *first_button_text, +                                         		 ...); + +void		 gedit_message_area_set_response_sensitive  +							(GeditMessageArea *message_area, +                                        		 gint              response_id, +                                        		 gboolean          setting); +void 		 gedit_message_area_set_default_response  +							(GeditMessageArea *message_area, +                                        		 gint              response_id); + +/* Emit response signal */ +void		 gedit_message_area_response           	(GeditMessageArea *message_area, +                                    			 gint              response_id); + +G_END_DECLS + +#endif  /* __GEDIT_MESSAGE_AREA_H__  */ diff --git a/gedit/gedit-message-bus.c b/gedit/gedit-message-bus.c new file mode 100755 index 00000000..bb6017a3 --- /dev/null +++ b/gedit/gedit-message-bus.c @@ -0,0 +1,1158 @@ +#include "gedit-message-bus.h" + +#include <string.h> +#include <stdarg.h> +#include <gobject/gvaluecollector.h> + +/** + * GeditMessageCallback: + * @bus: the #GeditMessageBus on which the message was sent + * @message: the #GeditMessage which was sent + * @userdata: the supplied user data when connecting the callback + * + * Callback signature used for connecting callback functions to be called + * when a message is received (see gedit_message_bus_connect()). + * + */ + +/** + * SECTION:gedit-message-bus + * @short_description: internal message communication bus + * @include: gedit/gedit-message-bus.h + * + * gedit has a communication bus very similar to DBus. Its primary use is to + * allow easy communication between plugins, but it can also be used to expose + * gedit functionality to external applications by providing DBus bindings for + * the internal gedit message bus. + * + * There are two different communication busses available. The default bus + * (see gedit_message_bus_get_default()) is an application wide communication + * bus. In addition, each #GeditWindow has a separate, private bus + * (see gedit_window_get_message_bus()). This makes it easier for plugins to + * communicate to other plugins in the same window. + * + * The concept of the message bus is very simple. You can register a message + * type on the bus, specified as a Method at a specific Object Path with a + * certain set of Method Arguments. You can then connect callback functions + * for this message type on the bus. Whenever a message with the Object Path + * and Method for which callbacks are connected is sent over the bus, the + * callbacks are called. There is no distinction between Methods and Signals + * (signals are simply messages where sender and receiver have switched places). + * + * <example> + * <title>Registering a message type</title> + * <programlisting> + * GeditMessageBus *bus = gedit_message_bus_get_default (); + * + * // Register 'method' at '/plugins/example' with one required + * // string argument 'arg1' + * GeditMessageType *message_type = gedit_message_bus_register ("/plugins/example", "method",  + *                                                              0,  + *                                                              "arg1", G_TYPE_STRING, + *                                                              NULL); + * </programlisting> + * </example> + * <example> + * <title>Connecting a callback</title> + * <programlisting> + * static void + * example_method_cb (GeditMessageBus *bus, + *                    GeditMessage    *message, + *                    gpointer         userdata) + * { + * 	gchar *arg1 = NULL; + *	 + * 	gedit_message_get (message, "arg1", &arg1, NULL); + * 	g_message ("Evoked /plugins/example.method with: %s", arg1); + * 	g_free (arg1); + * } + * + * GeditMessageBus *bus = gedit_message_bus_get_default (); + *  + * guint id = gedit_message_bus_connect (bus,  + *                                       "/plugins/example", "method", + *                                       example_method_cb, + *                                       NULL, + *                                       NULL); + *                                         + * </programlisting> + * </example> + * <example> + * <title>Sending a message</title> + * <programlisting> + * GeditMessageBus *bus = gedit_message_bus_get_default (); + * + * gedit_message_bus_send (bus,  + *                         "/plugins/example", "method",  + *                         "arg1", "Hello World",  + *                         NULL); + * </programlisting> + * </example> + * + * Since: 2.25.3 + * + */ +  +#define GEDIT_MESSAGE_BUS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_MESSAGE_BUS, GeditMessageBusPrivate)) + +typedef struct +{ +	gchar *object_path; +	gchar *method; + +	GList *listeners; +} Message; + +typedef struct +{ +	guint id; +	gboolean blocked; + +	GDestroyNotify destroy_data; +	GeditMessageCallback callback; +	gpointer userdata; +} Listener; + +typedef struct +{ +	Message *message; +	GList *listener; +} IdMap; + +struct _GeditMessageBusPrivate +{ +	GHashTable *messages; +	GHashTable *idmap; + +	GList *message_queue; +	guint idle_id; + +	guint next_id; +	 +	GHashTable *types; /* mapping from identifier to GeditMessageType */ +}; + +/* signals */ +enum +{ +	DISPATCH, +	REGISTERED, +	UNREGISTERED, +	LAST_SIGNAL +}; + +static guint message_bus_signals[LAST_SIGNAL]; + +static void gedit_message_bus_dispatch_real (GeditMessageBus *bus, +				 	     GeditMessage    *message); + +G_DEFINE_TYPE(GeditMessageBus, gedit_message_bus, G_TYPE_OBJECT) + +static void +listener_free (Listener *listener) +{ +	if (listener->destroy_data) +		listener->destroy_data (listener->userdata); + +	g_free (listener); +} + +static void +message_free (Message *message) +{ +	g_free (message->method); +	g_free (message->object_path); +	 +	g_list_foreach (message->listeners, (GFunc)listener_free, NULL); +	g_list_free (message->listeners); +	 +	g_free (message); +} + +static void +message_queue_free (GList *queue) +{ +	g_list_foreach (queue, (GFunc)g_object_unref, NULL); +	g_list_free (queue); +} + +static void +gedit_message_bus_finalize (GObject *object) +{ +	GeditMessageBus *bus = GEDIT_MESSAGE_BUS (object); +	 +	if (bus->priv->idle_id != 0) +		g_source_remove (bus->priv->idle_id); +	 +	message_queue_free (bus->priv->message_queue); + +	g_hash_table_destroy (bus->priv->messages); +	g_hash_table_destroy (bus->priv->idmap); +	g_hash_table_destroy (bus->priv->types); +	 +	G_OBJECT_CLASS (gedit_message_bus_parent_class)->finalize (object); +} + +static void +gedit_message_bus_class_init (GeditMessageBusClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	 +	object_class->finalize = gedit_message_bus_finalize; +	 +	klass->dispatch = gedit_message_bus_dispatch_real; + +	/** +	 * GeditMessageBus::dispatch: +	 * @bus: a #GeditMessageBus +	 * @message: the #GeditMessage to dispatch +	 * +	 * The "dispatch" signal is emitted when a message is to be dispatched. +	 * The message is dispatched in the default handler of this signal.  +	 * Primary use of this signal is to customize the dispatch of a message +	 * (for instance to automatically dispatch all messages over DBus). +	 *2 +	 */ +	message_bus_signals[DISPATCH] = +   		g_signal_new ("dispatch", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditMessageBusClass, dispatch), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_MESSAGE); + +	/** +	 * GeditMessageBus::registered: +	 * @bus: a #GeditMessageBus +	 * @message_type: the registered #GeditMessageType +	 * +	 * The "registered" signal is emitted when a message has been registered +	 * on the bus. +	 * +	 */ +	message_bus_signals[REGISTERED] = +   		g_signal_new ("registered", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditMessageBusClass, registered), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__BOXED, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_MESSAGE_TYPE); + +	/** +	 * GeditMessageBus::unregistered: +	 * @bus: a #GeditMessageBus +	 * @message_type: the unregistered #GeditMessageType +	 * +	 * The "unregistered" signal is emitted when a message has been  +	 * unregistered from the bus. +	 * +	 */ +	message_bus_signals[UNREGISTERED] = +   		g_signal_new ("unregistered", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditMessageBusClass, unregistered), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__BOXED, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_MESSAGE_TYPE); + +	g_type_class_add_private (object_class, sizeof(GeditMessageBusPrivate)); +} + +static Message * +message_new (GeditMessageBus *bus, +	     const gchar     *object_path, +	     const gchar     *method) +{ +	Message *message = g_new (Message, 1); +	 +	message->object_path = g_strdup (object_path); +	message->method = g_strdup (method); +	message->listeners = NULL; + +	g_hash_table_insert (bus->priv->messages,  +			     gedit_message_type_identifier (object_path, method), +			     message); +	return message; +} + +static Message * +lookup_message (GeditMessageBus *bus, +	       const gchar      *object_path, +	       const gchar      *method, +	       gboolean          create) +{ +	gchar *identifier; +	Message *message; +	 +	identifier = gedit_message_type_identifier (object_path, method); +	message = (Message *)g_hash_table_lookup (bus->priv->messages, identifier); +	g_free (identifier); + +	if (!message && !create) +		return NULL; +	 +	if (!message) +		message = message_new (bus, object_path, method); +	 +	return message; +} + +static guint +add_listener (GeditMessageBus      *bus, +	      Message		   *message, +	      GeditMessageCallback  callback, +	      gpointer		    userdata, +	      GDestroyNotify        destroy_data) +{ +	Listener *listener; +	IdMap *idmap; +	 +	listener = g_new (Listener, 1); +	listener->id = ++bus->priv->next_id; +	listener->callback = callback; +	listener->userdata = userdata; +	listener->blocked = FALSE; +	listener->destroy_data = destroy_data; + +	message->listeners = g_list_append (message->listeners, listener); +	 +	idmap = g_new (IdMap, 1); +	idmap->message = message; +	idmap->listener = g_list_last (message->listeners); + +	g_hash_table_insert (bus->priv->idmap, GINT_TO_POINTER (listener->id), idmap);	 +	return listener->id; +} + +static void +remove_listener (GeditMessageBus *bus, +		 Message         *message, +		 GList		 *listener) +{ +	Listener *lst; +	 +	lst = (Listener *)listener->data; +	 +	/* remove from idmap */ +	g_hash_table_remove (bus->priv->idmap, GINT_TO_POINTER (lst->id)); +	listener_free (lst); + +	/* remove from list of listeners */ +	message->listeners = g_list_delete_link (message->listeners, listener); +	 +	if (!message->listeners) +	{ +		/* remove message because it does not have any listeners */ +		g_hash_table_remove (bus->priv->messages, message); +	} +} + +static void +block_listener (GeditMessageBus *bus, +		Message		*message, +		GList		*listener) +{ +	Listener *lst; +	 +	lst = (Listener *)listener->data; +	lst->blocked = TRUE; +} + +static void +unblock_listener (GeditMessageBus *bus, +		  Message	  *message, +		  GList		  *listener) +{ +	Listener *lst; +	 +	lst = (Listener *)listener->data; +	lst->blocked = FALSE; +} + +static void +dispatch_message_real (GeditMessageBus *bus, +		       Message         *msg, +		       GeditMessage    *message) +{ +	GList *item; +	 +	for (item = msg->listeners; item; item = item->next) +	{ +		Listener *listener = (Listener *)item->data; +		 +		if (!listener->blocked) +			listener->callback (bus, message, listener->userdata); +	} +} + +static void +gedit_message_bus_dispatch_real (GeditMessageBus *bus, +				 GeditMessage    *message) +{ +	const gchar *object_path; +	const gchar *method; +	Message *msg; +	 +	object_path = gedit_message_get_object_path (message); +	method = gedit_message_get_method (message); + +	msg = lookup_message (bus, object_path, method, FALSE); +	 +	if (msg) +		dispatch_message_real (bus, msg, message); +} + +static void +dispatch_message (GeditMessageBus *bus, +		  GeditMessage    *message) +{ +	g_signal_emit (bus, message_bus_signals[DISPATCH], 0, message);	 +} + +static gboolean +idle_dispatch (GeditMessageBus *bus) +{ +	GList *list; +	GList *item; +	 +	/* make sure to set idle_id to 0 first so that any new async messages +	   will be queued properly */ +	bus->priv->idle_id = 0; + +	/* reverse queue to get correct delivery order */ +	list = g_list_reverse (bus->priv->message_queue); +	bus->priv->message_queue = NULL; +	 +	for (item = list; item; item = item->next) +	{ +		GeditMessage *msg = GEDIT_MESSAGE (item->data); +		 +		dispatch_message (bus, msg); +	} +	 +	message_queue_free (list); +	return FALSE; +} + +typedef void (*MatchCallback) (GeditMessageBus *, Message *, GList *); + +static void +process_by_id (GeditMessageBus  *bus, +	       guint	         id, +	       MatchCallback     processor) +{ +	IdMap *idmap; +	 +	idmap = (IdMap *)g_hash_table_lookup (bus->priv->idmap, GINT_TO_POINTER (id)); +	 +	if (idmap == NULL) +	{ +		g_warning ("No handler registered with id `%d'", id); +		return; +	} +		 +	processor (bus, idmap->message, idmap->listener); +} + +static void +process_by_match (GeditMessageBus      *bus, +	          const gchar          *object_path, +	          const gchar          *method, +	          GeditMessageCallback  callback, +	          gpointer              userdata, +	          MatchCallback         processor) +{ +	Message *message; +	GList *item; +	 +	message = lookup_message (bus, object_path, method, FALSE); +	 +	if (!message) +	{ +		g_warning ("No such handler registered for %s.%s", object_path, method); +		return; +	} +	 +	for (item = message->listeners; item; item = item->next) +	{ +		Listener *listener = (Listener *)item->data; +		 +		if (listener->callback == callback &&  +		    listener->userdata == userdata) +		{ +			processor (bus, message, item); +			return; +		} +	} +	 +	g_warning ("No such handler registered for %s.%s", object_path, method); +} + +static void +gedit_message_bus_init (GeditMessageBus *self) +{ +	self->priv = GEDIT_MESSAGE_BUS_GET_PRIVATE (self); +	 +	self->priv->messages = g_hash_table_new_full (g_str_hash, +						      g_str_equal, +						      (GDestroyNotify)g_free, +						      (GDestroyNotify)message_free); + +	self->priv->idmap = g_hash_table_new_full (g_direct_hash, +	 					   g_direct_equal, +	 					   NULL, +	 					   (GDestroyNotify)g_free); +	 					    +	self->priv->types = g_hash_table_new_full (g_str_hash, +						   g_str_equal, +						   (GDestroyNotify)g_free, +						   (GDestroyNotify)gedit_message_type_unref); +} + +/** + * gedit_message_bus_get_default: + * + * Get the default application #GeditMessageBus. + * + * Return value: the default #GeditMessageBus + * + */ +GeditMessageBus * +gedit_message_bus_get_default (void) +{ +	static GeditMessageBus *default_bus = NULL; +	 +	if (G_UNLIKELY (default_bus == NULL)) +	{ +		default_bus = g_object_new (GEDIT_TYPE_MESSAGE_BUS, NULL); +		g_object_add_weak_pointer (G_OBJECT (default_bus), +				           (gpointer) &default_bus); +	} +	 +	return default_bus; +} + +/** + * gedit_message_bus_new: + *  + * Create a new message bus. Use gedit_message_bus_get_default() to get the + * default, application wide, message bus. Creating a new bus is useful for + * associating a specific bus with for instance a #GeditWindow. + * + * Return value: a new #GeditMessageBus + * + */ +GeditMessageBus * +gedit_message_bus_new (void) +{ +	return GEDIT_MESSAGE_BUS (g_object_new (GEDIT_TYPE_MESSAGE_BUS, NULL)); +} + +/** + * gedit_message_bus_lookup: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * + * Get the registered #GeditMessageType for @method at @object_path. The  + * returned #GeditMessageType is owned by the bus and should not be unreffed. + * + * Return value: the registered #GeditMessageType or %NULL if no message type + *               is registered for @method at @object_path + * + */ +GeditMessageType * +gedit_message_bus_lookup (GeditMessageBus *bus, +			  const gchar	  *object_path, +			  const gchar	  *method) +{ +	gchar *identifier; +	GeditMessageType *message_type; +	 +	g_return_val_if_fail (GEDIT_IS_MESSAGE_BUS (bus), NULL); +	g_return_val_if_fail (object_path != NULL, NULL); +	g_return_val_if_fail (method != NULL, NULL); + +	identifier = gedit_message_type_identifier (object_path, method); +	message_type = GEDIT_MESSAGE_TYPE (g_hash_table_lookup (bus->priv->types, identifier)); +	 +	g_free (identifier); +	return message_type; +} + +/** + * gedit_message_bus_register: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method to register + * @num_optional: the number of optional arguments + * @...: NULL terminated list of key/gtype method argument pairs + * + * Register a message on the bus. A message must be registered on the bus before + * it can be send. This function registers the type arguments for @method at  + * @object_path. The arguments are specified with the variable arguments which  + * should contain pairs of const gchar *key and GType terminated by %NULL. The  + * last @num_optional arguments are registered as optional (and are thus not + * required when sending a message). + * + * This function emits a #GeditMessageBus::registered signal. + * + * Return value: the registered #GeditMessageType. The returned reference is + *               owned by the bus. If you want to keep it alive after + *               unregistering, use gedit_message_type_ref(). + * + */ +GeditMessageType * +gedit_message_bus_register (GeditMessageBus *bus, +			    const gchar     *object_path, +			    const gchar	    *method, +			    guint	     num_optional, +			    ...) +{ +	gchar *identifier; +	gpointer data; +	va_list var_args; +	GeditMessageType *message_type; + +	g_return_val_if_fail (GEDIT_IS_MESSAGE_BUS (bus), NULL); +	g_return_val_if_fail (gedit_message_type_is_valid_object_path (object_path), NULL); +	 +	if (gedit_message_bus_is_registered (bus, object_path, method)) +	{ +		g_warning ("Message type for '%s.%s' is already registered", object_path, method); +		return NULL; +	} + +	identifier = gedit_message_type_identifier (object_path, method); +	data = g_hash_table_lookup (bus->priv->types, identifier); +	 +	va_start (var_args, num_optional); +	message_type = gedit_message_type_new_valist (object_path,  +						      method, +						      num_optional, +						      var_args); +	va_end (var_args); +	 +	if (message_type) +	{ +		g_hash_table_insert (bus->priv->types, identifier, message_type); +		g_signal_emit (bus, message_bus_signals[REGISTERED], 0, message_type); +	} +	else +	{ +		g_free (identifier); +	} +	 +	return message_type;	 +} + +static void +gedit_message_bus_unregister_real (GeditMessageBus  *bus, +				   GeditMessageType *message_type, +				   gboolean          remove_from_store) +{ +	gchar *identifier; +	 +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); + +	identifier = gedit_message_type_identifier (gedit_message_type_get_object_path (message_type),  +						    gedit_message_type_get_method (message_type)); +	 +	/* Keep message type alive for signal emission */ +	gedit_message_type_ref (message_type); + +	if (!remove_from_store || g_hash_table_remove (bus->priv->types, identifier)) +		g_signal_emit (bus, message_bus_signals[UNREGISTERED], 0, message_type); +	 +	gedit_message_type_unref (message_type); +	g_free (identifier); +} + +/** + * gedit_message_bus_unregister: + * @bus: a #GeditMessageBus + * @message_type: the #GeditMessageType to unregister + * + * Unregisters a previously registered message type. This is especially useful  + * for plugins which should unregister message types when they are deactivated. + * + * This function emits the #GeditMessageBus::unregistered signal. + * + */ +void +gedit_message_bus_unregister (GeditMessageBus  *bus, +			      GeditMessageType *message_type) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	gedit_message_bus_unregister_real (bus, message_type, TRUE); +} + +typedef struct  +{ +	GeditMessageBus *bus; +	const gchar *object_path; +} UnregisterInfo; + +static gboolean +unregister_each (const gchar      *identifier, +		 GeditMessageType *message_type, +		 UnregisterInfo   *info) +{ +	if (strcmp (gedit_message_type_get_object_path (message_type), +		    info->object_path) == 0) +	{	 +		gedit_message_bus_unregister_real (info->bus, message_type, FALSE); +		return TRUE; +	} +	 +	return FALSE; +} + +/** + * gedit_message_bus_unregister_all: + * @bus: a #GeditMessageBus + * @object_path: the object path + * + * Unregisters all message types for @object_path. This is especially useful for + * plugins which should unregister message types when they are deactivated. + * + * This function emits the #GeditMessageBus::unregistered signal for all + * unregistered message types. + * + */ +void +gedit_message_bus_unregister_all (GeditMessageBus *bus, +			          const gchar     *object_path) +{ +	UnregisterInfo info = {bus, object_path}; + +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	g_return_if_fail (object_path != NULL); + +	g_hash_table_foreach_remove (bus->priv->types,  +				     (GHRFunc)unregister_each, +				     &info); +} + +/** + * gedit_message_bus_is_registered: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * + * Check whether a message type @method at @object_path is registered on the  + * bus. + * + * Return value: %TRUE if the @method at @object_path is a registered message  + *               type on the bus + * + */ +gboolean +gedit_message_bus_is_registered (GeditMessageBus	*bus, +				 const gchar	*object_path, +				 const gchar	*method) +{ +	gchar *identifier; +	gboolean ret; +	 +	g_return_val_if_fail (GEDIT_IS_MESSAGE_BUS (bus), FALSE); +	g_return_val_if_fail (object_path != NULL, FALSE); +	g_return_val_if_fail (method != NULL, FALSE); + +	identifier = gedit_message_type_identifier (object_path, method); +	ret = g_hash_table_lookup (bus->priv->types, identifier) != NULL; +	 +	g_free(identifier); +	return ret; +} + +typedef struct +{ +	GeditMessageBusForeach func; +	gpointer userdata; +} ForeachInfo; + +static void +foreach_type (const gchar      *key, +	      GeditMessageType *message_type, +	      ForeachInfo      *info) +{ +	gedit_message_type_ref (message_type); +	info->func (message_type, info->userdata); +	gedit_message_type_unref (message_type); +} + +/** + * gedit_message_bus_foreach: + * @bus: the #GeditMessagebus + * @func: the callback function + * @userdata: the user data to supply to the callback function + * + * Calls @func for each message type registered on the bus + * + */ +void  +gedit_message_bus_foreach (GeditMessageBus        *bus, +			   GeditMessageBusForeach  func, +			   gpointer		   userdata) +{ +	ForeachInfo info = {func, userdata}; +	 +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	g_return_if_fail (func != NULL); + +	g_hash_table_foreach (bus->priv->types, (GHFunc)foreach_type, &info); +} + +/** + * gedit_message_bus_connect: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @callback: function to be called when message @method at @object_path is sent + * @userdata: userdata to use for the callback + * @destroy_data: function to evoke with @userdata as argument when @userdata + *                needs to be freed + * + * Connect a callback handler to be evoked when message @method at @object_path + * is sent over the bus. + * + * Return value: the callback identifier + * + */ +guint +gedit_message_bus_connect (GeditMessageBus	*bus,  +		           const gchar		*object_path, +		           const gchar		*method, +		           GeditMessageCallback  callback, +		           gpointer		 userdata, +		           GDestroyNotify	 destroy_data) +{ +	Message *message; + +	g_return_val_if_fail (GEDIT_IS_MESSAGE_BUS (bus), 0); +	g_return_val_if_fail (object_path != NULL, 0); +	g_return_val_if_fail (method != NULL, 0); +	g_return_val_if_fail (callback != NULL, 0); +	 +	/* lookup the message and create if it does not exist yet */ +	message = lookup_message (bus, object_path, method, TRUE); +	 +	return add_listener (bus, message, callback, userdata, destroy_data); +} + +/** + * gedit_message_bus_disconnect: + * @bus: a #GeditMessageBus + * @id: the callback id as returned by gedit_message_bus_connect() + * + * Disconnects a previously connected message callback. + * + */ +void +gedit_message_bus_disconnect (GeditMessageBus *bus, +			      guint            id) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_id (bus, id, remove_listener); +} + +/** + * gedit_message_bus_disconnect_by_func: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @callback: the connected callback + * @userdata: the userdata with which the callback was connected + * + * Disconnects a previously connected message callback by matching the  + * provided callback function and userdata. See also  + * gedit_message_bus_disconnect(). + * + */ +void +gedit_message_bus_disconnect_by_func (GeditMessageBus      *bus, +				      const gchar	   *object_path, +				      const gchar	   *method, +				      GeditMessageCallback  callback, +				      gpointer		    userdata) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_match (bus, object_path, method, callback, userdata, remove_listener); +} + +/** + * gedit_message_bus_block: + * @bus: a #GeditMessageBus + * @id: the callback id + * + * Blocks evoking the callback specified by @id. Unblock the callback by + * using gedit_message_bus_unblock(). + * + */ +void +gedit_message_bus_block (GeditMessageBus *bus, +			 guint		  id) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_id (bus, id, block_listener); +} + +/** + * gedit_message_bus_block_by_func: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @callback: the callback to block + * @userdata: the userdata with which the callback was connected + * + * Blocks evoking the callback that matches provided @callback and @userdata. + * Unblock the callback using gedit_message_unblock_by_func(). + * + */ +void +gedit_message_bus_block_by_func (GeditMessageBus      *bus, +				 const gchar	      *object_path, +				 const gchar	      *method, +				 GeditMessageCallback  callback, +				 gpointer	       userdata) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_match (bus, object_path, method, callback, userdata, block_listener); +} + +/** + * gedit_message_bus_unblock: + * @bus: a #GeditMessageBus + * @id: the callback id + * + * Unblocks the callback specified by @id. + * + */ +void +gedit_message_bus_unblock (GeditMessageBus *bus, +			   guint	    id) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_id (bus, id, unblock_listener); +} + +/** + * gedit_message_bus_unblock_by_func: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @callback: the callback to block + * @userdata: the userdata with which the callback was connected + * + * Unblocks the callback that matches provided @callback and @userdata. + * + */ +void +gedit_message_bus_unblock_by_func (GeditMessageBus      *bus, +				   const gchar	        *object_path, +				   const gchar	        *method, +				   GeditMessageCallback  callback, +				   gpointer	         userdata) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	 +	process_by_match (bus, object_path, method, callback, userdata, unblock_listener); +} + +static gboolean +validate_message (GeditMessage *message) +{ +	if (!gedit_message_validate (message)) +	{ +		g_warning ("Message '%s.%s' is invalid", gedit_message_get_object_path (message), +							 gedit_message_get_method (message)); +		return FALSE; +	} +	 +	return TRUE; +} + +static void +send_message_real (GeditMessageBus *bus, +		   GeditMessage    *message) +{ +	if (!validate_message (message)) +	{ +		return; +	} +	 +	bus->priv->message_queue = g_list_prepend (bus->priv->message_queue,  +						   g_object_ref (message)); + +	if (bus->priv->idle_id == 0) +		bus->priv->idle_id = g_idle_add_full (G_PRIORITY_HIGH, +						      (GSourceFunc)idle_dispatch, +						      bus, +						      NULL); +} + +/** + * gedit_message_bus_send_message: + * @bus: a #GeditMessageBus + * @message: the message to send + * + * This sends the provided @message asynchronously over the bus. To send + * a message synchronously, use gedit_message_bus_send_message_sync(). The  + * convenience function gedit_message_bus_send() can be used to easily send + * a message without constructing the message object explicitly first. + * + */ +void +gedit_message_bus_send_message (GeditMessageBus *bus, +			        GeditMessage    *message) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	send_message_real (bus, message); +} + +static void +send_message_sync_real (GeditMessageBus *bus, +                        GeditMessage    *message) +{ +	if (!validate_message (message)) +	{ +		return; +	} +	 +	dispatch_message (bus, message); +} + +/** + * gedit_message_bus_send_message_sync: + * @bus: a #GeditMessageBus + * @message: the message to send + * + * This sends the provided @message synchronously over the bus. To send + * a message asynchronously, use gedit_message_bus_send_message(). The  + * convenience function gedit_message_bus_send_sync() can be used to easily send + * a message without constructing the message object explicitly first. + * + */ +void +gedit_message_bus_send_message_sync (GeditMessageBus *bus, +			             GeditMessage    *message) +{ +	g_return_if_fail (GEDIT_IS_MESSAGE_BUS (bus)); +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); + +	send_message_sync_real (bus, message);	 +} + +static GeditMessage * +create_message (GeditMessageBus *bus, +		const gchar     *object_path, +		const gchar     *method, +		va_list          var_args) +{ +	GeditMessageType *message_type; +	 +	message_type = gedit_message_bus_lookup (bus, object_path, method); +	 +	if (!message_type) +	{ +		g_warning ("Could not find message type for '%s.%s'", object_path, method); +		return NULL; +	} + +	return gedit_message_type_instantiate_valist (message_type,  +						      var_args); +} + +/** + * gedit_message_bus_send: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @...: NULL terminated list of key/value pairs + * + * This provides a convenient way to quickly send a message @method at  + * @object_path asynchronously over the bus. The variable argument list  + * specifies key (string) value pairs used to construct the message arguments.  + * To send a message synchronously use gedit_message_bus_send_sync(). + * + */ +void +gedit_message_bus_send (GeditMessageBus *bus, +			const gchar     *object_path, +			const gchar     *method, +			...) +{ +	va_list var_args; +	GeditMessage *message; +	 +	va_start (var_args, method); + +	message = create_message (bus, object_path, method, var_args); +	 +	if (message) +	{ +		send_message_real (bus, message); +		g_object_unref (message); +	} +	else +	{ +		g_warning ("Could not instantiate message"); +	} + +	va_end (var_args); +} + +/** + * gedit_message_bus_send_sync: + * @bus: a #GeditMessageBus + * @object_path: the object path + * @method: the method + * @...: NULL terminated list of key/value pairs + * + * This provides a convenient way to quickly send a message @method at  + * @object_path synchronously over the bus. The variable argument list  + * specifies key (string) value pairs used to construct the message  + * arguments. To send a message asynchronously use gedit_message_bus_send(). + * + * Return value: the constructed #GeditMessage. The caller owns a reference + *               to the #GeditMessage and should call g_object_unref() when + *               it is no longer needed + */ +GeditMessage * +gedit_message_bus_send_sync (GeditMessageBus *bus, +			     const gchar     *object_path, +			     const gchar     *method, +			     ...) +{ +	va_list var_args; +	GeditMessage *message; +	 +	va_start (var_args, method); +	message = create_message (bus, object_path, method, var_args); +	 +	if (message) +		send_message_sync_real (bus, message); + +	va_end (var_args); +	 +	return message; +} + +// ex:ts=8:noet: diff --git a/gedit/gedit-message-bus.h b/gedit/gedit-message-bus.h new file mode 100755 index 00000000..b60eabd0 --- /dev/null +++ b/gedit/gedit-message-bus.h @@ -0,0 +1,129 @@ +#ifndef __GEDIT_MESSAGE_BUS_H__ +#define __GEDIT_MESSAGE_BUS_H__ + +#include <glib-object.h> +#include <gedit/gedit-message.h> +#include <gedit/gedit-message-type.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_MESSAGE_BUS			(gedit_message_bus_get_type ()) +#define GEDIT_MESSAGE_BUS(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_MESSAGE_BUS, GeditMessageBus)) +#define GEDIT_MESSAGE_BUS_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_MESSAGE_BUS, GeditMessageBus const)) +#define GEDIT_MESSAGE_BUS_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_MESSAGE_BUS, GeditMessageBusClass)) +#define GEDIT_IS_MESSAGE_BUS(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_MESSAGE_BUS)) +#define GEDIT_IS_MESSAGE_BUS_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_MESSAGE_BUS)) +#define GEDIT_MESSAGE_BUS_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_MESSAGE_BUS, GeditMessageBusClass)) + +typedef struct _GeditMessageBus		GeditMessageBus; +typedef struct _GeditMessageBusClass	GeditMessageBusClass; +typedef struct _GeditMessageBusPrivate	GeditMessageBusPrivate; + +struct _GeditMessageBus { +	GObject parent; +	 +	GeditMessageBusPrivate *priv; +}; + +struct _GeditMessageBusClass { +	GObjectClass parent_class; +	 +	void (*dispatch)		(GeditMessageBus  *bus, +					 GeditMessage     *message); +	void (*registered)		(GeditMessageBus  *bus, +					 GeditMessageType *message_type); +	void (*unregistered)		(GeditMessageBus  *bus, +					 GeditMessageType *message_type); +}; + +typedef void (* GeditMessageCallback) 	(GeditMessageBus *bus, +					 GeditMessage	 *message, +					 gpointer	  userdata); + +typedef void (* GeditMessageBusForeach) (GeditMessageType *message_type, +					 gpointer	   userdata); + +GType gedit_message_bus_get_type (void) G_GNUC_CONST; + +GeditMessageBus *gedit_message_bus_get_default	(void); +GeditMessageBus *gedit_message_bus_new		(void); + +/* registering messages */ +GeditMessageType *gedit_message_bus_lookup	(GeditMessageBus 	*bus, +						 const gchar		*object_path, +						 const gchar		*method); +GeditMessageType *gedit_message_bus_register	(GeditMessageBus		*bus, +					   	 const gchar 		*object_path, +					  	 const gchar		*method, +					  	 guint		 	 num_optional, +					  	 ...) G_GNUC_NULL_TERMINATED; + +void gedit_message_bus_unregister	  (GeditMessageBus	*bus, +					   GeditMessageType	*message_type); + +void gedit_message_bus_unregister_all	  (GeditMessageBus	*bus, +					   const gchar		*object_path); + +gboolean gedit_message_bus_is_registered  (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method); + +void gedit_message_bus_foreach		  (GeditMessageBus        *bus, +					   GeditMessageBusForeach  func, +					   gpointer		   userdata); + + +/* connecting to message events */		    +guint gedit_message_bus_connect	 	  (GeditMessageBus	*bus,  +					   const gchar		*object_path, +					   const gchar		*method, +					   GeditMessageCallback	 callback, +					   gpointer		 userdata, +					   GDestroyNotify        destroy_data); + +void gedit_message_bus_disconnect	  (GeditMessageBus	*bus, +					   guint		 id); + +void gedit_message_bus_disconnect_by_func (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method, +					   GeditMessageCallback	 callback, +					   gpointer		 userdata); + +/* blocking message event callbacks */ +void gedit_message_bus_block		  (GeditMessageBus	*bus, +					   guint		 id); +void gedit_message_bus_block_by_func	  (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method, +					   GeditMessageCallback	 callback, +					   gpointer		 userdata); + +void gedit_message_bus_unblock		  (GeditMessageBus	*bus, +					   guint		 id); +void gedit_message_bus_unblock_by_func	  (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method, +					   GeditMessageCallback	 callback, +					   gpointer		 userdata); + +/* sending messages */ +void gedit_message_bus_send_message	  (GeditMessageBus	*bus, +					   GeditMessage		*message); +void gedit_message_bus_send_message_sync  (GeditMessageBus	*bus, +					   GeditMessage		*message); +					   +void gedit_message_bus_send		  (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method, +					   ...) G_GNUC_NULL_TERMINATED; +GeditMessage *gedit_message_bus_send_sync (GeditMessageBus	*bus, +					   const gchar		*object_path, +					   const gchar		*method, +					   ...) G_GNUC_NULL_TERMINATED; + +G_END_DECLS + +#endif /* __GEDIT_MESSAGE_BUS_H__ */ + +// ex:ts=8:noet: diff --git a/gedit/gedit-message-type.c b/gedit/gedit-message-type.c new file mode 100755 index 00000000..61e782b3 --- /dev/null +++ b/gedit/gedit-message-type.c @@ -0,0 +1,526 @@ +#include "gedit-message-type.h" + +/** + * SECTION:gedit-message-type + * @short_description: message type description + * @include: gedit/gedit-message-type.h + * + * A message type is a prototype description for a #GeditMessage used to + * transmit messages on a #GeditMessageBus. The message type describes + * the Object Path, Method and Arguments of the message. + * + * A message type can contain any number of required and optional arguments. + * To instantiate a #GeditMessage from a #GeditMessageType, use  + * gedit_message_type_instantiate(). + * + * Registering a new message type on a #GeditMessageBus with + * gedit_message_bus_register() internally creates a new #GeditMessageType. When + * then using gedit_message_bus_send(), an actual instantiation of the  + * registered type is internally created and send over the bus. + * + * <example> + * <programlisting> + * // Defining a new message type + * GeditMessageType *message_type = gedit_message_type_new ("/plugins/example", + *                                                          "method", + *                                                          0, + *                                                          "arg1", G_TYPE_STRING, + *                                                          NULL); + * + * // Instantiating an actual message from the type + * GeditMessage *message = gedit_message_type_instantiate (message_type, + *                                                         "arg1", "Hello World", + *                                                         NULL); + * </programlisting> + * </example> + * + * Since: 2.25.3 + * + */ +typedef struct +{ +	GType type; +	gboolean required; +} ArgumentInfo; + +struct _GeditMessageType +{ +	gint ref_count; + +	gchar *object_path; +	gchar *method; +	 +	guint num_arguments; +	guint num_required; +	 +	GHashTable *arguments; // mapping of key -> ArgumentInfo +}; + +/** + * gedit_message_type_ref: + * @message_type: the #GeditMessageType + * + * Increases the reference count on @message_type. + * + * Return value: @message_type + * + */ +GeditMessageType * +gedit_message_type_ref (GeditMessageType *message_type) +{ +	g_return_val_if_fail (message_type != NULL, NULL); +	g_atomic_int_inc (&message_type->ref_count); +	 +	return message_type; +} + +/** + * gedit_message_type_unref: + * @message_type: the #GeditMessageType + * + * Decreases the reference count on @message_type. When the reference count + * drops to 0, @message_type is destroyed. + * + */ +void  +gedit_message_type_unref (GeditMessageType *message_type) +{ +	g_return_if_fail (message_type != NULL); + +	if (!g_atomic_int_dec_and_test (&message_type->ref_count)) +		return; +	 +	g_free (message_type->object_path); +	g_free (message_type->method); +	 +	g_hash_table_destroy (message_type->arguments); +	g_free (message_type); +} + +/** + * gedit_message_type_get_type: + *  + * Retrieves the GType object which is associated with the + * #GeditMessageType class. + *  + * Return value: the GType associated with #GeditMessageType. + **/ +GType  +gedit_message_type_get_type (void) +{ +	static GType our_type = 0; + +	if (!our_type) +		our_type = g_boxed_type_register_static ( +			"GeditMessageType", +			(GBoxedCopyFunc) gedit_message_type_ref, +			(GBoxedFreeFunc) gedit_message_type_unref); + +	return our_type; +} + +/** + * gedit_message_type_identifier: + * @object_path: the object path + * @method: the method + * + * Get the string identifier for @method at @object_path. + * + * Return value: the identifier for @method at @object_path + * + */ +gchar * +gedit_message_type_identifier (const gchar *object_path, +			       const gchar *method) +{ +	return g_strconcat (object_path, ".", method, NULL); +} + +/** + * gedit_message_type_is_valid_object_path: + * @object_path: the object path + * + * Returns whether @object_path is a valid object path + * + * Return value: %TRUE if @object_path is a valid object path + * + */ +gboolean +gedit_message_type_is_valid_object_path (const gchar *object_path) +{ +	if (!object_path) +		return FALSE; +	 +	/* needs to start with / */ +	if (*object_path != '/') +		return FALSE; +	 +	while (*object_path) +	{ +		if (*object_path == '/') +		{ +			++object_path; +			 +			if (!*object_path || !(g_ascii_isalpha (*object_path) || *object_path == '_')) +				return FALSE; +		} +		else if (!(g_ascii_isalnum (*object_path) || *object_path == '_')) +		{ +			return FALSE; +		} +		 +		++object_path; +	} +	 +	return TRUE; +} + +/** + * gedit_message_type_is_supported: + * @type: the #GType + * + * Returns if @type is #GType supported by the message system. + * + * Return value: %TRUE if @type is a supported #GType + * + */ +gboolean +gedit_message_type_is_supported (GType type) +{ +	gint i = 0; +   +	static const GType type_list[] = +	{ +		G_TYPE_BOOLEAN, +		G_TYPE_CHAR, +		G_TYPE_UCHAR, +		G_TYPE_INT, +		G_TYPE_UINT, +		G_TYPE_LONG, +		G_TYPE_ULONG, +		G_TYPE_INT64, +		G_TYPE_UINT64, +		G_TYPE_ENUM, +		G_TYPE_FLAGS, +		G_TYPE_FLOAT, +		G_TYPE_DOUBLE, +		G_TYPE_STRING, +		G_TYPE_POINTER, +		G_TYPE_BOXED, +		G_TYPE_OBJECT, +		G_TYPE_INVALID +	}; + +	if (!G_TYPE_IS_VALUE_TYPE (type)) +		return FALSE; + +	while (type_list[i] != G_TYPE_INVALID) +	{ +		if (g_type_is_a (type, type_list[i])) +			return TRUE; +		i++; +	} + +	return FALSE; +} + +/** + * gedit_message_type_new_valist: + * @object_path: the object path + * @method: the method + * @num_optional: number of optional arguments + * @var_args: key/gtype pair variable argument list + * + * Create a new #GeditMessageType for @method at @object_path. Argument names + * and values are supplied by the NULL terminated variable argument list. + * The last @num_optional provided arguments are considered optional. + * + * Return value: the newly constructed #GeditMessageType + * + */ +GeditMessageType * +gedit_message_type_new_valist (const gchar *object_path, +			       const gchar *method, +			       guint        num_optional, +			       va_list      var_args) +{ +	GeditMessageType *message_type; + +	g_return_val_if_fail (object_path != NULL, NULL); +	g_return_val_if_fail (method != NULL, NULL); +	g_return_val_if_fail (gedit_message_type_is_valid_object_path (object_path), NULL); + +	message_type = g_new0(GeditMessageType, 1); +	 +	message_type->ref_count = 1; +	message_type->object_path = g_strdup(object_path); +	message_type->method = g_strdup(method); +	message_type->num_arguments = 0; +	message_type->arguments = g_hash_table_new_full (g_str_hash, +							 g_str_equal, +							 (GDestroyNotify)g_free, +							 (GDestroyNotify)g_free); + +	gedit_message_type_set_valist (message_type, num_optional, var_args); +	return message_type; +} + +/** + * gedit_message_type_new: + * @object_path: the object path + * @method: the method + * @num_optional: number of optional arguments + * @...: key/gtype pair variable argument list + * + * Create a new #GeditMessageType for @method at @object_path. Argument names + * and values are supplied by the NULL terminated variable argument list. + * The last @num_optional provided arguments are considered optional. + * + * Return value: the newly constructed #GeditMessageType + * + */ +GeditMessageType * +gedit_message_type_new (const gchar *object_path, +			const gchar *method, +			guint        num_optional, +			...) +{ +	GeditMessageType *message_type; +	va_list var_args; +	 +	va_start(var_args, num_optional); +	message_type = gedit_message_type_new_valist (object_path, method, num_optional, var_args); +	va_end(var_args); +	 +	return message_type; +} + +/** + * gedit_message_type_set: + * @message_type: the #GeditMessageType + * @num_optional: number of optional arguments + * @...: key/gtype pair variable argument list + * + * Sets argument names/types supplied by the NULL terminated variable + * argument list. The last @num_optional provided arguments are considered + * optional. + * + */ +void +gedit_message_type_set (GeditMessageType *message_type, +			guint		  num_optional, +			...) +{ +	va_list va_args; +	 +	va_start (va_args, num_optional); +	gedit_message_type_set_valist (message_type, num_optional, va_args); +	va_end (va_args); +} + +/** + * gedit_message_type_set_valist: + * @message_type: the #GeditMessageType + * @num_optional: number of optional arguments + * @var_args: key/gtype pair variable argument list + * + * Sets argument names/types supplied by the NULL terminated variable + * argument list @var_args. The last @num_optional provided arguments are  + * considered optional. + * + */ +void  +gedit_message_type_set_valist (GeditMessageType *message_type, +			       guint             num_optional, +			       va_list	         var_args) +{ +	const gchar *key; +	ArgumentInfo **optional = g_new0(ArgumentInfo *, num_optional); +	guint i; +	guint added = 0; + +	// parse key -> gtype pair arguments +	while ((key = va_arg (var_args, const gchar *)) != NULL) +	{ +		// get corresponding GType +		GType gtype = va_arg (var_args, GType); +		ArgumentInfo *info; +		 +		if (!gedit_message_type_is_supported (gtype)) +		{ +			g_error ("Message type '%s' is not supported", g_type_name (gtype)); + +			gedit_message_type_unref (message_type); +			g_free (optional); +			 +			return; +		} +		 +		info = g_new(ArgumentInfo, 1); +		info->type = gtype; +		info->required = TRUE; + +		g_hash_table_insert (message_type->arguments, g_strdup (key), info); +		 +		++message_type->num_arguments; +		++added; +		 +		if (num_optional > 0) +		{ +			for (i = num_optional - 1; i > 0; --i) +				optional[i] = optional[i - 1]; +		 +			*optional = info; +		} +	} +	 +	message_type->num_required += added; + +	// set required for last num_optional arguments +	for (i = 0; i < num_optional; ++i) +	{ +		if (optional[i]) +		{ +			optional[i]->required = FALSE; +			--message_type->num_required; +		} +	} +	 +	g_free (optional); +} + +/** + * gedit_message_type_instantiate_valist: + * @message_type: the #GeditMessageType + * @va_args: NULL terminated variable list of key/value pairs + * + * Instantiate a new message from the message type with specific values + * for the message arguments. + * + * Return value: the newly created message + * + */ +GeditMessage * +gedit_message_type_instantiate_valist (GeditMessageType *message_type, +				       va_list		 va_args) +{ +	GeditMessage *message; +	 +	g_return_val_if_fail (message_type != NULL, NULL); +	 +	message = GEDIT_MESSAGE (g_object_new (GEDIT_TYPE_MESSAGE, "type", message_type, NULL)); +	gedit_message_set_valist (message, va_args); +	 +	return message; +} + +/** + * gedit_message_type_instantiate: + * @message_type: the #GeditMessageType + * @...: NULL terminated variable list of key/value pairs + * + * Instantiate a new message from the message type with specific values + * for the message arguments. + * + * Return value: the newly created message + * + */ +GeditMessage * +gedit_message_type_instantiate (GeditMessageType *message_type, +				...) +{ +	GeditMessage *message; +	va_list va_args; +	 +	va_start (va_args, message_type); +	message = gedit_message_type_instantiate_valist (message_type, va_args); +	va_end (va_args); +	 +	return message; +} + +/** + * gedit_message_type_get_object_path: + * @message_type: the #GeditMessageType + * + * Get the message type object path. + * + * Return value: the message type object path + * + */ +const gchar * +gedit_message_type_get_object_path (GeditMessageType *message_type) +{ +	return message_type->object_path; +} + +/** + * gedit_message_type_get_method: + * @message_type: the #GeditMessageType + * + * Get the message type method. + * + * Return value: the message type method + * + */ +const gchar * +gedit_message_type_get_method (GeditMessageType *message_type) +{ +	return message_type->method; +} + +/** + * gedit_message_type_lookup: + * @message_type: the #GeditMessageType + * @key: the argument key + * + * Get the argument key #GType. + * + * Return value: the #GType of @key + * + */ +GType +gedit_message_type_lookup (GeditMessageType *message_type, +			   const gchar      *key) +{ +	ArgumentInfo *info = g_hash_table_lookup (message_type->arguments, key); +	 +	if (!info) +		return G_TYPE_INVALID; +	 +	return info->type; +} + +typedef struct +{ +	GeditMessageTypeForeach func; +	gpointer user_data; +} ForeachInfo; + +static void +foreach_gtype (const gchar  *key, +	       ArgumentInfo *info, +	       ForeachInfo  *finfo) +{ +	finfo->func (key, info->type, info->required, finfo->user_data); +} + +/** + * gedit_message_type_foreach: + * @message_type: the #GeditMessageType + * @func: the callback function + * @user_data: user data supplied to the callback function + * + * Calls @func for each argument in the message type. + * + */ +void  +gedit_message_type_foreach (GeditMessageType 	    *message_type, +			    GeditMessageTypeForeach  func, +			    gpointer		     user_data) +{ +	ForeachInfo info = {func, user_data}; +	g_hash_table_foreach (message_type->arguments, (GHFunc)foreach_gtype, &info); +} + +// ex:ts=8:noet: diff --git a/gedit/gedit-message-type.h b/gedit/gedit-message-type.h new file mode 100755 index 00000000..d3140cdc --- /dev/null +++ b/gedit/gedit-message-type.h @@ -0,0 +1,67 @@ +#ifndef __GEDIT_MESSAGE_TYPE_H__ +#define __GEDIT_MESSAGE_TYPE_H__ + +#include <glib-object.h> +#include <stdarg.h> + +#include "gedit-message.h" + +G_BEGIN_DECLS + +#define GEDIT_TYPE_MESSAGE_TYPE			(gedit_message_type_get_type ()) +#define GEDIT_MESSAGE_TYPE(x)			((GeditMessageType *)(x)) + +typedef void (*GeditMessageTypeForeach)		(const gchar *key,  +						 GType 	      type,  +						 gboolean     required,  +						 gpointer     user_data); + +typedef struct _GeditMessageType			GeditMessageType; + +GType gedit_message_type_get_type 		 (void) G_GNUC_CONST; + +gboolean gedit_message_type_is_supported 	 (GType type); +gchar *gedit_message_type_identifier		 (const gchar *object_path, +						  const gchar *method); +gboolean gedit_message_type_is_valid_object_path (const gchar *object_path); + +GeditMessageType *gedit_message_type_new	 (const gchar *object_path,  +						  const gchar *method, +						  guint	      num_optional, +						  ...) G_GNUC_NULL_TERMINATED; +GeditMessageType *gedit_message_type_new_valist	 (const gchar *object_path, +						  const gchar *method, +						  guint	      num_optional, +						  va_list      va_args); + +void gedit_message_type_set			 (GeditMessageType *message_type, +						  guint		   num_optional, +						  ...) G_GNUC_NULL_TERMINATED; +void gedit_message_type_set_valist		 (GeditMessageType *message_type, +						  guint		   num_optional, +						  va_list	           va_args); + +GeditMessageType *gedit_message_type_ref 	 (GeditMessageType *message_type); +void gedit_message_type_unref			 (GeditMessageType *message_type); + + +GeditMessage *gedit_message_type_instantiate_valist (GeditMessageType *message_type, +				       		     va_list	      va_args); +GeditMessage *gedit_message_type_instantiate 	 (GeditMessageType *message_type, +				       		  ...) G_GNUC_NULL_TERMINATED; + +const gchar *gedit_message_type_get_object_path	 (GeditMessageType *message_type); +const gchar *gedit_message_type_get_method	 (GeditMessageType *message_type); + +GType gedit_message_type_lookup			 (GeditMessageType *message_type, +						  const gchar      *key); +						  +void gedit_message_type_foreach 		 (GeditMessageType 	  *message_type, +						  GeditMessageTypeForeach  func, +						  gpointer	   	   user_data); + +G_END_DECLS + +#endif /* __GEDIT_MESSAGE_TYPE_H__ */ + +// ex:ts=8:noet: diff --git a/gedit/gedit-message.c b/gedit/gedit-message.c new file mode 100755 index 00000000..f1e12daa --- /dev/null +++ b/gedit/gedit-message.c @@ -0,0 +1,593 @@ +#include "gedit-message.h" +#include "gedit-message-type.h" + +#include <string.h> +#include <gobject/gvaluecollector.h> + +/** + * SECTION:gedit-message + * @short_description: message bus message object + * @include: gedit/gedit-message.h + * + * Communication on a #GeditMessageBus is done through messages. Messages are + * sent over the bus and received by connecting callbacks on the message bus. + * A #GeditMessage is an instantiation of a #GeditMessageType, containing + * values for the arguments as specified in the message type. + * + * A message can be seen as a method call, or signal emission depending on + * who is the sender and who is the receiver. There is no explicit distinction + * between methods and signals. + * + * Since: 2.25.3 + * + */ +#define GEDIT_MESSAGE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_MESSAGE, GeditMessagePrivate)) + +enum { +	PROP_0, + +	PROP_OBJECT_PATH, +	PROP_METHOD, +	PROP_TYPE +}; + +struct _GeditMessagePrivate +{ +	GeditMessageType *type; +	gboolean valid; + +	GHashTable *values; +}; + +G_DEFINE_TYPE (GeditMessage, gedit_message, G_TYPE_OBJECT) + +static void +gedit_message_finalize (GObject *object) +{ +	GeditMessage *message = GEDIT_MESSAGE (object); +	 +	gedit_message_type_unref (message->priv->type); +	g_hash_table_destroy (message->priv->values); + +	G_OBJECT_CLASS (gedit_message_parent_class)->finalize (object); +} + +static void +gedit_message_get_property (GObject    *object, +			    guint       prop_id, +			    GValue     *value, +			    GParamSpec *pspec) +{ +	GeditMessage *msg = GEDIT_MESSAGE (object); + +	switch (prop_id) +	{ +		case PROP_OBJECT_PATH: +			g_value_set_string (value, gedit_message_type_get_object_path (msg->priv->type)); +			break; +		case PROP_METHOD: +			g_value_set_string (value, gedit_message_type_get_method (msg->priv->type)); +			break; +		case PROP_TYPE: +			g_value_set_boxed (value, msg->priv->type); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_message_set_property (GObject      *object, +			    guint         prop_id, +			    const GValue *value, +			    GParamSpec   *pspec) +{ +	GeditMessage *msg = GEDIT_MESSAGE (object); + +	switch (prop_id) +	{ +		case PROP_TYPE: +			msg->priv->type = GEDIT_MESSAGE_TYPE (g_value_dup_boxed (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static GValue * +add_value (GeditMessage *message, +	   const gchar  *key) +{ +	GValue *value; +	GType type = gedit_message_type_lookup (message->priv->type, key); +	 +	if (type == G_TYPE_INVALID) +		return NULL; +	 +	value = g_new0 (GValue, 1); +	g_value_init (value, type); +	g_value_reset (value); + +	g_hash_table_insert (message->priv->values, g_strdup (key), value); +	 +	return value; +} + +static void +gedit_message_class_init (GeditMessageClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS(klass); +	 +	object_class->finalize = gedit_message_finalize; +	object_class->get_property = gedit_message_get_property; +	object_class->set_property = gedit_message_set_property; +	 +	/** +	 * GeditMessage:object_path: +	 * +	 * The messages object path (e.g. /gedit/object/path). +	 * +	 */ +	g_object_class_install_property (object_class, PROP_OBJECT_PATH, +					 g_param_spec_string ("object-path", +							      "OBJECT_PATH", +							      "The message object path", +							      NULL, +							      G_PARAM_READABLE | +							      G_PARAM_STATIC_STRINGS)); + +	/** +	 * GeditMessage:method: +	 * +	 * The messages method. +	 * +	 */ +	g_object_class_install_property (object_class, PROP_METHOD, +					 g_param_spec_string ("method", +							      "METHOD", +							      "The message method", +							      NULL, +							      G_PARAM_READABLE | +							      G_PARAM_STATIC_STRINGS)); +	 +	/** +	 * GeditMEssage:type: +	 * +	 * The message type. +	 * +	 */ +	g_object_class_install_property (object_class, PROP_TYPE, +					 g_param_spec_boxed ("type", +					 		     "TYPE", +					 		     "The message type", +					 		     GEDIT_TYPE_MESSAGE_TYPE, +					 		     G_PARAM_READWRITE | +					 		     G_PARAM_CONSTRUCT_ONLY | +					 		     G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof(GeditMessagePrivate)); +} + +static void +destroy_value (GValue *value) +{ +	g_value_unset (value); +	g_free (value); +} + +static void +gedit_message_init (GeditMessage *self) +{ +	self->priv = GEDIT_MESSAGE_GET_PRIVATE (self); + +	self->priv->values = g_hash_table_new_full (g_str_hash, +						    g_str_equal, +						    (GDestroyNotify)g_free, +						    (GDestroyNotify)destroy_value); +} + +static gboolean +set_value_real (GValue 	     *to,  +		const GValue *from) +{ +	GType from_type; +	GType to_type; +	 +	from_type = G_VALUE_TYPE (from); +	to_type = G_VALUE_TYPE (to); + +	if (!g_type_is_a (from_type, to_type)) +	{		 +		if (!g_value_transform (from, to)) +		{ +			g_warning ("%s: Unable to make conversion from %s to %s", +				   G_STRLOC, +				   g_type_name (from_type), +				   g_type_name (to_type)); +			return FALSE; +		} +		 +		return TRUE; +	} +	 +	g_value_copy (from, to); +	return TRUE; +} + +inline static GValue * +value_lookup (GeditMessage *message, +	      const gchar  *key, +	      gboolean	    create) +{ +	GValue *ret = (GValue *)g_hash_table_lookup (message->priv->values, key); +	 +	if (!ret && create) +		ret = add_value (message, key); +	 +	return ret; +} + +/** + * gedit_message_get_method: + * @message: the #GeditMessage + * + * Get the message method. + * + * Return value: the message method + * + */ +const gchar * +gedit_message_get_method (GeditMessage *message) +{ +	g_return_val_if_fail (GEDIT_IS_MESSAGE (message), NULL); +	 +	return gedit_message_type_get_method (message->priv->type); +} + +/** + * gedit_message_get_object_path: + * @message: the #GeditMessage + * + * Get the message object path. + * + * Return value: the message object path + * + */ +const gchar * +gedit_message_get_object_path (GeditMessage *message) +{ +	g_return_val_if_fail (GEDIT_IS_MESSAGE (message), NULL); +	 +	return gedit_message_type_get_object_path (message->priv->type); +} + +/** + * gedit_message_set: + * @message: the #GeditMessage + * @...: a NULL terminated variable list of key/value pairs + * + * Set values of message arguments. The supplied @var_args should contain + * pairs of keys and argument values. + * + */ +void +gedit_message_set (GeditMessage *message, +		   ...) +{ +	va_list ap; + +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); + +	va_start (ap, message); +	gedit_message_set_valist (message, ap); +	va_end (ap); +} + +/** + * gedit_message_set_valist: + * @message: the #GeditMessage + * @var_args: a NULL terminated variable list of key/value pairs + * + * Set values of message arguments. The supplied @var_args should contain + * pairs of keys and argument values. + * + */ +void +gedit_message_set_valist (GeditMessage *message, +			  va_list	var_args) +{ +	const gchar *key; + +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); + +	while ((key = va_arg (var_args, const gchar *)) != NULL) +	{ +		/* lookup the key */ +		GValue *container = value_lookup (message, key, TRUE); +		GValue value = {0,}; +		gchar *error = NULL; +		 +		if (!container) +		{ +			g_warning ("%s: Cannot set value for %s, does not exist",  +				   G_STRLOC, +				   key); +			 +			/* skip value */ +			va_arg (var_args, gpointer); +			continue; +		} +		 +		g_value_init (&value, G_VALUE_TYPE (container)); +		G_VALUE_COLLECT (&value, var_args, 0, &error); +		 +		if (error) +		{ +			g_warning ("%s: %s", G_STRLOC, error); +			continue; +		} + +		set_value_real (container, &value); +		g_value_unset (&value); +	} +} + +/** + * gedit_message_set_value: + * @message: the #GeditMessage + * @key: the argument key + * @value: the argument value + * + * Set value of message argument @key to @value. + * + */ +void +gedit_message_set_value (GeditMessage *message, +			 const gchar  *key, +			 GValue	      *value) +{ +	GValue *container; +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	container = value_lookup (message, key, TRUE); +	 +	if (!container) +	{ +		g_warning ("%s: Cannot set value for %s, does not exist",  +			   G_STRLOC,  +			   key); +		return; +	} +	 +	set_value_real (container, value); +} + +/** + * gedit_message_set_valuesv: + * @message: the #GeditMessage + * @keys: keys to set values for + * @values: values to set + * @n_values: number of arguments to set values for + * + * Set message argument values. + * + */ +void +gedit_message_set_valuesv (GeditMessage	 *message, +			   const gchar	**keys, +			   GValue        *values, +			   gint		  n_values) +{ +	gint i; +	 +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	for (i = 0; i < n_values; i++) +	{ +		gedit_message_set_value (message, keys[i], &values[i]); +	} +} + +/** + * gedit_message_get: + * @message: the #GeditMessage + * @...: a NULL variable argument list of key/value container pairs + * + * Get values of message arguments. The supplied @var_args should contain + * pairs of keys and pointers to variables which are set to the argument + * value for the specified key. + * + */ +void  +gedit_message_get (GeditMessage	*message, +		   ...) +{ +	va_list ap; + +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	va_start (ap, message); +	gedit_message_get_valist (message, ap); +	va_end (ap); +} + +/** + * gedit_message_get_valist: + * @message: the #GeditMessage + * @var_args: a NULL variable argument list of key/value container pairs + * + * Get values of message arguments. The supplied @var_args should contain + * pairs of keys and pointers to variables which are set to the argument + * value for the specified key. + * + */ +void +gedit_message_get_valist (GeditMessage *message, +			  va_list 	var_args) +{ +	const gchar *key; + +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	while ((key = va_arg (var_args, const gchar *)) != NULL) +	{ +		GValue *container; +		GValue copy = {0,}; +		gchar *error = NULL; + +		container = value_lookup (message, key, FALSE); +	 +		if (!container) +		{		 +			/* skip value */ +			va_arg (var_args, gpointer); +			continue; +		} +		 +		/* copy the value here, to be sure it isn't tainted */ +		g_value_init (©, G_VALUE_TYPE (container)); +		g_value_copy (container, ©); +		 +		G_VALUE_LCOPY (©, var_args, 0, &error); +		 +		if (error) +		{ +			g_warning ("%s: %s", G_STRLOC, error); +			g_free (error); +			 +			/* purposely leak the value here, because it might +			   be in a bad state */ +			continue; +		} +		 +		g_value_unset (©); +	} +} + +/** + * gedit_message_get_value: + * @message: the #GeditMessage + * @key: the argument key + * @value: value return container + * + * Get the value of a specific message argument. @value will be initialized + * with the correct type. + * + */ +void  +gedit_message_get_value (GeditMessage *message, +			 const gchar  *key, +			 GValue	      *value) +{ +	GValue *container; +	 +	g_return_if_fail (GEDIT_IS_MESSAGE (message)); +	 +	container = value_lookup (message, key, FALSE); +	 +	if (!container) +	{ +		g_warning ("%s: Invalid key `%s'", +			   G_STRLOC, +			   key); +		return; +	} +	 +	g_value_init (value, G_VALUE_TYPE (container)); +	set_value_real (value, container); +} + +/** + * gedit_message_get_key_type: + * @message: the #GeditMessage + * @key: the argument key + * + * Get the type of a message argument. + * + * Return value: the type of @key + * + */ +GType  +gedit_message_get_key_type (GeditMessage    *message, +			    const gchar	    *key) +{ +	g_return_val_if_fail (GEDIT_IS_MESSAGE (message), G_TYPE_INVALID); +	g_return_val_if_fail (message->priv->type != NULL, G_TYPE_INVALID); + +	return gedit_message_type_lookup (message->priv->type, key); +} + +/** + * gedit_message_has_key: + * @message: the #GeditMessage + * @key: the argument key + * + * Check whether the message has a specific key. + * + * Return value: %TRUE if @message has argument @key + * + */ +gboolean +gedit_message_has_key (GeditMessage *message, +		       const gchar  *key) +{ +	g_return_val_if_fail (GEDIT_IS_MESSAGE (message), FALSE); +	 +	return value_lookup (message, key, FALSE) != NULL; +} + +typedef struct +{ +	GeditMessage *message; +	gboolean valid;	 +} ValidateInfo; + +static void +validate_key (const gchar  *key, +	      GType         type, +	      gboolean	    required, +	      ValidateInfo *info) +{ +	GValue *value; +	 +	if (!info->valid || !required) +		return; +	 +	value = value_lookup (info->message, key, FALSE); +	 +	if (!value) +		info->valid = FALSE; +} + +/** + * gedit_message_validate: + * @message: the #GeditMessage + * + * Validates the message arguments according to the message type. + * + * Return value: %TRUE if the message is valid + * + */ +gboolean +gedit_message_validate (GeditMessage *message) +{ +	ValidateInfo info = {message, TRUE}; + +	g_return_val_if_fail (GEDIT_IS_MESSAGE (message), FALSE); +	g_return_val_if_fail (message->priv->type != NULL, FALSE); +	 +	if (!message->priv->valid) +	{ +		gedit_message_type_foreach (message->priv->type,  +					    (GeditMessageTypeForeach)validate_key, +					    &info); + +		message->priv->valid = info.valid; +	} +	 +	return message->priv->valid; +} + +// ex:ts=8:noet: diff --git a/gedit/gedit-message.h b/gedit/gedit-message.h new file mode 100755 index 00000000..9d17ac9e --- /dev/null +++ b/gedit/gedit-message.h @@ -0,0 +1,71 @@ +#ifndef __GEDIT_MESSAGE_H__ +#define __GEDIT_MESSAGE_H__ + +#include <glib-object.h> +#include <stdarg.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_MESSAGE			(gedit_message_get_type ()) +#define GEDIT_MESSAGE(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_MESSAGE, GeditMessage)) +#define GEDIT_MESSAGE_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_MESSAGE, GeditMessage const)) +#define GEDIT_MESSAGE_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_MESSAGE, GeditMessageClass)) +#define GEDIT_IS_MESSAGE(obj)			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_MESSAGE)) +#define GEDIT_IS_MESSAGE_CLASS(klass)		(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_MESSAGE)) +#define GEDIT_MESSAGE_GET_CLASS(obj)		(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_MESSAGE, GeditMessageClass)) + +typedef struct _GeditMessage		GeditMessage; +typedef struct _GeditMessageClass	GeditMessageClass; +typedef struct _GeditMessagePrivate	GeditMessagePrivate; + +struct _GeditMessage { +	GObject parent; +	 +	GeditMessagePrivate *priv; +}; + +struct _GeditMessageClass { +	GObjectClass parent_class; +}; + +GType gedit_message_get_type (void) G_GNUC_CONST; + +struct _GeditMessageType gedit_message_get_message_type (GeditMessage *message); + +void gedit_message_get			(GeditMessage	 *message, +					 ...) G_GNUC_NULL_TERMINATED; +void gedit_message_get_valist		(GeditMessage	 *message, +					 va_list 	  var_args); +void gedit_message_get_value		(GeditMessage	 *message, +					 const gchar	 *key, +					 GValue		 *value); + +void gedit_message_set			(GeditMessage	 *message, +					 ...) G_GNUC_NULL_TERMINATED; +void gedit_message_set_valist		(GeditMessage	 *message, +					 va_list	  	  var_args); +void gedit_message_set_value		(GeditMessage	 *message, +					 const gchar 	 *key, +					 GValue		 *value); +void gedit_message_set_valuesv		(GeditMessage	 *message, +					 const gchar	**keys, +					 GValue		 *values, +					 gint		  n_values); + +const gchar *gedit_message_get_object_path (GeditMessage	*message); +const gchar *gedit_message_get_method	(GeditMessage	 *message); + +gboolean gedit_message_has_key		(GeditMessage	 *message, +					 const gchar     *key); + +GType gedit_message_get_key_type 	(GeditMessage    *message, +			    		 const gchar     *key); + +gboolean gedit_message_validate		(GeditMessage	 *message); + + +G_END_DECLS + +#endif /* __GEDIT_MESSAGE_H__ */ + +// ex:ts=8:noet: diff --git a/gedit/gedit-metadata-manager.c b/gedit/gedit-metadata-manager.c new file mode 100755 index 00000000..bdb00fff --- /dev/null +++ b/gedit/gedit-metadata-manager.c @@ -0,0 +1,563 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-metadata-manager.c + * This file is part of gedit + * + * Copyright (C) 2003-2007  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2003-2007. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <time.h> +#include <stdlib.h> +#include <libxml/xmlreader.h> +#include "gedit-metadata-manager.h" +#include "gedit-debug.h" +#include "gedit-dirs.h" + +/* +#define GEDIT_METADATA_VERBOSE_DEBUG	1 +*/ + +#define METADATA_FILE 	"gedit-metadata.xml" + +#define MAX_ITEMS	50 + +typedef struct _GeditMetadataManager GeditMetadataManager; + +typedef struct _Item Item; + +struct _Item +{ +	time_t	 	 atime; /* time of last access */ + +	GHashTable	*values; +}; +	 +struct _GeditMetadataManager +{ +	gboolean	 values_loaded; /* It is true if the file  +					   has been read */ + +	guint 		 timeout_id; + +	GHashTable	*items; +}; + +static gboolean gedit_metadata_manager_save (gpointer data); + + +static GeditMetadataManager *gedit_metadata_manager = NULL; + +static void +item_free (gpointer data) +{ +	Item *item; +	 +	g_return_if_fail (data != NULL); + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug (DEBUG_METADATA); +#endif + +	item = (Item *)data; + +	if (item->values != NULL) +		g_hash_table_destroy (item->values); + +	g_free (item); +} + +static void +gedit_metadata_manager_arm_timeout (void) +{ +	if (gedit_metadata_manager->timeout_id == 0) +	{ +		gedit_metadata_manager->timeout_id =  +			g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE, +						    2, +						    (GSourceFunc)gedit_metadata_manager_save, +						    NULL, +						    NULL); +	} +} + +static gboolean +gedit_metadata_manager_init (void) +{ +	gedit_debug (DEBUG_METADATA); + +	if (gedit_metadata_manager != NULL) +		return TRUE; + +	gedit_metadata_manager = g_new0 (GeditMetadataManager, 1); + +	gedit_metadata_manager->values_loaded = FALSE; + +	gedit_metadata_manager->items =  +		g_hash_table_new_full (g_str_hash,  +				       g_str_equal,  +				       g_free, +				       item_free); + +	return TRUE; +} + +/* This function must be called before exiting gedit */ +void +gedit_metadata_manager_shutdown (void) +{ +	gedit_debug (DEBUG_METADATA); + +	if (gedit_metadata_manager == NULL) +		return; + +	if (gedit_metadata_manager->timeout_id) +	{ +		g_source_remove (gedit_metadata_manager->timeout_id); +		gedit_metadata_manager->timeout_id = 0; +		gedit_metadata_manager_save (NULL); +	} + +	if (gedit_metadata_manager->items != NULL) +		g_hash_table_destroy (gedit_metadata_manager->items); + +	g_free (gedit_metadata_manager); +	gedit_metadata_manager = NULL; +} + +static void +parseItem (xmlDocPtr doc, xmlNodePtr cur) +{ +	Item *item; +	 +	xmlChar *uri; +	xmlChar *atime; +	 +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug (DEBUG_METADATA); +#endif	 + +	if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0) +			return; + +	uri = xmlGetProp (cur, (const xmlChar *)"uri"); +	if (uri == NULL) +		return; +	 +	atime = xmlGetProp (cur, (const xmlChar *)"atime"); +	if (atime == NULL) +	{ +		xmlFree (uri); +		return; +	} + +	item = g_new0 (Item, 1); + +	item->atime = g_ascii_strtoull ((char *)atime, NULL, 0); + +	item->values = g_hash_table_new_full (g_str_hash,  +					      g_str_equal,  +					      g_free,  +					      g_free); + +	cur = cur->xmlChildrenNode; + +	while (cur != NULL) +	{ +		if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0) +		{ +			xmlChar *key; +			xmlChar *value; + +			key = xmlGetProp (cur, (const xmlChar *)"key"); +			value = xmlGetProp (cur, (const xmlChar *)"value"); + +			if ((key != NULL) && (value != NULL)) +				g_hash_table_insert (item->values, +						     g_strdup ((gchar *)key),  +						     g_strdup ((gchar *)value)); + +			if (key != NULL) +				xmlFree (key); +			if (value != NULL) +				xmlFree (value); +		} + +		cur = cur->next; +	} + +	g_hash_table_insert (gedit_metadata_manager->items, +			     g_strdup ((gchar *)uri), +			     item); + +	xmlFree (uri); +	xmlFree (atime); +} + +static gchar * +get_metadata_filename (void) +{ +	gchar *cache_dir; +	gchar *metadata; + +	cache_dir = gedit_dirs_get_user_cache_dir (); + +	metadata = g_build_filename (cache_dir, +				     METADATA_FILE, +				     NULL); + +	g_free (cache_dir); + +	return metadata; +} + +static gboolean +load_values (void) +{ +	xmlDocPtr doc; +	xmlNodePtr cur; +	gchar *file_name; + +	gedit_debug (DEBUG_METADATA); + +	g_return_val_if_fail (gedit_metadata_manager != NULL, FALSE); +	g_return_val_if_fail (gedit_metadata_manager->values_loaded == FALSE, FALSE); + +	gedit_metadata_manager->values_loaded = TRUE; +		 +	xmlKeepBlanksDefault (0); + +	/* FIXME: file locking - Paolo */ +	file_name = get_metadata_filename (); +	if ((file_name == NULL) || +	    (!g_file_test (file_name, G_FILE_TEST_EXISTS))) +	{ +		g_free (file_name); +		return FALSE; +	} + +	doc = xmlParseFile (file_name); +	g_free (file_name); + +	if (doc == NULL) +	{ +		return FALSE; +	} + +	cur = xmlDocGetRootElement (doc); +	if (cur == NULL)  +	{ +		g_message ("The metadata file '%s' is empty", METADATA_FILE); +		xmlFreeDoc (doc); +	 +		return FALSE; +	} + +	if (xmlStrcmp (cur->name, (const xmlChar *) "metadata"))  +	{ +		g_message ("File '%s' is of the wrong type", METADATA_FILE); +		xmlFreeDoc (doc); +		 +		return FALSE; +	} + +	cur = xmlDocGetRootElement (doc); +	cur = cur->xmlChildrenNode; +	 +	while (cur != NULL) +	{ +		parseItem (doc, cur); + +		cur = cur->next; +	} + +	xmlFreeDoc (doc); + +	return TRUE; +} + +gchar * +gedit_metadata_manager_get (const gchar *uri, +			    const gchar *key) +{ +	Item *item; +	gchar *value; + +	g_return_val_if_fail (uri != NULL, NULL); +	g_return_val_if_fail (key != NULL, NULL); + +	gedit_debug_message (DEBUG_METADATA, "URI: %s --- key: %s", uri, key ); + +	gedit_metadata_manager_init (); + +	if (!gedit_metadata_manager->values_loaded) +	{ +		gboolean res; + +		res = load_values (); + +		if (!res) +			return NULL; +	} + +	item = (Item *)g_hash_table_lookup (gedit_metadata_manager->items, +					    uri); + +	if (item == NULL) +		return NULL; + +	item->atime = time (NULL); +	 +	if (item->values == NULL) +		return NULL; + +	value = g_hash_table_lookup (item->values, key); + +	if (value == NULL) +		return NULL; +	else +		return g_strdup (value); +} + +void +gedit_metadata_manager_set (const gchar *uri, +			    const gchar *key, +			    const gchar *value) +{ +	Item *item; + +	g_return_if_fail (uri != NULL); +	g_return_if_fail (key != NULL); + +	gedit_debug_message (DEBUG_METADATA, "URI: %s --- key: %s --- value: %s", uri, key, value); +	 +	gedit_metadata_manager_init (); + +	if (!gedit_metadata_manager->values_loaded) +	{ +		gboolean res; + +		res = load_values (); + +		if (!res) +			return; +	} + +	item = (Item *)g_hash_table_lookup (gedit_metadata_manager->items, +					    uri); + +	if (item == NULL) +	{ +		item = g_new0 (Item, 1); + +		g_hash_table_insert (gedit_metadata_manager->items, +				     g_strdup (uri), +				     item); +	} +	 +	if (item->values == NULL) +		 item->values = g_hash_table_new_full (g_str_hash,  +				 		       g_str_equal,  +						       g_free,  +						       g_free); +	if (value != NULL) +		g_hash_table_insert (item->values, +				     g_strdup (key), +				     g_strdup (value)); +	else +		g_hash_table_remove (item->values, +				     key); + +	item->atime = time (NULL); + +	gedit_metadata_manager_arm_timeout (); +} + +static void +save_values (const gchar *key, const gchar *value, xmlNodePtr parent) +{ +	xmlNodePtr xml_node; + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug (DEBUG_METADATA); +#endif	 + +	g_return_if_fail (key != NULL); +	 +	if (value == NULL) +		return; +		 +	xml_node = xmlNewChild (parent, +				NULL, +				(const xmlChar *)"entry", +				NULL); + +	xmlSetProp (xml_node, +		    (const xmlChar *)"key", +		    (const xmlChar *)key); +	xmlSetProp (xml_node, +		    (const xmlChar *)"value", +		    (const xmlChar *)value); + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug_message (DEBUG_METADATA, "entry: %s = %s", key, value); +#endif	 +} + +static void +save_item (const gchar *key, const gpointer *data, xmlNodePtr parent) +{ +	xmlNodePtr xml_node; +	const Item *item = (const Item *)data; +	gchar *atime; + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug (DEBUG_METADATA); +#endif + +	g_return_if_fail (key != NULL); + +	if (item == NULL) +		return; + +	xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL); + +	xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key); + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug_message (DEBUG_METADATA, "uri: %s", key); +#endif	 + +	atime = g_strdup_printf ("%ld", item->atime); +	xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime);	 + +#ifdef GEDIT_METADATA_VERBOSE_DEBUG +	gedit_debug_message (DEBUG_METADATA, "atime: %s", atime); +#endif	 + +	g_free (atime); + +    	g_hash_table_foreach (item->values, +			      (GHFunc)save_values, +			      xml_node); +} + +static void +get_oldest (const gchar *key, const gpointer value, const gchar ** key_to_remove) +{ +	const Item *item = (const Item *)value; + +	if (*key_to_remove == NULL) +	{ +		*key_to_remove = key; +	} +	else +	{ +		const Item *item_to_remove =  +			g_hash_table_lookup (gedit_metadata_manager->items, +					     *key_to_remove); + +		g_return_if_fail (item_to_remove != NULL); + +		if (item->atime < item_to_remove->atime) +		{ +			*key_to_remove = key; +		} +	}	 +} + +static void +resize_items (void) +{ +	while (g_hash_table_size (gedit_metadata_manager->items) > MAX_ITEMS) +	{ +		gpointer key_to_remove = NULL; + +		g_hash_table_foreach (gedit_metadata_manager->items, +				      (GHFunc)get_oldest, +				      &key_to_remove); + +		g_return_if_fail (key_to_remove != NULL); +		 +		g_hash_table_remove (gedit_metadata_manager->items, +				     key_to_remove); +	} +} + +static gboolean +gedit_metadata_manager_save (gpointer data) +{	 +	xmlDocPtr  doc; +	xmlNodePtr root; +	gchar *file_name; + +	gedit_debug (DEBUG_METADATA); + +	gedit_metadata_manager->timeout_id = 0; + +	resize_items (); +		 +	xmlIndentTreeOutput = TRUE; + +	doc = xmlNewDoc ((const xmlChar *)"1.0"); +	if (doc == NULL) +		return TRUE; + +	/* Create metadata root */ +	root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL); +	xmlDocSetRootElement (doc, root); + +    	g_hash_table_foreach (gedit_metadata_manager->items, +			      (GHFunc)save_item, +			      root);         + +	/* FIXME: lock file - Paolo */ +	file_name = get_metadata_filename (); +	if (file_name != NULL) +	{ +		gchar *cache_dir; +		int res; + +		/* make sure the cache dir exists */ +		cache_dir = gedit_dirs_get_user_cache_dir (); +		res = g_mkdir_with_parents (cache_dir, 0755); +		if (res != -1) +		{ +			xmlSaveFormatFile (file_name, doc, 1); +		} + +		g_free (cache_dir); +		g_free (file_name); +	} + +	xmlFreeDoc (doc);  + +	gedit_debug_message (DEBUG_METADATA, "DONE"); + +	return FALSE; +} + diff --git a/gedit/gedit-metadata-manager.h b/gedit/gedit-metadata-manager.h new file mode 100755 index 00000000..6ee324f6 --- /dev/null +++ b/gedit/gedit-metadata-manager.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-metadata-manager.h + * This file is part of gedit + * + * Copyright (C) 2003  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2003. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef __GEDIT_METADATA_MANAGER_H__ +#define __GEDIT_METADATA_MANAGER_H__ + +#include <glib.h> + +G_BEGIN_DECLS + + +/* This function must be called before exiting gedit */ +void		 gedit_metadata_manager_shutdown 	(void); + + +gchar		*gedit_metadata_manager_get 		(const gchar *uri, +					     		 const gchar *key); +void		 gedit_metadata_manager_set		(const gchar *uri, +							 const gchar *key, +							 const gchar *value); + +G_END_DECLS + +#endif /* __GEDIT_METADATA_MANAGER_H__ */ diff --git a/gedit/gedit-notebook.c b/gedit/gedit-notebook.c new file mode 100755 index 00000000..2578815a --- /dev/null +++ b/gedit/gedit-notebook.c @@ -0,0 +1,1099 @@ +/* + * gedit-notebook.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +/* This file is a modified version of the epiphany file ephy-notebook.c + * Here the relevant copyright: + * + *  Copyright (C) 2002 Christophe Fergeau + *  Copyright (C) 2003 Marco Pesenti Gritti + *  Copyright (C) 2003, 2004 Christian Persch + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib-object.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-notebook.h" +#include "gedit-tab.h" +#include "gedit-tab-label.h" +#include "gedit-marshal.h" +#include "gedit-window.h" + +#ifdef BUILD_SPINNER +#include "gedit-spinner.h" +#endif + +#define AFTER_ALL_TABS -1 +#define NOT_IN_APP_WINDOWS -2 + +#define GEDIT_NOTEBOOK_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_NOTEBOOK, GeditNotebookPrivate)) + +struct _GeditNotebookPrivate +{ +	GList         *focused_pages; +	gulong         motion_notify_handler_id; +	gint           x_start; +	gint           y_start; +	gint           drag_in_progress : 1; +	gint	       always_show_tabs : 1; +	gint           close_buttons_sensitive : 1; +	gint           tab_drag_and_drop_enabled : 1; +	guint          destroy_has_run : 1; +}; + +G_DEFINE_TYPE(GeditNotebook, gedit_notebook, GTK_TYPE_NOTEBOOK) + +static void gedit_notebook_finalize (GObject *object); + +static gboolean gedit_notebook_change_current_page (GtkNotebook *notebook, +						    gint         offset); + +static void move_current_tab_to_another_notebook  (GeditNotebook  *src, +						   GeditNotebook  *dest, +						   GdkEventMotion *event, +						   gint            dest_position); + +/* Local variables */ +static GdkCursor *cursor = NULL; + +/* Signals */ +enum +{ +	TAB_ADDED, +	TAB_REMOVED, +	TABS_REORDERED, +	TAB_DETACHED, +	TAB_CLOSE_REQUEST, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +gedit_notebook_destroy (GtkObject *object) +{ +	GeditNotebook *notebook = GEDIT_NOTEBOOK (object); + +	if (!notebook->priv->destroy_has_run) +	{ +		GList *children, *l; + +		children = gtk_container_get_children (GTK_CONTAINER (notebook)); + +		for (l = children; l != NULL; l = g_list_next (l)) +		{ +			gedit_notebook_remove_tab (notebook, +						   GEDIT_TAB (l->data)); +		} + +		g_list_free (children); +		notebook->priv->destroy_has_run = TRUE; +	} + +	GTK_OBJECT_CLASS (gedit_notebook_parent_class)->destroy (object); +} + +static void +gedit_notebook_class_init (GeditNotebookClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); +	GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); + +	object_class->finalize = gedit_notebook_finalize; +	gtkobject_class->destroy = gedit_notebook_destroy; +	 +	notebook_class->change_current_page = gedit_notebook_change_current_page; + +	signals[TAB_ADDED] = +		g_signal_new ("tab_added", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditNotebookClass, tab_added), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[TAB_REMOVED] = +		g_signal_new ("tab_removed", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditNotebookClass, tab_removed), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[TAB_DETACHED] = +		g_signal_new ("tab_detached", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditNotebookClass, tab_detached), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[TABS_REORDERED] = +		g_signal_new ("tabs_reordered", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditNotebookClass, tabs_reordered), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, +			      0); +	signals[TAB_CLOSE_REQUEST] = +		g_signal_new ("tab-close-request", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditNotebookClass, tab_close_request), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); + +	g_type_class_add_private (object_class, sizeof(GeditNotebookPrivate)); +} + +static GeditNotebook * +find_notebook_at_pointer (gint abs_x, gint abs_y) +{ +	GdkWindow *win_at_pointer; +	GdkWindow *toplevel_win; +	gpointer toplevel = NULL; +	gint x, y; + +	/* FIXME multi-head */ +	win_at_pointer = gdk_window_at_pointer (&x, &y); +	if (win_at_pointer == NULL) +	{ +		/* We are outside all windows of the same application */ +		return NULL; +	} + +	toplevel_win = gdk_window_get_toplevel (win_at_pointer); + +	/* get the GtkWidget which owns the toplevel GdkWindow */ +	gdk_window_get_user_data (toplevel_win, &toplevel); + +	/* toplevel should be an GeditWindow */ +	if ((toplevel != NULL) &&  +	    GEDIT_IS_WINDOW (toplevel)) +	{ +		return GEDIT_NOTEBOOK (_gedit_window_get_notebook +						(GEDIT_WINDOW (toplevel))); +	} + +	/* We are outside all windows containing a notebook */ +	return NULL; +} + +static gboolean +is_in_notebook_window (GeditNotebook *notebook, +		       gint           abs_x,  +		       gint           abs_y) +{ +	GeditNotebook *nb_at_pointer; + +	g_return_val_if_fail (notebook != NULL, FALSE); + +	nb_at_pointer = find_notebook_at_pointer (abs_x, abs_y); + +	return (nb_at_pointer == notebook); +} + +static gint +find_tab_num_at_pos (GeditNotebook *notebook,  +		     gint           abs_x,  +		     gint           abs_y) +{ +	GtkPositionType tab_pos; +	int page_num = 0; +	GtkNotebook *nb = GTK_NOTEBOOK (notebook); +	GtkWidget *page; + +	tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook)); + +	if (GTK_NOTEBOOK (notebook)->first_tab == NULL) +	{ +		return AFTER_ALL_TABS; +	} + +	/* For some reason unfullscreen + quick click can +	   cause a wrong click event to be reported to the tab */ +	if (!is_in_notebook_window (notebook, abs_x, abs_y)) +	{ +		return NOT_IN_APP_WINDOWS; +	} + +	while ((page = gtk_notebook_get_nth_page (nb, page_num)) != NULL) +	{ +		GtkWidget *tab; +		gint max_x, max_y; +		gint x_root, y_root; + +		tab = gtk_notebook_get_tab_label (nb, page); +		g_return_val_if_fail (tab != NULL, AFTER_ALL_TABS); + +		if (!GTK_WIDGET_MAPPED (GTK_WIDGET (tab))) +		{ +			++page_num; +			continue; +		} + +		gdk_window_get_origin (GDK_WINDOW (tab->window), +				       &x_root, &y_root); + +		max_x = x_root + tab->allocation.x + tab->allocation.width; +		max_y = y_root + tab->allocation.y + tab->allocation.height; + +		if (((tab_pos == GTK_POS_TOP) ||  +		     (tab_pos == GTK_POS_BOTTOM)) && +		    (abs_x <= max_x)) +		{ +			return page_num; +		} +		else if (((tab_pos == GTK_POS_LEFT) ||  +		          (tab_pos == GTK_POS_RIGHT)) &&  +		         (abs_y <= max_y)) +		{ +			return page_num; +		} + +		++page_num; +	} +	 +	return AFTER_ALL_TABS; +} + +static gint  +find_notebook_and_tab_at_pos (gint            abs_x,  +			      gint            abs_y, +			      GeditNotebook **notebook, +			      gint           *page_num) +{ +	*notebook = find_notebook_at_pointer (abs_x, abs_y); +	if (*notebook == NULL) +	{ +		return NOT_IN_APP_WINDOWS; +	} +	 +	*page_num = find_tab_num_at_pos (*notebook, abs_x, abs_y); + +	if (*page_num < 0) +	{ +		return *page_num; +	} +	else +	{ +		return 0; +	} +} + +/** + * gedit_notebook_move_tab: + * @src: a #GeditNotebook + * @dest: a #GeditNotebook + * @tab: a #GeditTab + * @dest_position: the position for @tab + * + * Moves @tab from @src to @dest. + * If dest_position is greater than or equal to the number of tabs  + * of the destination nootebook or negative, tab will be moved to the  + * end of the tabs. + */ +void +gedit_notebook_move_tab (GeditNotebook *src, +			 GeditNotebook *dest, +			 GeditTab      *tab, +			 gint           dest_position) +{ +	g_return_if_fail (GEDIT_IS_NOTEBOOK (src));	 +	g_return_if_fail (GEDIT_IS_NOTEBOOK (dest)); +	g_return_if_fail (src != dest); +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	/* make sure the tab isn't destroyed while we move it */ +	g_object_ref (tab); +	gedit_notebook_remove_tab (src, tab); +	gedit_notebook_add_tab (dest, tab, dest_position, TRUE); +	g_object_unref (tab); +} + +/** + * gedit_notebook_reorder_tab: + * @src: a #GeditNotebook + * @tab: a #GeditTab + * @dest_position: the position for @tab + * + * Reorders the page containing @tab, so that it appears in @dest_position position. + * If dest_position is greater than or equal to the number of tabs  + * of the destination notebook or negative, tab will be moved to the  + * end of the tabs. + */ +void +gedit_notebook_reorder_tab (GeditNotebook *src, +			    GeditTab      *tab, +			    gint           dest_position) +{ +	gint old_position; +	 +	g_return_if_fail (GEDIT_IS_NOTEBOOK (src));	 +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	old_position = gtk_notebook_page_num (GTK_NOTEBOOK (src),  +				    	      GTK_WIDGET (tab)); +				    	       +	if (old_position == dest_position) +		return; + +	gtk_notebook_reorder_child (GTK_NOTEBOOK (src),  +				    GTK_WIDGET (tab), +				    dest_position); +		 +	if (!src->priv->drag_in_progress) +	{ +		g_signal_emit (G_OBJECT (src),  +			       signals[TABS_REORDERED],  +			       0); +	} +} + +static void +drag_start (GeditNotebook *notebook, +	    guint32        time) +{ +	notebook->priv->drag_in_progress = TRUE; + +	/* get a new cursor, if necessary */ +	/* FIXME multi-head */ +	if (cursor == NULL) +		cursor = gdk_cursor_new (GDK_FLEUR); + +	/* grab the pointer */ +	gtk_grab_add (GTK_WIDGET (notebook)); + +	/* FIXME multi-head */ +	if (!gdk_pointer_is_grabbed ()) +	{ +		gdk_pointer_grab (gtk_widget_get_window (GTK_WIDGET (notebook)), +				  FALSE, +				  GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, +				  NULL,  +				  cursor,  +				  time); +	} +} + +static void +drag_stop (GeditNotebook *notebook) +{ +	if (notebook->priv->drag_in_progress) +	{ +		g_signal_emit (G_OBJECT (notebook),  +			       signals[TABS_REORDERED],  +			       0); +	} + +	notebook->priv->drag_in_progress = FALSE; +	if (notebook->priv->motion_notify_handler_id != 0) +	{ +		g_signal_handler_disconnect (G_OBJECT (notebook), +					     notebook->priv->motion_notify_handler_id); +		notebook->priv->motion_notify_handler_id = 0; +	} +} + +/* This function is only called during dnd, we don't need to emit TABS_REORDERED + * here, instead we do it on drag_stop + */ +static void +move_current_tab (GeditNotebook *notebook, +	          gint           dest_position) +{ +	gint cur_page_num; + +	cur_page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)); + +	if (dest_position != cur_page_num) +	{ +		GtkWidget *cur_tab; +		 +		cur_tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), +						     cur_page_num); +						      +		gedit_notebook_reorder_tab (GEDIT_NOTEBOOK (notebook), +					    GEDIT_TAB (cur_tab), +					    dest_position); +	} +} + +static gboolean +motion_notify_cb (GeditNotebook  *notebook, +		  GdkEventMotion *event, +		  gpointer        data) +{ +	GeditNotebook *dest; +	gint page_num; +	gint result; + +	if (notebook->priv->drag_in_progress == FALSE) +	{ +		if (notebook->priv->tab_drag_and_drop_enabled == FALSE) +			return FALSE; +			 +		if (gtk_drag_check_threshold (GTK_WIDGET (notebook), +					      notebook->priv->x_start, +					      notebook->priv->y_start, +					      event->x_root,  +					      event->y_root)) +		{ +			drag_start (notebook, event->time); +			return TRUE; +		} + +		return FALSE; +	} + +	result = find_notebook_and_tab_at_pos ((gint)event->x_root, +					       (gint)event->y_root, +					       &dest,  +					       &page_num); + +	if (result != NOT_IN_APP_WINDOWS) +	{ +		if (dest != notebook) +		{ +			move_current_tab_to_another_notebook (notebook,  +							      dest, +						      	      event,  +						      	      page_num); +		} +		else +		{ +			g_return_val_if_fail (page_num >= -1, FALSE); +			move_current_tab (notebook, page_num); +		} +	} + +	return FALSE; +} + +static void +move_current_tab_to_another_notebook (GeditNotebook  *src, +				      GeditNotebook  *dest, +				      GdkEventMotion *event, +				      gint            dest_position) +{ +	GeditTab *tab; +	gint cur_page; + +	/* This is getting tricky, the tab was dragged in a notebook +	 * in another window of the same app, we move the tab +	 * to that new notebook, and let this notebook handle the +	 * drag +	 */ +	g_return_if_fail (GEDIT_IS_NOTEBOOK (dest)); +	g_return_if_fail (dest != src); + +	cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (src)); +	tab = GEDIT_TAB (gtk_notebook_get_nth_page (GTK_NOTEBOOK (src),  +						    cur_page)); + +	/* stop drag in origin window */ +	/* ungrab the pointer if it's grabbed */ +	drag_stop (src); +	if (gdk_pointer_is_grabbed ()) +	{ +		gdk_pointer_ungrab (event->time); +	} +	gtk_grab_remove (GTK_WIDGET (src)); + +	gedit_notebook_move_tab (src, dest, tab, dest_position); + +	/* start drag handling in dest notebook */ +	dest->priv->motion_notify_handler_id = +		g_signal_connect (G_OBJECT (dest), +				  "motion-notify-event", +				  G_CALLBACK (motion_notify_cb), +				  NULL); + +	drag_start (dest, event->time); +} + +static gboolean +button_release_cb (GeditNotebook  *notebook, +		   GdkEventButton *event, +		   gpointer        data) +{ +	if (notebook->priv->drag_in_progress) +	{ +		gint cur_page_num; +		GtkWidget *cur_page; + +		cur_page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)); +		cur_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), +						      cur_page_num); + +		/* CHECK: I don't follow the code here -- Paolo  */ +		if (!is_in_notebook_window (notebook, event->x_root, event->y_root) && +		    (gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) > 1)) +		{ +			/* Tab was detached */ +			g_signal_emit (G_OBJECT (notebook), +				       signals[TAB_DETACHED],  +				       0,  +				       cur_page); +		} + +		/* ungrab the pointer if it's grabbed */ +		if (gdk_pointer_is_grabbed ()) +		{ +			gdk_pointer_ungrab (event->time); +		} +		gtk_grab_remove (GTK_WIDGET (notebook)); +	} + +	/* This must be called even if a drag isn't happening */ +	drag_stop (notebook); + +	return FALSE; +} + +static gboolean +button_press_cb (GeditNotebook  *notebook, +		 GdkEventButton *event, +		 gpointer        data) +{ +	gint tab_clicked; + +	if (notebook->priv->drag_in_progress) +		return TRUE; + +	tab_clicked = find_tab_num_at_pos (notebook, +					   event->x_root, +					   event->y_root); +					    +	if ((event->button == 1) &&  +	    (event->type == GDK_BUTTON_PRESS) &&  +	    (tab_clicked >= 0)) +	{ +		notebook->priv->x_start = event->x_root; +		notebook->priv->y_start = event->y_root; +		 +		notebook->priv->motion_notify_handler_id = +			g_signal_connect (G_OBJECT (notebook), +					  "motion-notify-event", +					  G_CALLBACK (motion_notify_cb),  +					  NULL); +	} +	else if ((event->type == GDK_BUTTON_PRESS) &&  +		 (event->button == 3)) +	{ +		if (tab_clicked == -1) +		{ +			// CHECK: do we really need it? +			 +			/* consume event, so that we don't pop up the context menu when +			 * the mouse if not over a tab label +			 */ +			return TRUE; +		} +		else +		{ +			/* Switch to the page the mouse is over, but don't consume the event */ +			gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),  +						       tab_clicked); +		} +	} + +	return FALSE; +} + +/** + * gedit_notebook_new: + * + * Creates a new #GeditNotebook object. + * + * Returns: a new #GeditNotebook + */ +GtkWidget * +gedit_notebook_new (void) +{ +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_NOTEBOOK, NULL)); +} + +static void +gedit_notebook_switch_page_cb (GtkNotebook     *notebook, +                               GtkNotebookPage *page, +                               guint            page_num, +                               gpointer         data) +{ +	GeditNotebook *nb = GEDIT_NOTEBOOK (notebook); +	GtkWidget *child; +	GeditView *view; + +	child = gtk_notebook_get_nth_page (notebook, page_num); + +	/* Remove the old page, we dont want to grow unnecessarily +	 * the list */ +	if (nb->priv->focused_pages) +	{ +		nb->priv->focused_pages = +			g_list_remove (nb->priv->focused_pages, child); +	} + +	nb->priv->focused_pages = g_list_append (nb->priv->focused_pages, +						 child); + +	/* give focus to the view */ +	view = gedit_tab_get_view (GEDIT_TAB (child)); +	gtk_widget_grab_focus (GTK_WIDGET (view)); +} + +/* + * update_tabs_visibility: Hide tabs if there is only one tab + * and the pref is not set. + */ +static void +update_tabs_visibility (GeditNotebook *nb,  +			gboolean       before_inserting) +{ +	gboolean show_tabs; +	guint num; + +	num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb)); + +	if (before_inserting) num++; + +	show_tabs = (nb->priv->always_show_tabs || num > 1); + +	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), show_tabs); +} + +static void +gedit_notebook_init (GeditNotebook *notebook) +{ +	notebook->priv = GEDIT_NOTEBOOK_GET_PRIVATE (notebook); + +	notebook->priv->close_buttons_sensitive = TRUE; +	notebook->priv->tab_drag_and_drop_enabled = TRUE; +	 +	gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE); +	gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE); +	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE); + +	notebook->priv->always_show_tabs = TRUE; + +	g_signal_connect (notebook,  +			  "button-press-event", +			  (GCallback)button_press_cb,  +			  NULL); +	g_signal_connect (notebook,  +			  "button-release-event", +			  (GCallback)button_release_cb, +			  NULL); +	gtk_widget_add_events (GTK_WIDGET (notebook),  +			       GDK_BUTTON1_MOTION_MASK); + +	g_signal_connect_after (G_OBJECT (notebook),  +				"switch_page", +                                G_CALLBACK (gedit_notebook_switch_page_cb), +                                NULL); +} + +static void +gedit_notebook_finalize (GObject *object) +{ +	GeditNotebook *notebook = GEDIT_NOTEBOOK (object); + +	g_list_free (notebook->priv->focused_pages); + +	G_OBJECT_CLASS (gedit_notebook_parent_class)->finalize (object); +} + +/* + * We need to override this because when we don't show the tabs, like in + * fullscreen we need to have wrap around too + */ +static gboolean +gedit_notebook_change_current_page (GtkNotebook *notebook, +				    gint         offset) +{ +	gboolean wrap_around; +	gint current; +	 +	current = gtk_notebook_get_current_page (notebook); + +	if (current != -1) +	{ +		current = current + offset; +		 +		g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)), +			      "gtk-keynav-wrap-around", &wrap_around, +			      NULL); +		 +		if (wrap_around) +		{ +			if (current < 0) +			{ +				current = gtk_notebook_get_n_pages (notebook) - 1; +			} +			else if (current >= gtk_notebook_get_n_pages (notebook)) +			{ +				current = 0; +			} +		} +		 +		gtk_notebook_set_current_page (notebook, current); +	} +	else +	{ +		gtk_widget_error_bell (GTK_WIDGET (notebook)); +	} + +	return TRUE; +} + +static void +close_button_clicked_cb (GeditTabLabel *tab_label, GeditNotebook *notebook) +{ +	GeditTab *tab; + +	tab = gedit_tab_label_get_tab (tab_label); +	g_signal_emit (notebook, signals[TAB_CLOSE_REQUEST], 0, tab); +} + +static GtkWidget * +create_tab_label (GeditNotebook *nb, +		  GeditTab      *tab) +{ +	GtkWidget *tab_label; + +	tab_label = gedit_tab_label_new (tab); + +	g_signal_connect (tab_label, +			  "close-clicked", +			  G_CALLBACK (close_button_clicked_cb), +			  nb); + +	g_object_set_data (G_OBJECT (tab), "tab-label", tab_label); + +	return tab_label; +} + +static void +remove_tab_label (GeditNotebook *nb, +		  GeditTab      *tab) +{ +	GtkWidget *tab_label; + +	tab_label = gedit_tab_label_new (tab); + +	g_signal_handlers_disconnect_by_func (tab_label, +					      G_CALLBACK (close_button_clicked_cb), +					      nb); + +	g_object_set_data (G_OBJECT (tab), "tab-label", NULL); +} + +static GtkWidget * +get_tab_label (GeditTab *tab) +{ +	GtkWidget *tab_label; + +	tab_label = GTK_WIDGET (g_object_get_data (G_OBJECT (tab), "tab-label")); +	g_return_val_if_fail (tab_label != NULL, NULL); + +	return tab_label; +} + +/** + * gedit_notebook_set_always_show_tabs: + * @nb: a #GeditNotebook + * @show_tabs: %TRUE to always show the tabs + * + * Sets the visibility of the tabs in the @nb. + */ +void +gedit_notebook_set_always_show_tabs (GeditNotebook *nb,  +				     gboolean       show_tabs) +{ +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); + +	nb->priv->always_show_tabs = (show_tabs != FALSE); + +	update_tabs_visibility (nb, FALSE); +} + +/** + * gedit_notebook_add_tab: + * @nb: a #GeditNotebook + * @tab: a #GeditTab + * @position: the position where the @tab should be added + * @jump_to: %TRUE to set the @tab as active + * + * Adds the specified @tab to the @nb. + */ +void +gedit_notebook_add_tab (GeditNotebook *nb, +		        GeditTab      *tab, +		        gint           position, +		        gboolean       jump_to) +{ +	GtkWidget *tab_label; + +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	tab_label = create_tab_label (nb, tab); +	gtk_notebook_insert_page (GTK_NOTEBOOK (nb),  +				  GTK_WIDGET (tab), +				  tab_label, +				  position); +	update_tabs_visibility (nb, TRUE); + +	g_signal_emit (G_OBJECT (nb), signals[TAB_ADDED], 0, tab); + +	/* The signal handler may have reordered the tabs */ +	position = gtk_notebook_page_num (GTK_NOTEBOOK (nb),  +					  GTK_WIDGET (tab)); + +	if (jump_to) +	{ +		GeditView *view; +		 +		gtk_notebook_set_current_page (GTK_NOTEBOOK (nb), position); +		g_object_set_data (G_OBJECT (tab),  +				   "jump_to", +				   GINT_TO_POINTER (jump_to)); +		view = gedit_tab_get_view (tab); +		 +		gtk_widget_grab_focus (GTK_WIDGET (view)); +	} +} + +static void +smart_tab_switching_on_closure (GeditNotebook *nb, +				GeditTab      *tab) +{ +	gboolean jump_to; + +	jump_to = GPOINTER_TO_INT (g_object_get_data +				   (G_OBJECT (tab), "jump_to")); + +	if (!jump_to || !nb->priv->focused_pages) +	{ +		gtk_notebook_next_page (GTK_NOTEBOOK (nb)); +	} +	else +	{ +		GList *l; +		GtkWidget *child; +		int page_num; + +		/* activate the last focused tab */ +		l = g_list_last (nb->priv->focused_pages); +		child = GTK_WIDGET (l->data); +		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (nb), +						  child); +		gtk_notebook_set_current_page (GTK_NOTEBOOK (nb),  +					       page_num); +	} +} + +static void +remove_tab (GeditTab      *tab, +	    GeditNotebook *nb) +{ +	gint position; + +	position = gtk_notebook_page_num (GTK_NOTEBOOK (nb), GTK_WIDGET (tab)); + +	/* we ref the tab so that it's still alive while the tabs_removed +	 * signal is processed. +	 */ +	g_object_ref (tab); + +	remove_tab_label (nb, tab); +	gtk_notebook_remove_page (GTK_NOTEBOOK (nb), position); +	update_tabs_visibility (nb, FALSE); + +	g_signal_emit (G_OBJECT (nb), signals[TAB_REMOVED], 0, tab); + +	g_object_unref (tab); +} + +/** + * gedit_notebook_remove_tab: + * @nb: a #GeditNotebook + * @tab: a #GeditTab + * + * Removes @tab from @nb. + */ +void +gedit_notebook_remove_tab (GeditNotebook *nb, +			   GeditTab      *tab) +{ +	gint position, curr; + +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	/* Remove the page from the focused pages list */ +	nb->priv->focused_pages =  g_list_remove (nb->priv->focused_pages, +						  tab); + +	position = gtk_notebook_page_num (GTK_NOTEBOOK (nb), GTK_WIDGET (tab)); +	curr = gtk_notebook_get_current_page (GTK_NOTEBOOK (nb)); + +	if (position == curr) +	{ +		smart_tab_switching_on_closure (nb, tab); +	} + +	remove_tab (tab, nb); +} + +/** + * gedit_notebook_remove_all_tabs: + * @nb: a #GeditNotebook + * + * Removes all #GeditTab from @nb. + */ +void +gedit_notebook_remove_all_tabs (GeditNotebook *nb) +{	 +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); +	 +	g_list_free (nb->priv->focused_pages); +	nb->priv->focused_pages = NULL; + +	gtk_container_foreach (GTK_CONTAINER (nb), +			       (GtkCallback)remove_tab, +			       nb); +} + +static void +set_close_buttons_sensitivity (GeditTab      *tab, +                               GeditNotebook *nb) +{ +	GtkWidget *tab_label; + +	tab_label = get_tab_label (tab); + +	gedit_tab_label_set_close_button_sensitive (GEDIT_TAB_LABEL (tab_label), +						    nb->priv->close_buttons_sensitive); +} + +/** + * gedit_notebook_set_close_buttons_sensitive: + * @nb: a #GeditNotebook + * @sensitive: %TRUE to make the buttons sensitive + * + * Sets whether the close buttons in the tabs of @nb are sensitive. + */ +void +gedit_notebook_set_close_buttons_sensitive (GeditNotebook *nb, +					    gboolean       sensitive) +{ +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); + +	sensitive = (sensitive != FALSE); + +	if (sensitive == nb->priv->close_buttons_sensitive) +		return; + +	nb->priv->close_buttons_sensitive = sensitive; + +	gtk_container_foreach (GTK_CONTAINER (nb), +			       (GtkCallback)set_close_buttons_sensitivity, +			       nb); +} + +/** + * gedit_notebook_get_close_buttons_sensitive: + * @nb: a #GeditNotebook + * + * Whether the close buttons are sensitive. + * + * Returns: %TRUE if the close buttons are sensitive + */ +gboolean +gedit_notebook_get_close_buttons_sensitive (GeditNotebook *nb) +{ +	g_return_val_if_fail (GEDIT_IS_NOTEBOOK (nb), TRUE); + +	return nb->priv->close_buttons_sensitive; +} + +/** + * gedit_notebook_set_tab_drag_and_drop_enabled: + * @nb: a #GeditNotebook + * @enable: %TRUE to enable the drag and drop + * + * Sets whether drag and drop of tabs in the @nb is enabled. + */ +void +gedit_notebook_set_tab_drag_and_drop_enabled (GeditNotebook *nb, +					      gboolean       enable) +{ +	g_return_if_fail (GEDIT_IS_NOTEBOOK (nb)); +	 +	enable = (enable != FALSE); +	 +	if (enable == nb->priv->tab_drag_and_drop_enabled) +		return; +		 +	nb->priv->tab_drag_and_drop_enabled = enable;		 +} + +/** + * gedit_notebook_get_tab_drag_and_drop_enabled: + * @nb: a #GeditNotebook + * + * Whether the drag and drop is enabled in the @nb. + * + * Returns: %TRUE if the drag and drop is enabled. + */ +gboolean	 +gedit_notebook_get_tab_drag_and_drop_enabled (GeditNotebook *nb) +{ +	g_return_val_if_fail (GEDIT_IS_NOTEBOOK (nb), TRUE); +	 +	return nb->priv->tab_drag_and_drop_enabled; +} + diff --git a/gedit/gedit-notebook.h b/gedit/gedit-notebook.h new file mode 100755 index 00000000..ada7c274 --- /dev/null +++ b/gedit/gedit-notebook.h @@ -0,0 +1,143 @@ +/* + * gedit-notebook.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +/* This file is a modified version of the epiphany file ephy-notebook.h + * Here the relevant copyright: + * + *  Copyright (C) 2002 Christophe Fergeau + *  Copyright (C) 2003 Marco Pesenti Gritti + *  Copyright (C) 2003, 2004 Christian Persch + * + */ +  +#ifndef GEDIT_NOTEBOOK_H +#define GEDIT_NOTEBOOK_H + +#include <gedit/gedit-tab.h> + +#include <glib.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_NOTEBOOK		(gedit_notebook_get_type ()) +#define GEDIT_NOTEBOOK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_NOTEBOOK, GeditNotebook)) +#define GEDIT_NOTEBOOK_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_NOTEBOOK, GeditNotebookClass)) +#define GEDIT_IS_NOTEBOOK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_NOTEBOOK)) +#define GEDIT_IS_NOTEBOOK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_NOTEBOOK)) +#define GEDIT_NOTEBOOK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_NOTEBOOK, GeditNotebookClass)) + +/* Private structure type */ +typedef struct _GeditNotebookPrivate	GeditNotebookPrivate; + +/* + * Main object structure + */ +typedef struct _GeditNotebook		GeditNotebook; +  +struct _GeditNotebook +{ +	GtkNotebook notebook; + +	/*< private >*/ +        GeditNotebookPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditNotebookClass	GeditNotebookClass; + +struct _GeditNotebookClass +{ +        GtkNotebookClass parent_class; + +	/* Signals */ +	void	 (* tab_added)      (GeditNotebook *notebook, +				     GeditTab      *tab); +	void	 (* tab_removed)    (GeditNotebook *notebook, +				     GeditTab      *tab); +	void	 (* tab_detached)   (GeditNotebook *notebook, +				     GeditTab      *tab); +	void	 (* tabs_reordered) (GeditNotebook *notebook); +	void	 (* tab_close_request) +				    (GeditNotebook *notebook, +				     GeditTab      *tab); +}; + +/* + * Public methods + */ +GType		gedit_notebook_get_type		(void) G_GNUC_CONST; + +GtkWidget      *gedit_notebook_new		(void); + +void		gedit_notebook_add_tab		(GeditNotebook *nb, +						 GeditTab      *tab, +						 gint           position, +						 gboolean       jump_to); + +void		gedit_notebook_remove_tab	(GeditNotebook *nb, +						 GeditTab      *tab); + +void		gedit_notebook_remove_all_tabs 	(GeditNotebook *nb); + +void		gedit_notebook_reorder_tab	(GeditNotebook *src, +			    			 GeditTab      *tab, +			    			 gint           dest_position); +			    			  +void            gedit_notebook_move_tab		(GeditNotebook *src, +						 GeditNotebook *dest, +						 GeditTab      *tab, +						 gint           dest_position); + +/* FIXME: do we really need this function ? */ +void		gedit_notebook_set_always_show_tabs	 +						(GeditNotebook *nb, +						 gboolean       show_tabs); + +void		gedit_notebook_set_close_buttons_sensitive +						(GeditNotebook *nb, +						 gboolean       sensitive); + +gboolean	gedit_notebook_get_close_buttons_sensitive +						(GeditNotebook *nb); + +void		gedit_notebook_set_tab_drag_and_drop_enabled +						(GeditNotebook *nb, +						 gboolean       enable); + +gboolean	gedit_notebook_get_tab_drag_and_drop_enabled +						(GeditNotebook *nb); + +G_END_DECLS + +#endif /* GEDIT_NOTEBOOK_H */ diff --git a/gedit/gedit-object-module.c b/gedit/gedit-object-module.c new file mode 100755 index 00000000..3c6f09c1 --- /dev/null +++ b/gedit/gedit-object-module.c @@ -0,0 +1,343 @@ +/* + * gedit-object-module.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* This is a modified version of ephy-module.c from Epiphany source code. + * Here the original copyright assignment: + * + *  Copyright (C) 2003 Marco Pesenti Gritti + *  Copyright (C) 2003, 2004 Christian Persch + * + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id: gedit-module.c 6314 2008-06-05 12:57:53Z pborelli $ + */ + +#include "config.h" + +#include "gedit-object-module.h" +#include "gedit-debug.h" + +typedef GType (*GeditObjectModuleRegisterFunc) (GTypeModule *); + +enum { +	PROP_0, +	PROP_MODULE_NAME, +	PROP_PATH, +	PROP_TYPE_REGISTRATION, +	PROP_RESIDENT +}; + +struct _GeditObjectModulePrivate +{ +	GModule *library; + +	GType type; +	gchar *path; +	gchar *module_name; +	gchar *type_registration; + +	gboolean resident; +}; + +G_DEFINE_TYPE (GeditObjectModule, gedit_object_module, G_TYPE_TYPE_MODULE); + +static gboolean +gedit_object_module_load (GTypeModule *gmodule) +{ +	GeditObjectModule *module = GEDIT_OBJECT_MODULE (gmodule); +	GeditObjectModuleRegisterFunc register_func; +	gchar *path; + +	gedit_debug_message (DEBUG_PLUGINS, "Loading %s module from %s", +			     module->priv->module_name, module->priv->path); + +	path = g_module_build_path (module->priv->path, module->priv->module_name); +	g_return_val_if_fail (path != NULL, FALSE); +	gedit_debug_message (DEBUG_PLUGINS, "Module filename: %s", path); + +	module->priv->library = g_module_open (path,  +					       G_MODULE_BIND_LAZY); +	g_free (path); + +	if (module->priv->library == NULL) +	{ +		g_warning ("%s: %s", module->priv->module_name, g_module_error()); + +		return FALSE; +	} + +	/* extract symbols from the lib */ +	if (!g_module_symbol (module->priv->library, module->priv->type_registration, +			      (void *) ®ister_func)) +	{ +		g_warning ("%s: %s", module->priv->module_name, g_module_error()); +		g_module_close (module->priv->library); + +		return FALSE; +	} + +	/* symbol can still be NULL even though g_module_symbol +	 * returned TRUE */ +	if (register_func == NULL) +	{ +		g_warning ("Symbol '%s' should not be NULL", module->priv->type_registration); +		g_module_close (module->priv->library); + +		return FALSE; +	} + +	module->priv->type = register_func (gmodule); + +	if (module->priv->type == 0) +	{ +		g_warning ("Invalid object contained by module %s", module->priv->module_name); +		return FALSE; +	} + +	if (module->priv->resident) +	{ +		g_module_make_resident (module->priv->library); +	} + +	return TRUE; +} + +static void +gedit_object_module_unload (GTypeModule *gmodule) +{ +	GeditObjectModule *module = GEDIT_OBJECT_MODULE (gmodule); + +	gedit_debug_message (DEBUG_PLUGINS, "Unloading %s", module->priv->path); + +	g_module_close (module->priv->library); + +	module->priv->library = NULL; +	module->priv->type = 0; +} + +static void +gedit_object_module_init (GeditObjectModule *module) +{ +	gedit_debug_message (DEBUG_PLUGINS, "GeditObjectModule %p initialising", module); +	 +	module->priv = G_TYPE_INSTANCE_GET_PRIVATE (module, +						    GEDIT_TYPE_OBJECT_MODULE, +						    GeditObjectModulePrivate); +} + +static void +gedit_object_module_finalize (GObject *object) +{ +	GeditObjectModule *module = GEDIT_OBJECT_MODULE (object); + +	gedit_debug_message (DEBUG_PLUGINS, "GeditObjectModule %p finalising", module); + +	g_free (module->priv->path); +	g_free (module->priv->module_name); +	g_free (module->priv->type_registration); + +	G_OBJECT_CLASS (gedit_object_module_parent_class)->finalize (object); +} + +static void +gedit_object_module_get_property (GObject    *object, +			          guint       prop_id, +			          GValue     *value, +			          GParamSpec *pspec) +{ +	GeditObjectModule *module = GEDIT_OBJECT_MODULE (object); + +	switch (prop_id) +	{ +		case PROP_MODULE_NAME: +			g_value_set_string (value, module->priv->module_name); +			break; +		case PROP_PATH: +			g_value_set_string (value, module->priv->path); +			break; +		case PROP_TYPE_REGISTRATION: +			g_value_set_string (value, module->priv->type_registration); +			break; +		case PROP_RESIDENT: +			g_value_set_boolean (value, module->priv->resident); +			break; +		default: +			g_return_if_reached (); +	} +} + +static void +gedit_object_module_set_property (GObject      *object, +				  guint         prop_id, +				  const GValue *value, +				  GParamSpec   *pspec) +{ +	GeditObjectModule *module = GEDIT_OBJECT_MODULE (object); + +	switch (prop_id) +	{ +		case PROP_MODULE_NAME: +			module->priv->module_name = g_value_dup_string (value); +			g_type_module_set_name (G_TYPE_MODULE (object), +						module->priv->module_name); +			break; +		case PROP_PATH: +			module->priv->path = g_value_dup_string (value); +			break; +		case PROP_TYPE_REGISTRATION: +			module->priv->type_registration = g_value_dup_string (value); +			break; +		case PROP_RESIDENT: +			module->priv->resident = g_value_get_boolean (value); +			break; +		default: +			g_return_if_reached (); +	} +} + +static void +gedit_object_module_class_init (GeditObjectModuleClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass); + +	object_class->set_property = gedit_object_module_set_property; +	object_class->get_property = gedit_object_module_get_property; +	object_class->finalize = gedit_object_module_finalize; + +	module_class->load = gedit_object_module_load; +	module_class->unload = gedit_object_module_unload; + +	g_object_class_install_property (object_class, +					 PROP_MODULE_NAME, +					 g_param_spec_string ("module-name", +							      "Module Name", +							      "The module to load for this object", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	g_object_class_install_property (object_class, +					 PROP_PATH, +					 g_param_spec_string ("path", +							      "Path", +							      "The path to use when loading this module", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); +							       +	g_object_class_install_property (object_class, +					 PROP_TYPE_REGISTRATION, +					 g_param_spec_string ("type-registration", +							      "Type Registration", +							      "The name of the type registration function", +							      NULL, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	g_object_class_install_property (object_class, +					 PROP_RESIDENT, +					 g_param_spec_boolean ("resident", +							       "Resident", +							       "Whether the module is resident", +							       FALSE, +							       G_PARAM_READWRITE | +							       G_PARAM_CONSTRUCT_ONLY)); + +	g_type_class_add_private (klass, sizeof (GeditObjectModulePrivate)); +} + +GeditObjectModule * +gedit_object_module_new (const gchar *module_name, +			 const gchar *path, +			 const gchar *type_registration, +			 gboolean     resident) +{ +	return (GeditObjectModule *)g_object_new (GEDIT_TYPE_OBJECT_MODULE, +						  "module-name", +						  module_name, +						  "path", +						  path, +						  "type-registration", +						  type_registration, +						  "resident", +						  resident, +						  NULL); +} + +GObject * +gedit_object_module_new_object (GeditObjectModule *module, +				const gchar       *first_property_name, +				...) +{ +	va_list var_args; +	GObject *result; +	 +	g_return_val_if_fail (module->priv->type != 0, NULL); + +	gedit_debug_message (DEBUG_PLUGINS, "Creating object of type %s", +			     g_type_name (module->priv->type)); + +	va_start (var_args, first_property_name); +	result = g_object_new_valist (module->priv->type, first_property_name, var_args); +	va_end (var_args); +	 +	return result; +} + +const gchar * +gedit_object_module_get_path (GeditObjectModule *module) +{ +	g_return_val_if_fail (GEDIT_IS_OBJECT_MODULE (module), NULL); + +	return module->priv->path; +} + +const gchar * +gedit_object_module_get_module_name (GeditObjectModule *module) +{ +	g_return_val_if_fail (GEDIT_IS_OBJECT_MODULE (module), NULL); + +	return module->priv->module_name; +} + +const gchar * +gedit_object_module_get_type_registration (GeditObjectModule *module) +{ +	g_return_val_if_fail (GEDIT_IS_OBJECT_MODULE (module), NULL); + +	return module->priv->type_registration; +} + +GType +gedit_object_module_get_object_type (GeditObjectModule *module) +{ +	g_return_val_if_fail (GEDIT_IS_OBJECT_MODULE (module), 0); +	 +	return module->priv->type; +} diff --git a/gedit/gedit-object-module.h b/gedit/gedit-object-module.h new file mode 100755 index 00000000..06210794 --- /dev/null +++ b/gedit/gedit-object-module.h @@ -0,0 +1,94 @@ +/* + * gedit-object-module.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* This is a modified version of gedit-module.h from Epiphany source code. + * Here the original copyright assignment: + * + *  Copyright (C) 2003 Marco Pesenti Gritti + *  Copyright (C) 2003, 2004 Christian Persch + * + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id: gedit-module.h 6263 2008-05-05 10:52:10Z sfre $ + */ +  +#ifndef __GEDIT_OBJECT_MODULE_H__ +#define __GEDIT_OBJECT_MODULE_H__ + +#include <glib-object.h> +#include <gmodule.h> +#include <stdarg.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_OBJECT_MODULE		(gedit_object_module_get_type ()) +#define GEDIT_OBJECT_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_OBJECT_MODULE, GeditObjectModule)) +#define GEDIT_OBJECT_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_OBJECT_MODULE, GeditObjectModuleClass)) +#define GEDIT_IS_OBJECT_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_OBJECT_MODULE)) +#define GEDIT_IS_OBJECT_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_OBJECT_MODULE)) +#define GEDIT_OBJECT_MODULE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_OBJECT_MODULE, GeditObjectModuleClass)) + +typedef struct _GeditObjectModule 		GeditObjectModule; +typedef struct _GeditObjectModulePrivate	GeditObjectModulePrivate; + +struct _GeditObjectModule +{ +	GTypeModule parent; + +	GeditObjectModulePrivate *priv; +}; + +typedef struct _GeditObjectModuleClass GeditObjectModuleClass; + +struct _GeditObjectModuleClass +{ +	GTypeModuleClass parent_class; + +	/* Virtual class methods */ +	void		 (* garbage_collect)	(); +}; + +GType		 gedit_object_module_get_type			(void) G_GNUC_CONST; + +GeditObjectModule *gedit_object_module_new			(const gchar *module_name, +								 const gchar *path, +								 const gchar *type_registration, +								 gboolean     resident); + +GObject		*gedit_object_module_new_object			(GeditObjectModule *module,  +								 const gchar	   *first_property_name, +								 ...); + +GType		 gedit_object_module_get_object_type		(GeditObjectModule *module); +const gchar	*gedit_object_module_get_path			(GeditObjectModule *module); +const gchar	*gedit_object_module_get_module_name		(GeditObjectModule *module); +const gchar 	*gedit_object_module_get_type_registration 	(GeditObjectModule *module); + +G_END_DECLS + +#endif diff --git a/gedit/gedit-panel.c b/gedit/gedit-panel.c new file mode 100755 index 00000000..fb1f9672 --- /dev/null +++ b/gedit/gedit-panel.c @@ -0,0 +1,950 @@ +/* + * gedit-panel.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#include "gedit-panel.h" + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include "gedit-close-button.h" +#include "gedit-window.h" +#include "gedit-debug.h" + +#define PANEL_ITEM_KEY "GeditPanelItemKey" + +#define GEDIT_PANEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_PANEL, GeditPanelPrivate)) + +struct _GeditPanelPrivate  +{ +	GtkOrientation orientation; +	 +	/* Title bar (vertical panel only) */ +	GtkWidget *title_image; +	GtkWidget *title_label; + +	/* Notebook */ +	GtkWidget *notebook; +}; + +typedef struct _GeditPanelItem GeditPanelItem; + +struct _GeditPanelItem  +{ +	gchar *name; +	GtkWidget *icon; +}; + +/* Properties */ +enum { +	PROP_0, +	PROP_ORIENTATION +}; + +/* Signals */ +enum { +	ITEM_ADDED, +	ITEM_REMOVED, +	CLOSE, +	FOCUS_DOCUMENT, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static GObject	*gedit_panel_constructor	(GType type, +						 guint n_construct_properties, +						 GObjectConstructParam *construct_properties); + + +G_DEFINE_TYPE(GeditPanel, gedit_panel, GTK_TYPE_VBOX) + +static void +gedit_panel_finalize (GObject *obj) +{ +	if (G_OBJECT_CLASS (gedit_panel_parent_class)->finalize) +		(*G_OBJECT_CLASS (gedit_panel_parent_class)->finalize) (obj); +} + +static void +gedit_panel_get_property (GObject    *object, +			  guint       prop_id, +			  GValue     *value, +			  GParamSpec *pspec) +{ +	GeditPanel *panel = GEDIT_PANEL (object); +	 +	switch (prop_id) +	{ +		case PROP_ORIENTATION: +			g_value_set_enum(value, panel->priv->orientation); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_panel_set_property (GObject      *object, +			  guint         prop_id, +			  const GValue *value, +			  GParamSpec   *pspec) +{ +	GeditPanel *panel = GEDIT_PANEL (object); + +	switch (prop_id) +	{ +		case PROP_ORIENTATION: +			panel->priv->orientation = g_value_get_enum (value); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_panel_close (GeditPanel *panel) +{ +	gtk_widget_hide (GTK_WIDGET (panel)); +} + +static void +gedit_panel_focus_document (GeditPanel *panel) +{ +	GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (panel)); +#if !GTK_CHECK_VERSION (2, 18, 0) +	if (GTK_WIDGET_TOPLEVEL (toplevel) && GEDIT_IS_WINDOW (toplevel)) +#else +	if (gtk_widget_is_toplevel (toplevel) && GEDIT_IS_WINDOW (toplevel)) +#endif +	{ +		GeditView *view; + +		view = gedit_window_get_active_view (GEDIT_WINDOW (toplevel)); +		if (view != NULL) +			gtk_widget_grab_focus (GTK_WIDGET (view)); +	} +} + +static void +gedit_panel_grab_focus (GtkWidget *w) +{ +	gint n; +	GtkWidget *tab; +	GeditPanel *panel = GEDIT_PANEL (w); + +	n = gtk_notebook_get_current_page (GTK_NOTEBOOK (panel->priv->notebook)); +	if (n == -1) +		return; + +	tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (panel->priv->notebook), +					 n); +	g_return_if_fail (tab != NULL); + +	gtk_widget_grab_focus (tab); +} + +static void +gedit_panel_class_init (GeditPanelClass *klass) +{ +	GtkBindingSet *binding_set; +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + +	g_type_class_add_private (klass, sizeof (GeditPanelPrivate)); + +	object_class->constructor = gedit_panel_constructor; +	object_class->finalize = gedit_panel_finalize; +	object_class->get_property = gedit_panel_get_property; +	object_class->set_property = gedit_panel_set_property; + +	g_object_class_install_property (object_class, +					 PROP_ORIENTATION, +					 g_param_spec_enum ("orientation", +							    "Orientation", +							    "The panel's orientation", +							    GTK_TYPE_ORIENTATION, +							    GTK_ORIENTATION_VERTICAL, +							    G_PARAM_WRITABLE | +							    G_PARAM_READABLE | +							    G_PARAM_CONSTRUCT_ONLY | +							    G_PARAM_STATIC_STRINGS)); + +	widget_class->grab_focus = gedit_panel_grab_focus; + +	klass->close = gedit_panel_close; +	klass->focus_document = gedit_panel_focus_document; + +	signals[ITEM_ADDED] = +		g_signal_new ("item_added", +			      G_OBJECT_CLASS_TYPE (klass), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditPanelClass, item_added), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GTK_TYPE_WIDGET); +	signals[ITEM_REMOVED] = +		g_signal_new ("item_removed", +			      G_OBJECT_CLASS_TYPE (klass), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditPanelClass, item_removed), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GTK_TYPE_WIDGET); + +	/* Keybinding signals */ +	signals[CLOSE] = +		g_signal_new ("close", +			      G_OBJECT_CLASS_TYPE (klass), +			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +			      G_STRUCT_OFFSET (GeditPanelClass, close), +		  	      NULL, NULL, +		  	      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, 0); +	signals[FOCUS_DOCUMENT] = +		g_signal_new ("focus_document", +			      G_OBJECT_CLASS_TYPE (klass), +			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +			      G_STRUCT_OFFSET (GeditPanelClass, focus_document), +		  	      NULL, NULL, +		  	      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, 0);					 +	binding_set = gtk_binding_set_by_class (klass); + +	gtk_binding_entry_add_signal (binding_set,  +				      GDK_Escape,  +				      0,  +				      "close",  +				      0); +	gtk_binding_entry_add_signal (binding_set,  +				      GDK_Return,  +				      GDK_CONTROL_MASK,  +				      "focus_document",  +				      0); +} + +/* This is ugly, since it supports only known + * storage types of GtkImage, otherwise fall back + * to the empty icon. + * See http://bugzilla.mate.org/show_bug.cgi?id=317520. + */ +static void +set_gtk_image_from_gtk_image (GtkImage *image, +			      GtkImage *source) +{ +	switch (gtk_image_get_storage_type (source)) +	{ +	case GTK_IMAGE_EMPTY: +		gtk_image_clear (image); +		break; +	case GTK_IMAGE_PIXMAP: +		{ +			GdkPixmap *pm; +			GdkBitmap *bm; + +			gtk_image_get_pixmap (source, &pm, &bm); +			gtk_image_set_from_pixmap (image, pm, bm); +		} +		break; +	case GTK_IMAGE_IMAGE: +		{ +			GdkImage *i; +			GdkBitmap *bm; + +			gtk_image_get_image (source, &i, &bm); +			gtk_image_set_from_image (image, i, bm); +		} +		break; +	case GTK_IMAGE_PIXBUF: +		{ +			GdkPixbuf *pb; + +			pb = gtk_image_get_pixbuf (source); +			gtk_image_set_from_pixbuf (image, pb); +		} +		break; +	case GTK_IMAGE_STOCK: +		{ +			gchar *s_id; +			GtkIconSize s; + +			gtk_image_get_stock (source, &s_id, &s); +			gtk_image_set_from_stock (image, s_id, s); +		} +		break; +	case GTK_IMAGE_ICON_SET: +		{ +			GtkIconSet *is; +			GtkIconSize s; + +			gtk_image_get_icon_set (source, &is, &s); +			gtk_image_set_from_icon_set (image, is, s); +		} +		break; +	case GTK_IMAGE_ANIMATION: +		{ +			GdkPixbufAnimation *a; + +			a = gtk_image_get_animation (source); +			gtk_image_set_from_animation (image, a); +		} +		break; +	case GTK_IMAGE_ICON_NAME: +		{ +			const gchar *n; +			GtkIconSize s; + +			gtk_image_get_icon_name (source, &n, &s); +			gtk_image_set_from_icon_name (image, n, s); +		} +		break; +	default: +		gtk_image_set_from_stock (image, +					  GTK_STOCK_FILE, +					  GTK_ICON_SIZE_MENU); +	} +} + +static void +sync_title (GeditPanel     *panel, +	    GeditPanelItem *item) +{ +	if (panel->priv->orientation != GTK_ORIENTATION_VERTICAL) +		return; + +	if (item != NULL) +	{ +		gtk_label_set_text (GTK_LABEL (panel->priv->title_label),  +				    item->name); + +		set_gtk_image_from_gtk_image (GTK_IMAGE (panel->priv->title_image), +					      GTK_IMAGE (item->icon)); +	} +	else +	{ +		gtk_label_set_text (GTK_LABEL (panel->priv->title_label),  +				    _("Empty")); + +		gtk_image_set_from_stock (GTK_IMAGE (panel->priv->title_image), +					  GTK_STOCK_FILE, +					  GTK_ICON_SIZE_MENU); +	} +} + +static void +notebook_page_changed (GtkNotebook     *notebook, +                       GtkNotebookPage *page, +                       guint            page_num, +                       GeditPanel      *panel) +{ +	GtkWidget *item; +	GeditPanelItem *data; + +	item = gtk_notebook_get_nth_page (notebook, page_num); +	g_return_if_fail (item != NULL); + +	data = (GeditPanelItem *)g_object_get_data (G_OBJECT (item), +						    PANEL_ITEM_KEY); +	g_return_if_fail (data != NULL); + +	sync_title (panel, data); +} + +static void +panel_show (GeditPanel *panel, +	    gpointer    user_data) +{ +	gint page; +	GtkNotebook *nb; + +	nb = GTK_NOTEBOOK (panel->priv->notebook); + +	page = gtk_notebook_get_current_page (nb); + +	if (page != -1) +		notebook_page_changed (nb, NULL, page, panel); +} + +static void +gedit_panel_init (GeditPanel *panel) +{ +	panel->priv = GEDIT_PANEL_GET_PRIVATE (panel); +} + +static void +close_button_clicked_cb (GtkWidget *widget, +			 GtkWidget *panel) +{ +	gtk_widget_hide (panel); +} + +static GtkWidget * +create_close_button (GeditPanel *panel) +{ +	GtkWidget *button; + +	button = gedit_close_button_new (); + +	gtk_widget_set_tooltip_text (button, _("Hide panel")); + +	g_signal_connect (button, +			  "clicked", +			  G_CALLBACK (close_button_clicked_cb), +			  panel); + +	return button; +} + +static void +build_notebook_for_panel (GeditPanel *panel) +{ +	/* Create the panel notebook */ +	panel->priv->notebook = gtk_notebook_new (); + +	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (panel->priv->notebook), +				  GTK_POS_BOTTOM); +	gtk_notebook_set_scrollable (GTK_NOTEBOOK (panel->priv->notebook), +				     TRUE); +	gtk_notebook_popup_enable (GTK_NOTEBOOK (panel->priv->notebook)); + +	gtk_widget_show (GTK_WIDGET (panel->priv->notebook)); + +	g_signal_connect (panel->priv->notebook, +			  "switch-page", +			  G_CALLBACK (notebook_page_changed), +			  panel); +} + +static void +build_horizontal_panel (GeditPanel *panel) +{ +	GtkWidget *box; +	GtkWidget *sidebar; +	GtkWidget *close_button; + +	box = gtk_hbox_new(FALSE, 0); + +	gtk_box_pack_start (GTK_BOX (box),  +			    panel->priv->notebook,  +			    TRUE,  +			    TRUE,  +			    0); + +	/* Toolbar, close button and first separator */ +	sidebar = gtk_vbox_new(FALSE, 6); +	gtk_container_set_border_width (GTK_CONTAINER (sidebar), 4); + +	gtk_box_pack_start (GTK_BOX (box), +			    sidebar, +			    FALSE,  +			    FALSE,  +			    0); + +	close_button = create_close_button (panel); + +	gtk_box_pack_start (GTK_BOX (sidebar), +			    close_button, +			    FALSE,  +			    FALSE,  +			    0); + +	gtk_widget_show_all (box); + +	gtk_box_pack_start (GTK_BOX (panel), +			    box, +			    TRUE, +			    TRUE, +			    0); +} + +static void +build_vertical_panel (GeditPanel *panel) +{ +	GtkWidget *close_button; +	GtkWidget *title_hbox; +	GtkWidget *icon_name_hbox; +	GtkWidget *dummy_label; + +	/* Create title hbox */ +	title_hbox = gtk_hbox_new (FALSE, 6); +	gtk_container_set_border_width (GTK_CONTAINER (title_hbox), 5); +					 +	gtk_box_pack_start (GTK_BOX (panel), title_hbox, FALSE, FALSE, 0); +	 +	icon_name_hbox = gtk_hbox_new (FALSE, 0); +	gtk_box_pack_start (GTK_BOX (title_hbox),  +			    icon_name_hbox,  +			    TRUE,  +			    TRUE,  +			    0); +		 +	panel->priv->title_image =  +				gtk_image_new_from_stock (GTK_STOCK_FILE, +							  GTK_ICON_SIZE_MENU); +	gtk_box_pack_start (GTK_BOX (icon_name_hbox),  +			    panel->priv->title_image,  +			    FALSE,  +			    TRUE,  +			    0); + +	dummy_label = gtk_label_new (" "); + +	gtk_box_pack_start (GTK_BOX (icon_name_hbox),  +			    dummy_label,  +			    FALSE,  +			    FALSE,  +			    0);	 + +	panel->priv->title_label = gtk_label_new (_("Empty")); +	gtk_misc_set_alignment (GTK_MISC (panel->priv->title_label), 0, 0.5); +	gtk_label_set_ellipsize(GTK_LABEL (panel->priv->title_label), PANGO_ELLIPSIZE_END); + +	gtk_box_pack_start (GTK_BOX (icon_name_hbox), +			    panel->priv->title_label, +			    TRUE, +			    TRUE, +			    0); + +	close_button = create_close_button (panel); + +	gtk_box_pack_start (GTK_BOX (title_hbox), +			    close_button,  +			    FALSE,  +			    FALSE,  +			    0); + +	gtk_widget_show_all (title_hbox); + +	gtk_box_pack_start (GTK_BOX (panel), +			    panel->priv->notebook, +			    TRUE, +			    TRUE, +			    0); +} + +static GObject * +gedit_panel_constructor (GType type, +			 guint n_construct_properties, +			 GObjectConstructParam *construct_properties) +{ +	 +	/* Invoke parent constructor. */ +	GeditPanelClass *klass = GEDIT_PANEL_CLASS (g_type_class_peek (GEDIT_TYPE_PANEL)); +	GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); +	GObject *obj = parent_class->constructor (type, +						  n_construct_properties, +						  construct_properties); + +	/* Build the panel, now that we know the orientation  +			   (_init has been called previously) */ +	GeditPanel *panel = GEDIT_PANEL (obj); + +	build_notebook_for_panel (panel); +  	if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL) +  		build_horizontal_panel (panel); +	else +		build_vertical_panel (panel); + +	g_signal_connect (panel, +			  "show", +			  G_CALLBACK (panel_show), +			  NULL); + +	return obj; +} + +/** + * gedit_panel_new: + * @orientation: a #GtkOrientation + * + * Creates a new #GeditPanel with the given @orientation. You shouldn't create + * a new panel use gedit_window_get_side_panel() or gedit_window_get_bottom_panel() + * instead. + * + * Returns: a new #GeditPanel object. + */ +GtkWidget * +gedit_panel_new (GtkOrientation orientation) +{ +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_PANEL, "orientation", orientation, NULL)); +} + +static GtkWidget * +build_tab_label (GeditPanel  *panel, +		 GtkWidget   *item, +		 const gchar *name, +		 GtkWidget   *icon) +{ +	GtkWidget *hbox, *label_hbox, *label_ebox; +	GtkWidget *label; + +	/* set hbox spacing and label padding (see below) so that there's an +	 * equal amount of space around the label */ +	hbox = gtk_hbox_new (FALSE, 4); + +	label_ebox = gtk_event_box_new (); +	gtk_event_box_set_visible_window (GTK_EVENT_BOX (label_ebox), FALSE); +	gtk_box_pack_start (GTK_BOX (hbox), label_ebox, TRUE, TRUE, 0); + +	label_hbox = gtk_hbox_new (FALSE, 4); +	gtk_container_add (GTK_CONTAINER (label_ebox), label_hbox); + +	/* setup icon */ +	gtk_box_pack_start (GTK_BOX (label_hbox), icon, FALSE, FALSE, 0); + +	/* setup label */ +        label = gtk_label_new (name); +	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); +        gtk_misc_set_padding (GTK_MISC (label), 0, 0); +	gtk_box_pack_start (GTK_BOX (label_hbox), label, TRUE, TRUE, 0); + +	gtk_widget_set_tooltip_text (label_ebox, name); + +	gtk_widget_show_all (hbox); + +	if (panel->priv->orientation == GTK_ORIENTATION_VERTICAL) +		gtk_widget_hide(label); + +	g_object_set_data (G_OBJECT (item), "label", label); +	g_object_set_data (G_OBJECT (item), "hbox", hbox); + +	return hbox; +} + +/** + * gedit_panel_add_item: + * @panel: a #GeditPanel + * @item: the #GtkWidget to add to the @panel + * @name: the name to be shown in the @panel + * @image: the image to be shown in the @panel + * + * Adds a new item to the @panel. + */ +void +gedit_panel_add_item (GeditPanel  *panel,  +		      GtkWidget   *item,  +		      const gchar *name, +		      GtkWidget   *image) +{ +	GeditPanelItem *data; +	GtkWidget *tab_label; +	GtkWidget *menu_label; +	gint w, h; +	 +	g_return_if_fail (GEDIT_IS_PANEL (panel)); +	g_return_if_fail (GTK_IS_WIDGET (item)); +	g_return_if_fail (name != NULL); +	g_return_if_fail (image == NULL || GTK_IS_IMAGE (image)); + +	data = g_new (GeditPanelItem, 1); + +	data->name = g_strdup (name); + +	if (image == NULL) +	{ +		/* default to empty */ +		data->icon = gtk_image_new_from_stock (GTK_STOCK_FILE, +						       GTK_ICON_SIZE_MENU); +	} +	else +	{ +		data->icon = image; +	} + +	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); +	gtk_widget_set_size_request (data->icon, w, h); +	 +	g_object_set_data (G_OBJECT (item), +		           PANEL_ITEM_KEY, +		           data); + +	tab_label = build_tab_label (panel, item, data->name, data->icon); + +	menu_label = gtk_label_new (name); +	gtk_misc_set_alignment (GTK_MISC (menu_label), 0.0, 0.5); + +	if (!GTK_WIDGET_VISIBLE (item)) +		gtk_widget_show (item); + +	gtk_notebook_append_page_menu (GTK_NOTEBOOK (panel->priv->notebook), +				       item, +				       tab_label, +				       menu_label); + +	g_signal_emit (G_OBJECT (panel), signals[ITEM_ADDED], 0, item); +} + +/** + * gedit_panel_add_item_with_stock_icon: + * @panel: a #GeditPanel + * @item: the #GtkWidget to add to the @panel + * @name: the name to be shown in the @panel + * @stock_id: a stock id + * + * Same as gedit_panel_add_item() but using an image from stock. + */ +void +gedit_panel_add_item_with_stock_icon (GeditPanel  *panel,  +				      GtkWidget   *item,  +				      const gchar *name, +				      const gchar *stock_id) +{ +	GtkWidget *icon = NULL; + +	if (stock_id != NULL) +	{ +		icon = gtk_image_new_from_stock (stock_id, +						 GTK_ICON_SIZE_MENU); +	} + +	gedit_panel_add_item (panel, item, name, icon); +} + +/** + * gedit_panel_remove_item: + * @panel: a #GeditPanel + * @item: the item to be removed from the panel + * + * Removes the widget @item from the panel if it is in the @panel and returns + * TRUE if there was not any problem. + * + * Returns: TRUE if it was well removed. + */ +gboolean +gedit_panel_remove_item (GeditPanel *panel, +			 GtkWidget  *item) +{ +	GeditPanelItem *data; +	gint page_num; +	 +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), FALSE); +	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE); + +	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook), +					  item); +					   +	if (page_num == -1) +		return FALSE; +		 +	data = (GeditPanelItem *)g_object_get_data (G_OBJECT (item), +					            PANEL_ITEM_KEY); +	g_return_val_if_fail (data != NULL, FALSE); +	 +	g_free (data->name); +	g_free (data); + +	g_object_set_data (G_OBJECT (item), +		           PANEL_ITEM_KEY, +		           NULL); + +	/* ref the item to keep it alive during signal emission */ +	g_object_ref (G_OBJECT (item)); + +	gtk_notebook_remove_page (GTK_NOTEBOOK (panel->priv->notebook),  +				  page_num); + +	/* if we removed all the pages, reset the title */ +	if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->priv->notebook)) == 0) +		sync_title (panel, NULL); + +	g_signal_emit (G_OBJECT (panel), signals[ITEM_REMOVED], 0, item); + +	g_object_unref (G_OBJECT (item)); + +	return TRUE; +} + +/** + * gedit_panel_activate_item: + * @panel: a #GeditPanel + * @item: the item to be activated + * + * Switches to the page that contains @item. + * + * Returns: TRUE if it was activated + */ +gboolean +gedit_panel_activate_item (GeditPanel *panel, +			   GtkWidget  *item) +{ +	gint page_num; + +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), FALSE); +	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE); + +	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook), +					  item); + +	if (page_num == -1) +		return FALSE; + +	gtk_notebook_set_current_page (GTK_NOTEBOOK (panel->priv->notebook), +				       page_num); + +	return TRUE; +} + +/** + * gedit_panel_item_is_active: + * @panel: a #GeditPanel + * @item: a widget contained in #GeditPanel + * + * Wheter @item is the one current active in @panel + * + * Returns: TRUE if the widget is active + */ +gboolean +gedit_panel_item_is_active (GeditPanel *panel, +			    GtkWidget  *item) +{ +	gint cur_page; +	gint page_num; + +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), FALSE); +	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE); + +	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook), +					  item); + +	if (page_num == -1) +		return FALSE; + +	cur_page = gtk_notebook_get_current_page ( +				GTK_NOTEBOOK (panel->priv->notebook)); + +	return (page_num == cur_page); +} + +/** + * gedit_panel_get_orientation: + * @panel: a #GeditPanel + * + * Gets the orientation of the @panel.  + * + * Returns: the #GtkOrientation of #GeditPanel + */ +GtkOrientation +gedit_panel_get_orientation (GeditPanel *panel) +{ +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), GTK_ORIENTATION_VERTICAL); + +	return panel->priv->orientation; +} + +/** + * gedit_panel_get_n_items: + * @panel: a #GeditPanel + * + * Gets the number of items in a @panel. + *  + * Returns: the number of items contained in #GeditPanel + */ +gint +gedit_panel_get_n_items (GeditPanel *panel) +{ +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), -1); + +	return gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->priv->notebook)); +} + +gint +_gedit_panel_get_active_item_id (GeditPanel *panel) +{ +	gint cur_page; +	GtkWidget *item; +	GeditPanelItem *data; + +	g_return_val_if_fail (GEDIT_IS_PANEL (panel), 0); + +	cur_page = gtk_notebook_get_current_page ( +				GTK_NOTEBOOK (panel->priv->notebook)); +	if (cur_page == -1) +		return 0; + +	item = gtk_notebook_get_nth_page ( +				GTK_NOTEBOOK (panel->priv->notebook), +				cur_page); + +	/* FIXME: for now we use as the hash of the name as id. +	 * However the name is not guaranteed to be unique and +	 * it is a translated string, so it's subotimal, but should +	 * be good enough for now since we don't want to add an +	 * ad hoc id argument. +	 */ + +	data = (GeditPanelItem *)g_object_get_data (G_OBJECT (item), +					            PANEL_ITEM_KEY); +	g_return_val_if_fail (data != NULL, 0); + +	return g_str_hash (data->name); +} + +void +_gedit_panel_set_active_item_by_id (GeditPanel *panel, +				    gint        id) +{ +	gint n, i; + +	g_return_if_fail (GEDIT_IS_PANEL (panel)); + +	if (id == 0) +		return; + +	n = gtk_notebook_get_n_pages ( +				GTK_NOTEBOOK (panel->priv->notebook)); + +	for (i = 0; i < n; i++) +	{ +		GtkWidget *item; +		GeditPanelItem *data; + +		item = gtk_notebook_get_nth_page ( +				GTK_NOTEBOOK (panel->priv->notebook), i); + +		data = (GeditPanelItem *)g_object_get_data (G_OBJECT (item), +						            PANEL_ITEM_KEY); +		g_return_if_fail (data != NULL); + +		if (g_str_hash (data->name) == id) +		{ +			gtk_notebook_set_current_page ( +				GTK_NOTEBOOK (panel->priv->notebook), i); + +			return; +		} +	} +} diff --git a/gedit/gedit-panel.h b/gedit/gedit-panel.h new file mode 100755 index 00000000..87b0536c --- /dev/null +++ b/gedit/gedit-panel.h @@ -0,0 +1,130 @@ +/* + * gedit-panel.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PANEL_H__ +#define __GEDIT_PANEL_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PANEL		(gedit_panel_get_type()) +#define GEDIT_PANEL(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PANEL, GeditPanel)) +#define GEDIT_PANEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PANEL, GeditPanelClass)) +#define GEDIT_IS_PANEL(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PANEL)) +#define GEDIT_IS_PANEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PANEL)) +#define GEDIT_PANEL_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PANEL, GeditPanelClass)) + +/* Private structure type */ +typedef struct _GeditPanelPrivate GeditPanelPrivate; + +/* + * Main object structure + */ +typedef struct _GeditPanel GeditPanel; + +struct _GeditPanel  +{ +	GtkVBox vbox; + +	/*< private > */ +	GeditPanelPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditPanelClass GeditPanelClass; + +struct _GeditPanelClass  +{ +	GtkVBoxClass parent_class; + +	void (* item_added)     (GeditPanel     *panel, +				 GtkWidget      *item); +	void (* item_removed)   (GeditPanel     *panel, +				 GtkWidget      *item); + +	/* Keybinding signals */ +	void (* close)          (GeditPanel     *panel); +	void (* focus_document) (GeditPanel     *panel); + +	/* Padding for future expansion */ +	void (*_gedit_reserved1) (void); +	void (*_gedit_reserved2) (void); +	void (*_gedit_reserved3) (void); +	void (*_gedit_reserved4) (void);	 +}; + +/* + * Public methods + */ +GType 		 gedit_panel_get_type 			(void) G_GNUC_CONST; + +GtkWidget 	*gedit_panel_new 			(GtkOrientation	 orientation); + +void		 gedit_panel_add_item			(GeditPanel     *panel, +						      	 GtkWidget      *item, +						      	 const gchar    *name, +							 GtkWidget      *image); + +void		 gedit_panel_add_item_with_stock_icon	(GeditPanel     *panel, +							 GtkWidget      *item, +						      	 const gchar    *name, +						      	 const gchar    *stock_id); + +gboolean	 gedit_panel_remove_item	(GeditPanel     *panel, +					  	 GtkWidget      *item); + +gboolean	 gedit_panel_activate_item 	(GeditPanel     *panel, +					    	 GtkWidget      *item); + +gboolean	 gedit_panel_item_is_active 	(GeditPanel     *panel, +					    	 GtkWidget      *item); + +GtkOrientation	 gedit_panel_get_orientation	(GeditPanel	*panel); + +gint		 gedit_panel_get_n_items	(GeditPanel	*panel); + + +/* + * Non exported functions + */ +gint		 _gedit_panel_get_active_item_id	(GeditPanel	*panel); + +void		 _gedit_panel_set_active_item_by_id	(GeditPanel	*panel, +							 gint		 id); + +G_END_DECLS + +#endif  /* __GEDIT_PANEL_H__  */ diff --git a/gedit/gedit-plugin-info-priv.h b/gedit/gedit-plugin-info-priv.h new file mode 100755 index 00000000..247b51c5 --- /dev/null +++ b/gedit/gedit-plugin-info-priv.h @@ -0,0 +1,68 @@ +/* + * gedit-plugin-info-priv.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 - Paolo Maggi  + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_PLUGIN_INFO_PRIV_H__ +#define __GEDIT_PLUGIN_INFO_PRIV_H__ + +#include "gedit-plugin-info.h" +#include "gedit-plugin.h" + +struct _GeditPluginInfo +{ +	gint               refcount; + +	GeditPlugin       *plugin; +	gchar             *file; + +	gchar             *module_name; +	gchar		  *loader; +	gchar            **dependencies; + +	gchar             *name; +	gchar             *desc; +	gchar             *icon_name; +	gchar            **authors; +	gchar             *copyright; +	gchar             *website; +	gchar             *version; + +	/* A plugin is unavailable if it is not possible to activate it +	   due to an error loading the plugin module (e.g. for Python plugins +	   when the interpreter has not been correctly initializated) */ +	gint               available : 1; +}; + +GeditPluginInfo		*_gedit_plugin_info_new		(const gchar *file); +void			 _gedit_plugin_info_ref		(GeditPluginInfo *info); +void			 _gedit_plugin_info_unref	(GeditPluginInfo *info); + + +#endif /* __GEDIT_PLUGIN_INFO_PRIV_H__ */ diff --git a/gedit/gedit-plugin-info.c b/gedit/gedit-plugin-info.c new file mode 100755 index 00000000..31411c42 --- /dev/null +++ b/gedit/gedit-plugin-info.c @@ -0,0 +1,394 @@ +/* + * gedit-plugin-info.c + * This file is part of gedit + * + * Copyright (C) 2002-2005 - Paolo Maggi  + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <glib.h> + +#include "gedit-plugin-info.h" +#include "gedit-plugin-info-priv.h" +#include "gedit-debug.h" +#include "gedit-plugin.h" + +void +_gedit_plugin_info_ref (GeditPluginInfo *info) +{ +	g_atomic_int_inc (&info->refcount); +} + +static GeditPluginInfo * +gedit_plugin_info_copy (GeditPluginInfo *info) +{ +	_gedit_plugin_info_ref (info); +	return info; +} + +void +_gedit_plugin_info_unref (GeditPluginInfo *info) +{ +	if (!g_atomic_int_dec_and_test (&info->refcount)) +		return; + +	if (info->plugin != NULL) +	{ +		gedit_debug_message (DEBUG_PLUGINS, "Unref plugin %s", info->name); + +		g_object_unref (info->plugin); +	} + +	g_free (info->file); +	g_free (info->module_name); +	g_strfreev (info->dependencies); +	g_free (info->name); +	g_free (info->desc); +	g_free (info->icon_name); +	g_free (info->website); +	g_free (info->copyright); +	g_free (info->loader); +	g_free (info->version); +	g_strfreev (info->authors); + +	g_free (info); +} + +/** + * gedit_plugin_info_get_type: + * + * Retrieves the #GType object which is associated with the #GeditPluginInfo + * class. + * + * Return value: the GType associated with #GeditPluginInfo. + **/ +GType +gedit_plugin_info_get_type (void) +{ +	static GType the_type = 0; + +	if (G_UNLIKELY (!the_type)) +		the_type = g_boxed_type_register_static ( +					"GeditPluginInfo", +					(GBoxedCopyFunc) gedit_plugin_info_copy, +					(GBoxedFreeFunc) _gedit_plugin_info_unref); + +	return the_type; +}  + +/** + * gedit_plugin_info_new: + * @filename: the filename where to read the plugin information + * + * Creates a new #GeditPluginInfo from a file on the disk. + * + * Return value: a newly created #GeditPluginInfo. + */ +GeditPluginInfo * +_gedit_plugin_info_new (const gchar *file) +{ +	GeditPluginInfo *info; +	GKeyFile *plugin_file = NULL; +	gchar *str; + +	g_return_val_if_fail (file != NULL, NULL); + +	gedit_debug_message (DEBUG_PLUGINS, "Loading plugin: %s", file); + +	info = g_new0 (GeditPluginInfo, 1); +	info->refcount = 1; +	info->file = g_strdup (file); + +	plugin_file = g_key_file_new (); +	if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL)) +	{ +		g_warning ("Bad plugin file: %s", file); +		goto error; +	} + +	if (!g_key_file_has_key (plugin_file, +			   	 "Gedit Plugin", +				 "IAge", +				 NULL)) +	{ +		gedit_debug_message (DEBUG_PLUGINS, +				     "IAge key does not exist in file: %s", file); +		goto error; +	} +	 +	/* Check IAge=2 */ +	if (g_key_file_get_integer (plugin_file, +				    "Gedit Plugin", +				    "IAge", +				    NULL) != 2) +	{ +		gedit_debug_message (DEBUG_PLUGINS, +				     "Wrong IAge in file: %s", file); +		goto error; +	} +				     +	/* Get module name */ +	str = g_key_file_get_string (plugin_file, +				     "Gedit Plugin", +				     "Module", +				     NULL); + +	if ((str != NULL) && (*str != '\0')) +	{ +		info->module_name = str; +	} +	else +	{ +		g_warning ("Could not find 'Module' in %s", file); +		goto error; +	} + +	/* Get the dependency list */ +	info->dependencies = g_key_file_get_string_list (plugin_file, +							 "Gedit Plugin", +							 "Depends", +							 NULL, +							 NULL); +	if (info->dependencies == NULL) +	{ +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Depends' in %s", file); +		info->dependencies = g_new0 (gchar *, 1); +	} + +	/* Get the loader for this plugin */ +	str = g_key_file_get_string (plugin_file, +				     "Gedit Plugin", +				     "Loader", +				     NULL); +	 +	if ((str != NULL) && (*str != '\0')) +	{ +		info->loader = str; +	} +	else +	{ +		/* default to the C loader */ +		info->loader = g_strdup("c"); +	} + +	/* Get Name */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Gedit Plugin", +					    "Name", +					    NULL, NULL); +	if (str) +		info->name = str; +	else +	{ +		g_warning ("Could not find 'Name' in %s", file); +		goto error; +	} + +	/* Get Description */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Gedit Plugin", +					    "Description", +					    NULL, NULL); +	if (str) +		info->desc = str; +	else +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Description' in %s", file); + +	/* Get Icon */ +	str = g_key_file_get_locale_string (plugin_file, +					    "Gedit Plugin", +					    "Icon", +					    NULL, NULL); +	if (str) +		info->icon_name = str; +	else +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Icon' in %s, using 'gedit-plugin'", file); +	 + +	/* Get Authors */ +	info->authors = g_key_file_get_string_list (plugin_file, +						    "Gedit Plugin", +						    "Authors", +						    NULL, +						    NULL); +	if (info->authors == NULL) +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Authors' in %s", file); + + +	/* Get Copyright */ +	str = g_key_file_get_string (plugin_file, +				     "Gedit Plugin", +				     "Copyright", +				     NULL); +	if (str) +		info->copyright = str; +	else +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Copyright' in %s", file); + +	/* Get Website */ +	str = g_key_file_get_string (plugin_file, +				     "Gedit Plugin", +				     "Website", +				     NULL); +	if (str) +		info->website = str; +	else +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Website' in %s", file); +	 +	/* Get Version */ +	str = g_key_file_get_string (plugin_file, +				     "Gedit Plugin", +				     "Version", +				     NULL); +	if (str) +		info->version = str; +	else +		gedit_debug_message (DEBUG_PLUGINS, "Could not find 'Version' in %s", file); +	 +	g_key_file_free (plugin_file); +	 +	/* If we know nothing about the availability of the plugin, +	   set it as available */ +	info->available = TRUE; +	 +	return info; + +error: +	g_free (info->file); +	g_free (info->module_name); +	g_free (info->name); +	g_free (info->loader); +	g_free (info); +	g_key_file_free (plugin_file); + +	return NULL; +} + +gboolean +gedit_plugin_info_is_active (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, FALSE); + +	return info->available && info->plugin != NULL; +} + +gboolean +gedit_plugin_info_is_available (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, FALSE); + +	return info->available != FALSE; +} + +gboolean +gedit_plugin_info_is_configurable (GeditPluginInfo *info) +{ +	gedit_debug_message (DEBUG_PLUGINS, "Is '%s' configurable?", info->name); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (info->plugin == NULL || !info->available) +		return FALSE; + +	return gedit_plugin_is_configurable (info->plugin); +} + +const gchar * +gedit_plugin_info_get_module_name (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->module_name; +} + +const gchar * +gedit_plugin_info_get_name (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->name; +} + +const gchar * +gedit_plugin_info_get_description (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->desc; +} + +const gchar * +gedit_plugin_info_get_icon_name (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	/* use the gedit-plugin icon as a default if the plugin does not +	   have its own */ +	if (info->icon_name != NULL &&  +	    gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), +				     info->icon_name)) +		return info->icon_name; +	else +		return "gedit-plugin"; +} + +const gchar ** +gedit_plugin_info_get_authors (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, (const gchar **)NULL); + +	return (const gchar **) info->authors; +} + +const gchar * +gedit_plugin_info_get_website (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->website; +} + +const gchar * +gedit_plugin_info_get_copyright (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->copyright; +} + +const gchar * +gedit_plugin_info_get_version (GeditPluginInfo *info) +{ +	g_return_val_if_fail (info != NULL, NULL); + +	return info->version; +} diff --git a/gedit/gedit-plugin-info.h b/gedit/gedit-plugin-info.h new file mode 100755 index 00000000..ac9e2fa7 --- /dev/null +++ b/gedit/gedit-plugin-info.h @@ -0,0 +1,63 @@ +/* + * gedit-plugin-info.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 - Paolo Maggi  + * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2002-2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifndef __GEDIT_PLUGIN_INFO_H__ +#define __GEDIT_PLUGIN_INFO_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_PLUGIN_INFO			(gedit_plugin_info_get_type ()) +#define GEDIT_PLUGIN_INFO(obj)			((GeditPluginInfo *) (obj)) + +typedef struct _GeditPluginInfo			GeditPluginInfo; + +GType		 gedit_plugin_info_get_type		(void) G_GNUC_CONST; + +gboolean 	 gedit_plugin_info_is_active		(GeditPluginInfo *info); +gboolean 	 gedit_plugin_info_is_available		(GeditPluginInfo *info); +gboolean	 gedit_plugin_info_is_configurable	(GeditPluginInfo *info); + +const gchar	*gedit_plugin_info_get_module_name	(GeditPluginInfo *info); + +const gchar	*gedit_plugin_info_get_name		(GeditPluginInfo *info); +const gchar	*gedit_plugin_info_get_description	(GeditPluginInfo *info); +const gchar	*gedit_plugin_info_get_icon_name	(GeditPluginInfo *info); +const gchar    **gedit_plugin_info_get_authors		(GeditPluginInfo *info); +const gchar	*gedit_plugin_info_get_website		(GeditPluginInfo *info); +const gchar	*gedit_plugin_info_get_copyright	(GeditPluginInfo *info); +const gchar	*gedit_plugin_info_get_version		(GeditPluginInfo *info); + +G_END_DECLS + +#endif /* __GEDIT_PLUGIN_INFO_H__ */ + diff --git a/gedit/gedit-plugin-loader.c b/gedit/gedit-plugin-loader.c new file mode 100755 index 00000000..2dccde49 --- /dev/null +++ b/gedit/gedit-plugin-loader.c @@ -0,0 +1,131 @@ +/* + * gedit-plugin-loader.c + * This file is part of gedit + * + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +#include "gedit-plugin-loader.h" + +static void +gedit_plugin_loader_base_init (gpointer g_class) +{ +	static gboolean initialized = FALSE; + +	if (G_UNLIKELY (!initialized)) +	{ +		/* create interface signals here. */ +		initialized = TRUE; +	} +} + +GType +gedit_plugin_loader_get_type (void) +{ +	static GType type = 0; + +	if (G_UNLIKELY (type == 0)) +	{ +		static const GTypeInfo info = +		{ +			sizeof (GeditPluginLoaderInterface), +			gedit_plugin_loader_base_init,   /* base_init */ +			NULL,   /* base_finalize */ +			NULL,   /* class_init */ +			NULL,   /* class_finalize */ +			NULL,   /* class_data */ +			0, +			0,      /* n_preallocs */ +			NULL    /* instance_init */ +		}; +		 +		type = g_type_register_static (G_TYPE_INTERFACE, "GeditPluginLoader", &info, 0); +	} + +	return type; +} + +const gchar * +gedit_plugin_loader_type_get_id (GType type) +{ +	GTypeClass *klass; +	GeditPluginLoaderInterface *iface; +	 +	klass = g_type_class_ref (type); +	 +	if (klass == NULL) +	{ +		g_warning ("Could not get class info for plugin loader"); +		return NULL; +	} + +	iface = g_type_interface_peek (klass, GEDIT_TYPE_PLUGIN_LOADER); +	 +	if (iface == NULL) +	{ +		g_warning ("Could not get plugin loader interface"); +		g_type_class_unref (klass); +		 +		return NULL; +	} +	 +	g_return_val_if_fail (iface->get_id != NULL, NULL); +	return iface->get_id (); +} + +GeditPlugin * +gedit_plugin_loader_load (GeditPluginLoader *loader, +			  GeditPluginInfo   *info, +			  const gchar       *path) +{ +	GeditPluginLoaderInterface *iface; +	 +	g_return_val_if_fail (GEDIT_IS_PLUGIN_LOADER (loader), NULL); +	 +	iface = GEDIT_PLUGIN_LOADER_GET_INTERFACE (loader); +	g_return_val_if_fail (iface->load != NULL, NULL); +	 +	return iface->load (loader, info, path); +} + +void +gedit_plugin_loader_unload (GeditPluginLoader *loader, +			    GeditPluginInfo   *info) +{ +	GeditPluginLoaderInterface *iface; +	 +	g_return_if_fail (GEDIT_IS_PLUGIN_LOADER (loader)); +	 +	iface = GEDIT_PLUGIN_LOADER_GET_INTERFACE (loader); +	g_return_if_fail (iface->unload != NULL); +	 +	iface->unload (loader, info); +} + +void +gedit_plugin_loader_garbage_collect (GeditPluginLoader *loader) +{ +	GeditPluginLoaderInterface *iface; +	 +	g_return_if_fail (GEDIT_IS_PLUGIN_LOADER (loader)); +	 +	iface = GEDIT_PLUGIN_LOADER_GET_INTERFACE (loader); +	 +	if (iface->garbage_collect != NULL) +		iface->garbage_collect (loader); +} diff --git a/gedit/gedit-plugin-loader.h b/gedit/gedit-plugin-loader.h new file mode 100755 index 00000000..a064e5ec --- /dev/null +++ b/gedit/gedit-plugin-loader.h @@ -0,0 +1,106 @@ +/* + * gedit-plugin-loader.h + * This file is part of gedit + * + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +#ifndef __GEDIT_PLUGIN_LOADER_H__ +#define __GEDIT_PLUGIN_LOADER_H__ + +#include <glib-object.h> +#include <gedit/gedit-plugin.h> +#include <gedit/gedit-plugin-info.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_PLUGIN_LOADER                (gedit_plugin_loader_get_type ()) +#define GEDIT_PLUGIN_LOADER(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_PLUGIN_LOADER, GeditPluginLoader)) +#define GEDIT_IS_PLUGIN_LOADER(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_PLUGIN_LOADER)) +#define GEDIT_PLUGIN_LOADER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GEDIT_TYPE_PLUGIN_LOADER, GeditPluginLoaderInterface)) + +typedef struct _GeditPluginLoader GeditPluginLoader; /* dummy object */ +typedef struct _GeditPluginLoaderInterface GeditPluginLoaderInterface; + +struct _GeditPluginLoaderInterface { +	GTypeInterface parent; + +	const gchar *(*get_id)		(void); + +	GeditPlugin *(*load) 		(GeditPluginLoader 	*loader, +			     		 GeditPluginInfo	*info, +			      		 const gchar       	*path); + +	void 	     (*unload)		(GeditPluginLoader 	*loader, +					 GeditPluginInfo       	*info); + +	void         (*garbage_collect) 	(GeditPluginLoader	*loader); +}; + +GType gedit_plugin_loader_get_type (void); + +const gchar *gedit_plugin_loader_type_get_id	(GType 			 type); +GeditPlugin *gedit_plugin_loader_load		(GeditPluginLoader 	*loader, +						 GeditPluginInfo 	*info, +						 const gchar		*path); +void gedit_plugin_loader_unload			(GeditPluginLoader 	*loader, +						 GeditPluginInfo	*info); +void gedit_plugin_loader_garbage_collect	(GeditPluginLoader 	*loader); + +/** + * GEDIT_PLUGIN_LOADER_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init): + * + * Utility macro used to register interfaces for gobject types in plugin loaders. + */ +#define GEDIT_PLUGIN_LOADER_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init)		\ +	const GInterfaceInfo g_implement_interface_info = 			\ +	{ 									\ +		(GInterfaceInitFunc) iface_init,				\ +		NULL, 								\ +		NULL								\ +	};									\ +										\ +	g_type_module_add_interface (type_module,				\ +				     g_define_type_id, 				\ +				     TYPE_IFACE, 				\ +				     &g_implement_interface_info); + +/** + * GEDIT_PLUGIN_LOADER_REGISTER_TYPE(PluginLoaderName, plugin_loader_name, PARENT_TYPE, loader_interface_init): + * + * Utility macro used to register plugin loaders. + */ +#define GEDIT_PLUGIN_LOADER_REGISTER_TYPE(PluginLoaderName, plugin_loader_name, PARENT_TYPE, loader_iface_init) 	\ +	G_DEFINE_DYNAMIC_TYPE_EXTENDED (PluginLoaderName,			\ +					plugin_loader_name,			\ +					PARENT_TYPE,			\ +					0,					\ +					GEDIT_PLUGIN_LOADER_IMPLEMENT_INTERFACE(GEDIT_TYPE_PLUGIN_LOADER, loader_iface_init));	\ +										\ +										\ +G_MODULE_EXPORT GType								\ +register_gedit_plugin_loader (GTypeModule *type_module)				\ +{										\ +	plugin_loader_name##_register_type (type_module);			\ +										\ +	return plugin_loader_name##_get_type();					\ +} + +G_END_DECLS + +#endif /* __GEDIT_PLUGIN_LOADER_H__ */ diff --git a/gedit/gedit-plugin-manager.c b/gedit/gedit-plugin-manager.c new file mode 100755 index 00000000..b72abad9 --- /dev/null +++ b/gedit/gedit-plugin-manager.c @@ -0,0 +1,889 @@ +/* + * gedit-plugin-manager.c + * This file is part of gedit + * + * Copyright (C) 2002 Paolo Maggi and James Willcox + * Copyright (C) 2003-2006 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 1998-2006. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> + +#include "gedit-plugin-manager.h" +#include "gedit-utils.h" +#include "gedit-plugins-engine.h" +#include "gedit-plugin.h" +#include "gedit-debug.h" + +enum +{ +	ACTIVE_COLUMN, +	AVAILABLE_COLUMN, +	INFO_COLUMN, +	N_COLUMNS +}; + +#define PLUGIN_MANAGER_NAME_TITLE _("Plugin") +#define PLUGIN_MANAGER_ACTIVE_TITLE _("Enabled") + +#define GEDIT_PLUGIN_MANAGER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_PLUGIN_MANAGER, GeditPluginManagerPrivate)) + +struct _GeditPluginManagerPrivate +{ +	GtkWidget	*tree; + +	GtkWidget	*about_button; +	GtkWidget	*configure_button; + +	GeditPluginsEngine *engine; + +	GtkWidget 	*about; +	 +	GtkWidget	*popup_menu; +}; + +G_DEFINE_TYPE(GeditPluginManager, gedit_plugin_manager, GTK_TYPE_VBOX) + +static GeditPluginInfo *plugin_manager_get_selected_plugin (GeditPluginManager *pm);  +static void plugin_manager_toggle_active (GeditPluginManager *pm, GtkTreeIter *iter, GtkTreeModel *model); +static void gedit_plugin_manager_finalize (GObject *object); + +static void  +gedit_plugin_manager_class_init (GeditPluginManagerClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_plugin_manager_finalize; + +	g_type_class_add_private (object_class, sizeof (GeditPluginManagerPrivate)); +} + +static void +about_button_cb (GtkWidget          *button, +		 GeditPluginManager *pm) +{ +	GeditPluginInfo *info; + +	gedit_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	g_return_if_fail (info != NULL); + +	/* if there is another about dialog already open destroy it */ +	if (pm->priv->about) +		gtk_widget_destroy (pm->priv->about); + +	pm->priv->about = g_object_new (GTK_TYPE_ABOUT_DIALOG, +		"program-name", gedit_plugin_info_get_name (info), +		"copyright", gedit_plugin_info_get_copyright (info), +		"authors", gedit_plugin_info_get_authors (info), +		"comments", gedit_plugin_info_get_description (info), +		"website", gedit_plugin_info_get_website (info), +		"logo-icon-name", gedit_plugin_info_get_icon_name (info), +		"version", gedit_plugin_info_get_version (info), +		NULL); + +	gtk_window_set_destroy_with_parent (GTK_WINDOW (pm->priv->about), +					    TRUE); + +	g_signal_connect (pm->priv->about, +			  "response", +			  G_CALLBACK (gtk_widget_destroy), +			  NULL); +	g_signal_connect (pm->priv->about, +			  "destroy", +			  G_CALLBACK (gtk_widget_destroyed), +			  &pm->priv->about); + +	gtk_window_set_transient_for (GTK_WINDOW (pm->priv->about), +				      GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm)))); +	gtk_widget_show (pm->priv->about); +} + +static void +configure_button_cb (GtkWidget          *button, +		     GeditPluginManager *pm) +{ +	GeditPluginInfo *info; +	GtkWindow *toplevel; + +	gedit_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	g_return_if_fail (info != NULL); + +	gedit_debug_message (DEBUG_PLUGINS, "Configuring: %s\n",  +			     gedit_plugin_info_get_name (info)); + +	toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(pm))); + +	gedit_plugins_engine_configure_plugin (pm->priv->engine, +					       info, toplevel); + +	gedit_debug_message (DEBUG_PLUGINS, "Done");	 +} + +static void +plugin_manager_view_info_cell_cb (GtkTreeViewColumn *tree_column, +				  GtkCellRenderer   *cell, +				  GtkTreeModel      *tree_model, +				  GtkTreeIter       *iter, +				  gpointer           data) +{ +	GeditPluginInfo *info; +	gchar *text; +	 +	g_return_if_fail (tree_model != NULL); +	g_return_if_fail (tree_column != NULL); + +	gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + +	if (info == NULL) +		return; + +	text = g_markup_printf_escaped ("<b>%s</b>\n%s", +					gedit_plugin_info_get_name (info), +					gedit_plugin_info_get_description (info)); +	g_object_set (G_OBJECT (cell), +		      "markup", text, +		      "sensitive", gedit_plugin_info_is_available (info), +		      NULL); + +	g_free (text); +} + +static void +plugin_manager_view_icon_cell_cb (GtkTreeViewColumn *tree_column, +				  GtkCellRenderer   *cell, +				  GtkTreeModel      *tree_model, +				  GtkTreeIter       *iter, +				  gpointer           data) +{ +	GeditPluginInfo *info; +	 +	g_return_if_fail (tree_model != NULL); +	g_return_if_fail (tree_column != NULL); + +	gtk_tree_model_get (tree_model, iter, INFO_COLUMN, &info, -1); + +	if (info == NULL) +		return; + +	g_object_set (G_OBJECT (cell), +		      "icon-name", gedit_plugin_info_get_icon_name (info), +		      "sensitive", gedit_plugin_info_is_available (info), +		      NULL); +} + + +static void +active_toggled_cb (GtkCellRendererToggle *cell, +		   gchar                 *path_str, +		   GeditPluginManager    *pm) +{ +	GtkTreeIter iter; +	GtkTreePath *path; +	GtkTreeModel *model; + +	gedit_debug (DEBUG_PLUGINS); + +	path = gtk_tree_path_new_from_string (path_str); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter (model, &iter, path); + +	if (&iter != NULL) +		plugin_manager_toggle_active (pm, &iter, model); + +	gtk_tree_path_free (path); +} + +static void +cursor_changed_cb (GtkTreeView *view, +		   gpointer     data) +{ +	GeditPluginManager *pm = data; +	GeditPluginInfo *info; + +	gedit_debug (DEBUG_PLUGINS); + +	info = plugin_manager_get_selected_plugin (pm); + +	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->about_button), +				  info != NULL); +	gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), +				  (info != NULL) &&  +				   gedit_plugin_info_is_configurable (info)); +} + +static void +row_activated_cb (GtkTreeView       *tree_view, +		  GtkTreePath       *path, +		  GtkTreeViewColumn *column, +		  gpointer           data) +{ +	GeditPluginManager *pm = data; +	GtkTreeIter iter; +	GtkTreeModel *model; + +	gedit_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter (model, &iter, path); + +	g_return_if_fail (&iter != NULL); + +	plugin_manager_toggle_active (pm, &iter, model); +} + +static void +plugin_manager_populate_lists (GeditPluginManager *pm) +{ +	const GList *plugins; +	GtkListStore *model; +	GtkTreeIter iter; + +	gedit_debug (DEBUG_PLUGINS); + +	plugins = gedit_plugins_engine_get_plugin_list (pm->priv->engine); + +	model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree))); + +	while (plugins) +	{ +		GeditPluginInfo *info; +		info = (GeditPluginInfo *)plugins->data; + +		gtk_list_store_append (model, &iter); +		gtk_list_store_set (model, &iter, +				    ACTIVE_COLUMN, gedit_plugin_info_is_active (info), +				    AVAILABLE_COLUMN, gedit_plugin_info_is_available (info), +				    INFO_COLUMN, info, +				    -1); + +		plugins = plugins->next; +	} + +	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) +	{ +		GtkTreeSelection *selection; +		GeditPluginInfo* info; + +		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); +		g_return_if_fail (selection != NULL); +		 +		gtk_tree_selection_select_iter (selection, &iter); + +		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, +				    INFO_COLUMN, &info, -1); + +		gtk_widget_set_sensitive (GTK_WIDGET (pm->priv->configure_button), +					  gedit_plugin_info_is_configurable (info)); +	} +} + +static gboolean +plugin_manager_set_active (GeditPluginManager *pm, +			   GtkTreeIter        *iter, +			   GtkTreeModel       *model, +			   gboolean            active) +{ +	GeditPluginInfo *info; +	gboolean res = TRUE; +	 +	gedit_debug (DEBUG_PLUGINS); + +	gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (active) +	{ +		/* activate the plugin */ +		if (!gedit_plugins_engine_activate_plugin (pm->priv->engine, info)) { +			gedit_debug_message (DEBUG_PLUGINS, "Could not activate %s.\n",  +					     gedit_plugin_info_get_name (info)); + +			res = FALSE; +		} +	} +	else +	{ +		/* deactivate the plugin */ +		if (!gedit_plugins_engine_deactivate_plugin (pm->priv->engine, info)) { +			gedit_debug_message (DEBUG_PLUGINS, "Could not deactivate %s.\n",  +					     gedit_plugin_info_get_name (info)); + +			res = FALSE; +		} +	} + +	return res; +} + +static void +plugin_manager_toggle_active (GeditPluginManager *pm, +			      GtkTreeIter        *iter, +			      GtkTreeModel       *model) +{ +	gboolean active; +	 +	gedit_debug (DEBUG_PLUGINS); + +	gtk_tree_model_get (model, iter, ACTIVE_COLUMN, &active, -1); + +	active ^= 1; + +	plugin_manager_set_active (pm, iter, model, active); +} + +static GeditPluginInfo * +plugin_manager_get_selected_plugin (GeditPluginManager *pm) +{ +	GeditPluginInfo *info = NULL; +	GtkTreeModel *model; +	GtkTreeIter iter; +	GtkTreeSelection *selection; + +	gedit_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); +	g_return_val_if_fail (model != NULL, NULL); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); +	g_return_val_if_fail (selection != NULL, NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) +	{ +		gtk_tree_model_get (model, &iter, INFO_COLUMN, &info, -1); +	} +	 +	return info; +} + +static void +plugin_manager_set_active_all (GeditPluginManager *pm, +			       gboolean            active) +{ +	GtkTreeModel *model; +	GtkTreeIter iter; + +	gedit_debug (DEBUG_PLUGINS); + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); + +	g_return_if_fail (model != NULL); + +	gtk_tree_model_get_iter_first (model, &iter); + +	do { +		plugin_manager_set_active (pm, &iter, model, active); +	} +	while (gtk_tree_model_iter_next (model, &iter)); +} + +/* Callback used as the interactive search comparison function */ +static gboolean +name_search_cb (GtkTreeModel *model, +		gint          column, +		const gchar  *key, +		GtkTreeIter  *iter, +		gpointer      data) +{ +	GeditPluginInfo *info; +	gchar *normalized_string; +	gchar *normalized_key; +	gchar *case_normalized_string; +	gchar *case_normalized_key; +	gint key_len; +	gboolean retval; + +	gtk_tree_model_get (model, iter, INFO_COLUMN, &info, -1); +	if (!info) +		return FALSE; + +	normalized_string = g_utf8_normalize (gedit_plugin_info_get_name (info), -1, G_NORMALIZE_ALL); +	normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); +	case_normalized_string = g_utf8_casefold (normalized_string, -1); +	case_normalized_key = g_utf8_casefold (normalized_key, -1); + +	key_len = strlen (case_normalized_key); + +	/* Oddly enough, this callback must return whether to stop the search +	 * because we found a match, not whether we actually matched. +	 */ +	retval = (strncmp (case_normalized_key, case_normalized_string, key_len) != 0); + +	g_free (normalized_key); +	g_free (normalized_string); +	g_free (case_normalized_key); +	g_free (case_normalized_string); + +	return retval; +} + +static void +enable_plugin_menu_cb (GtkMenu            *menu, +		       GeditPluginManager *pm) +{ +	GtkTreeModel *model; +	GtkTreeIter iter; +	GtkTreeSelection *selection; + +	model = gtk_tree_view_get_model (GTK_TREE_VIEW (pm->priv->tree)); +	g_return_if_fail (model != NULL); + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); +	g_return_if_fail (selection != NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) +		plugin_manager_toggle_active (pm, &iter, model); +} + +static void +enable_all_menu_cb (GtkMenu            *menu, +		    GeditPluginManager *pm) +{ +	plugin_manager_set_active_all (pm, TRUE); +} + +static void +disable_all_menu_cb (GtkMenu            *menu, +		     GeditPluginManager *pm) +{ +	plugin_manager_set_active_all (pm, FALSE); +} + +static GtkWidget * +create_tree_popup_menu (GeditPluginManager *pm) +{ +	GtkWidget *menu; +	GtkWidget *item; +	GtkWidget *image; +	GeditPluginInfo *info; + +	info = plugin_manager_get_selected_plugin (pm); + +	menu = gtk_menu_new (); + +	item = gtk_image_menu_item_new_with_mnemonic (_("_About")); +	image = gtk_image_new_from_stock (GTK_STOCK_ABOUT, +					  GTK_ICON_SIZE_MENU); +	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); +	g_signal_connect (item, "activate", +			  G_CALLBACK (about_button_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_image_menu_item_new_with_mnemonic (_("C_onfigure")); +	image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, +					  GTK_ICON_SIZE_MENU); +	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); +	g_signal_connect (item, "activate", +			  G_CALLBACK (configure_button_cb), pm); +	gtk_widget_set_sensitive (item, gedit_plugin_info_is_configurable (info)); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_check_menu_item_new_with_mnemonic (_("A_ctivate")); +	gtk_widget_set_sensitive (item, gedit_plugin_info_is_available (info)); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), +					gedit_plugin_info_is_active (info)); +	g_signal_connect (item, "toggled", +			  G_CALLBACK (enable_plugin_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_separator_menu_item_new (); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_menu_item_new_with_mnemonic (_("Ac_tivate All")); +	g_signal_connect (item, "activate", +			  G_CALLBACK (enable_all_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + +	item = gtk_menu_item_new_with_mnemonic (_("_Deactivate All")); +	g_signal_connect (item, "activate", +			  G_CALLBACK (disable_all_menu_cb), pm); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +	 +	gtk_widget_show_all (menu); +	 +	return menu; +} + +static void +tree_popup_menu_detach (GeditPluginManager *pm, +			GtkMenu            *menu) +{ +	pm->priv->popup_menu = NULL; +} + +static void +show_tree_popup_menu (GtkTreeView        *tree, +		      GeditPluginManager *pm, +		      GdkEventButton     *event) +{ +	if (pm->priv->popup_menu) +		gtk_widget_destroy (pm->priv->popup_menu); + +	pm->priv->popup_menu = create_tree_popup_menu (pm); +	 +	gtk_menu_attach_to_widget (GTK_MENU (pm->priv->popup_menu), +				   GTK_WIDGET (pm), +				   (GtkMenuDetachFunc) tree_popup_menu_detach); + +	if (event != NULL) +	{ +		gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, +				NULL, NULL, +				event->button, event->time); +	} +	else +	{ +		gtk_menu_popup (GTK_MENU (pm->priv->popup_menu), NULL, NULL, +				gedit_utils_menu_position_under_tree_view, tree, +				0, gtk_get_current_event_time ()); + +		gtk_menu_shell_select_first (GTK_MENU_SHELL (pm->priv->popup_menu), +					     FALSE); +	} +} + +static gboolean +button_press_event_cb (GtkWidget          *tree, +		       GdkEventButton     *event, +		       GeditPluginManager *pm) +{ +	/* We want the treeview selection to be updated before showing the menu. +	 * This code is evil, thanks to Federico Mena Quintero's black magic. +	 * See: http://mail.mate.org/archives/gtk-devel-list/2006-February/msg00168.html +	 * FIXME: Let's remove it asap. +	 */ + +	static gboolean in_press = FALSE; +	gboolean handled; + +	if (in_press) +		return FALSE; /* we re-entered */ + +	if (GDK_BUTTON_PRESS != event->type || 3 != event->button) +		return FALSE; /* let the normal handler run */ + +	in_press = TRUE; +	handled = gtk_widget_event (tree, (GdkEvent *) event); +	in_press = FALSE; + +	if (!handled) +		return FALSE; +		 +	/* The selection is fully updated by now */ +	show_tree_popup_menu (GTK_TREE_VIEW (tree), pm, event); +	return TRUE; +} + +static gboolean +popup_menu_cb (GtkTreeView        *tree, +	       GeditPluginManager *pm) +{ +	show_tree_popup_menu (tree, pm, NULL); +	return TRUE; +} + +static gint  +model_name_sort_func (GtkTreeModel *model, +		      GtkTreeIter  *iter1, +		      GtkTreeIter  *iter2, +		      gpointer      user_data) +{ +	GeditPluginInfo *info1, *info2; +	 +	gtk_tree_model_get (model, iter1, INFO_COLUMN, &info1, -1); +	gtk_tree_model_get (model, iter2, INFO_COLUMN, &info2, -1); + +	return g_utf8_collate (gedit_plugin_info_get_name (info1), +			       gedit_plugin_info_get_name (info2)); +} + +static void +plugin_manager_construct_tree (GeditPluginManager *pm) +{ +	GtkTreeViewColumn *column; +	GtkCellRenderer *cell; +	GtkListStore *model; + +	gedit_debug (DEBUG_PLUGINS); + +	model = gtk_list_store_new (N_COLUMNS, +				    G_TYPE_BOOLEAN, +				    G_TYPE_BOOLEAN, +				    G_TYPE_POINTER); + +	gtk_tree_view_set_model (GTK_TREE_VIEW (pm->priv->tree), +				 GTK_TREE_MODEL (model)); +	g_object_unref (model); + +	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (pm->priv->tree), TRUE); +	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (pm->priv->tree), FALSE); + +	/* first column */ +	cell = gtk_cell_renderer_toggle_new (); +	g_object_set (cell, "xpad", 6, NULL); +	g_signal_connect (cell, +			  "toggled", +			  G_CALLBACK (active_toggled_cb), +			  pm); +	column = gtk_tree_view_column_new_with_attributes (PLUGIN_MANAGER_ACTIVE_TITLE, +							   cell, +							   "active", +							   ACTIVE_COLUMN, +							   "activatable", +							   AVAILABLE_COLUMN, +							   "sensitive", +							   AVAILABLE_COLUMN, +							   NULL); +	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + +	/* second column */ +	column = gtk_tree_view_column_new (); +	gtk_tree_view_column_set_title (column, PLUGIN_MANAGER_NAME_TITLE); +	gtk_tree_view_column_set_resizable (column, TRUE); + +	cell = gtk_cell_renderer_pixbuf_new (); +	gtk_tree_view_column_pack_start (column, cell, FALSE); +	g_object_set (cell, "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); +	gtk_tree_view_column_set_cell_data_func (column, cell, +						 plugin_manager_view_icon_cell_cb, +						 pm, NULL); +	 +	cell = gtk_cell_renderer_text_new (); +	gtk_tree_view_column_pack_start (column, cell, TRUE); +	g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +	gtk_tree_view_column_set_cell_data_func (column, cell, +						 plugin_manager_view_info_cell_cb, +						 pm, NULL); +	 +	 +	gtk_tree_view_column_set_spacing (column, 6); +	gtk_tree_view_append_column (GTK_TREE_VIEW (pm->priv->tree), column); + +	/* Sort on the plugin names */ +	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model), +	                                         model_name_sort_func, +        	                                 NULL, +                	                         NULL); +	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), +					      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, +					      GTK_SORT_ASCENDING); + +	/* Enable search for our non-string column */ +	gtk_tree_view_set_search_column (GTK_TREE_VIEW (pm->priv->tree), +					 INFO_COLUMN); +	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (pm->priv->tree), +					     name_search_cb, +					     NULL, +					     NULL); + +	g_signal_connect (pm->priv->tree, +			  "cursor_changed", +			  G_CALLBACK (cursor_changed_cb), +			  pm); +	g_signal_connect (pm->priv->tree, +			  "row_activated", +			  G_CALLBACK (row_activated_cb), +			  pm); + +	g_signal_connect (pm->priv->tree, +			  "button-press-event", +			  G_CALLBACK (button_press_event_cb), +			  pm); +	g_signal_connect (pm->priv->tree, +			  "popup-menu", +			  G_CALLBACK (popup_menu_cb), +			  pm); +	gtk_widget_show (pm->priv->tree); +} + +static void +plugin_toggled_cb (GeditPluginsEngine *engine, +		   GeditPluginInfo    *info, +		   GeditPluginManager *pm) +{ +	GtkTreeSelection *selection; +	GtkTreeModel *model; +	GtkTreeIter iter; +	gboolean info_found = FALSE; + +	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pm->priv->tree)); + +	if (gtk_tree_selection_get_selected (selection, &model, &iter)) +	{ +		/* There is an item selected: it's probably the one we want! */ +		GeditPluginInfo *tinfo; +		gtk_tree_model_get (model, &iter, INFO_COLUMN, &tinfo, -1); +		info_found = info == tinfo; +	} + +	if (!info_found) +	{ +		gtk_tree_model_get_iter_first (model, &iter); + +		do +		{ +			GeditPluginInfo *tinfo; +			gtk_tree_model_get (model, &iter, INFO_COLUMN, &tinfo, -1); +			info_found = info == tinfo; +		} +		while (!info_found && gtk_tree_model_iter_next (model, &iter)); +	} + +	if (!info_found) +	{ +		g_warning ("GeditPluginManager: plugin '%s' not found in the tree model", +			   gedit_plugin_info_get_name (info)); +		return; +	} + +	gtk_list_store_set (GTK_LIST_STORE (model), &iter, ACTIVE_COLUMN, gedit_plugin_info_is_active (info), -1); +} + +static void  +gedit_plugin_manager_init (GeditPluginManager *pm) +{ +	GtkWidget *label; +	GtkWidget *viewport; +	GtkWidget *hbuttonbox; + +	gedit_debug (DEBUG_PLUGINS); + +	pm->priv = GEDIT_PLUGIN_MANAGER_GET_PRIVATE (pm); + +	/* +	 * Always we create the manager, firstly we rescan the plugins directory +	 */ +	gedit_plugins_engine_rescan_plugins (gedit_plugins_engine_get_default ()); + +	gtk_box_set_spacing (GTK_BOX (pm), 6); + +	label = gtk_label_new_with_mnemonic (_("Active _Plugins:")); +	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); +	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); +	 +	gtk_box_pack_start (GTK_BOX (pm), label, FALSE, TRUE, 0); +	 +	viewport = gtk_scrolled_window_new (NULL, NULL); +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (viewport), +					GTK_POLICY_AUTOMATIC, +					GTK_POLICY_AUTOMATIC); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (viewport),  +					     GTK_SHADOW_IN); + +	gtk_box_pack_start (GTK_BOX (pm), viewport, TRUE, TRUE, 0); + +	pm->priv->tree = gtk_tree_view_new (); +	gtk_container_add (GTK_CONTAINER (viewport), pm->priv->tree); + +	gtk_label_set_mnemonic_widget (GTK_LABEL (label), pm->priv->tree); + +	hbuttonbox = gtk_hbutton_box_new (); +	gtk_box_pack_start (GTK_BOX (pm), hbuttonbox, FALSE, FALSE, 0); +	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END); +	gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8); + +	pm->priv->about_button = gedit_gtk_button_new_with_stock_icon (_("_About Plugin"), +								       GTK_STOCK_ABOUT); +	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->about_button); + +	pm->priv->configure_button = gedit_gtk_button_new_with_stock_icon (_("C_onfigure Plugin"), +									   GTK_STOCK_PREFERENCES); +	gtk_container_add (GTK_CONTAINER (hbuttonbox), pm->priv->configure_button); + +	/* setup a window of a sane size. */ +	gtk_widget_set_size_request (GTK_WIDGET (viewport), 270, 100); + +	g_signal_connect (pm->priv->about_button, +			  "clicked", +			  G_CALLBACK (about_button_cb), +			  pm); +	g_signal_connect (pm->priv->configure_button, +			  "clicked", +			  G_CALLBACK (configure_button_cb), +			  pm); + +	plugin_manager_construct_tree (pm); + +	/* get the plugin engine and populate the treeview */ +	pm->priv->engine = gedit_plugins_engine_get_default (); + +	g_signal_connect_after (pm->priv->engine, +				"activate-plugin", +				G_CALLBACK (plugin_toggled_cb), +				pm); +	g_signal_connect_after (pm->priv->engine, +				"deactivate-plugin", +				G_CALLBACK (plugin_toggled_cb), +				pm); + +	if (gedit_plugins_engine_get_plugin_list (pm->priv->engine) != NULL) +	{ +		plugin_manager_populate_lists (pm); +	} +	else +	{ +		gtk_widget_set_sensitive (pm->priv->about_button, FALSE); +		gtk_widget_set_sensitive (pm->priv->configure_button, FALSE);		 +	} +} + +static void +gedit_plugin_manager_finalize (GObject *object) +{ +	GeditPluginManager *pm = GEDIT_PLUGIN_MANAGER (object); + +	g_signal_handlers_disconnect_by_func (pm->priv->engine, +					      plugin_toggled_cb, +					      pm); + +	if (pm->priv->popup_menu) +		gtk_widget_destroy (pm->priv->popup_menu); + +	G_OBJECT_CLASS (gedit_plugin_manager_parent_class)->finalize (object); + +} + +GtkWidget *gedit_plugin_manager_new (void) +{ +	return g_object_new (GEDIT_TYPE_PLUGIN_MANAGER,0); +} diff --git a/gedit/gedit-plugin-manager.h b/gedit/gedit-plugin-manager.h new file mode 100755 index 00000000..53392ba5 --- /dev/null +++ b/gedit/gedit-plugin-manager.h @@ -0,0 +1,83 @@ +/* + * gedit-plugin-manager.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PLUGIN_MANAGER_H__ +#define __GEDIT_PLUGIN_MANAGER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PLUGIN_MANAGER              (gedit_plugin_manager_get_type()) +#define GEDIT_PLUGIN_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PLUGIN_MANAGER, GeditPluginManager)) +#define GEDIT_PLUGIN_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PLUGIN_MANAGER, GeditPluginManagerClass)) +#define GEDIT_IS_PLUGIN_MANAGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PLUGIN_MANAGER)) +#define GEDIT_IS_PLUGIN_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PLUGIN_MANAGER)) +#define GEDIT_PLUGIN_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PLUGIN_MANAGER, GeditPluginManagerClass)) + +/* Private structure type */ +typedef struct _GeditPluginManagerPrivate GeditPluginManagerPrivate; + +/* + * Main object structure + */ +typedef struct _GeditPluginManager GeditPluginManager; + +struct _GeditPluginManager  +{ +	GtkVBox vbox; + +	/*< private > */ +	GeditPluginManagerPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditPluginManagerClass GeditPluginManagerClass; + +struct _GeditPluginManagerClass  +{ +	GtkVBoxClass parent_class; +}; + +/* + * Public methods + */ +GType		 gedit_plugin_manager_get_type		(void) G_GNUC_CONST; + +GtkWidget	*gedit_plugin_manager_new		(void); +    +G_END_DECLS + +#endif  /* __GEDIT_PLUGIN_MANAGER_H__  */ diff --git a/gedit/gedit-plugin.c b/gedit/gedit-plugin.c new file mode 100755 index 00000000..2b00e3fd --- /dev/null +++ b/gedit/gedit-plugin.c @@ -0,0 +1,334 @@ +/* + * gedit-plugin.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-plugin.h" +#include "gedit-dirs.h" + +/* properties */ +enum { +	PROP_0, +	PROP_INSTALL_DIR, +	PROP_DATA_DIR_NAME, +	PROP_DATA_DIR +}; + +typedef struct _GeditPluginPrivate GeditPluginPrivate; + +struct _GeditPluginPrivate +{ +	gchar *install_dir; +	gchar *data_dir_name; +}; + +#define GEDIT_PLUGIN_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_PLUGIN, GeditPluginPrivate)) + +G_DEFINE_TYPE(GeditPlugin, gedit_plugin, G_TYPE_OBJECT) + +static void +dummy (GeditPlugin *plugin, GeditWindow *window) +{ +	/* Empty */ +} + +static GtkWidget * +create_configure_dialog	(GeditPlugin *plugin) +{ +	return NULL; +} + +static gboolean +is_configurable (GeditPlugin *plugin) +{ +	return (GEDIT_PLUGIN_GET_CLASS (plugin)->create_configure_dialog != +		create_configure_dialog); +} + +static void +gedit_plugin_get_property (GObject    *object, +			   guint       prop_id, +			   GValue     *value, +			   GParamSpec *pspec) +{ +	switch (prop_id) +	{ +		case PROP_INSTALL_DIR: +			g_value_take_string (value, gedit_plugin_get_install_dir (GEDIT_PLUGIN (object))); +			break; +		case PROP_DATA_DIR: +			g_value_take_string (value, gedit_plugin_get_data_dir (GEDIT_PLUGIN (object))); +			break; +		default: +			g_return_if_reached (); +	} +} + +static void +gedit_plugin_set_property (GObject      *object, +			   guint         prop_id, +			   const GValue *value, +			   GParamSpec   *pspec) +{ +	GeditPluginPrivate *priv = GEDIT_PLUGIN_GET_PRIVATE (object); + +	switch (prop_id) +	{ +		case PROP_INSTALL_DIR: +			priv->install_dir = g_value_dup_string (value); +			break; +		case PROP_DATA_DIR_NAME: +			priv->data_dir_name = g_value_dup_string (value); +			break; +		default: +			g_return_if_reached (); +	} +} + +static void +gedit_plugin_finalize (GObject *object) +{ +	GeditPluginPrivate *priv = GEDIT_PLUGIN_GET_PRIVATE (object); + +	g_free (priv->install_dir); +	g_free (priv->data_dir_name); + +	G_OBJECT_CLASS (gedit_plugin_parent_class)->finalize (object); +} + +static void  +gedit_plugin_class_init (GeditPluginClass *klass) +{ +    	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	klass->activate = dummy; +	klass->deactivate = dummy; +	klass->update_ui = dummy; +	 +	klass->create_configure_dialog = create_configure_dialog; +	klass->is_configurable = is_configurable; + +	object_class->get_property = gedit_plugin_get_property; +	object_class->set_property = gedit_plugin_set_property; +	object_class->finalize = gedit_plugin_finalize; + +	g_object_class_install_property (object_class, +					 PROP_INSTALL_DIR, +					 g_param_spec_string ("install-dir", +							      "Install Directory", +							      "The directory where the plugin is installed", +							      NULL, +							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + +	/* the basename of the data dir is set at construction time by the plugin loader +	 * while the full path is constructed on the fly to take into account relocability +	 * that's why we have a writeonly prop and a readonly prop */ +	g_object_class_install_property (object_class, +					 PROP_DATA_DIR_NAME, +					 g_param_spec_string ("data-dir-name", +							      "Basename of the data directory", +							      "The basename of the directory where the plugin should look for its data files", +							      NULL, +							      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +	g_object_class_install_property (object_class, +					 PROP_DATA_DIR, +					 g_param_spec_string ("data-dir", +							      "Data Directory", +							      "The full path of the directory where the plugin should look for its data files", +							      NULL, +							      G_PARAM_READABLE)); + +	g_type_class_add_private (klass, sizeof (GeditPluginPrivate)); +} + +static void +gedit_plugin_init (GeditPlugin *plugin) +{ +	/* Empty */ +} + +/** + * gedit_plugin_get_install_dir: + * @plugin: a #GeditPlugin + * + * Get the path of the directory where the plugin is installed. + * + * Return value: a newly allocated string with the path of the + * directory where the plugin is installed + */ +gchar * +gedit_plugin_get_install_dir (GeditPlugin *plugin) +{ +	g_return_val_if_fail (GEDIT_IS_PLUGIN (plugin), NULL); + +	return g_strdup (GEDIT_PLUGIN_GET_PRIVATE (plugin)->install_dir); +} + +/** + * gedit_plugin_get_data_dir: + * @plugin: a #GeditPlugin + * + * Get the path of the directory where the plugin should look for + * its data files. + * + * Return value: a newly allocated string with the path of the + * directory where the plugin should look for its data files + */ +gchar * +gedit_plugin_get_data_dir (GeditPlugin *plugin) +{ +	GeditPluginPrivate *priv; +	gchar *gedit_lib_dir; +	gchar *data_dir; + +	g_return_val_if_fail (GEDIT_IS_PLUGIN (plugin), NULL); + +	priv = GEDIT_PLUGIN_GET_PRIVATE (plugin); + +	/* If it's a "user" plugin the data dir is +	 * install_dir/data_dir_name if instead it's a +	 * "system" plugin the data dir is under gedit_data_dir, +	 * so it's under $prefix/share/gedit-2/plugins/data_dir_name +	 * where data_dir_name usually it's the name of the plugin +	 */ +	gedit_lib_dir = gedit_dirs_get_gedit_lib_dir (); + +	/* CHECK: is checking the prefix enough or should we be more +	 * careful about normalizing paths etc? */ +	if (g_str_has_prefix (priv->install_dir, gedit_lib_dir)) +	{ +		gchar *gedit_data_dir; + +		gedit_data_dir = gedit_dirs_get_gedit_data_dir (); + +		data_dir = g_build_filename (gedit_data_dir, +					     "plugins", +					     priv->data_dir_name, +					     NULL); + +		g_free (gedit_data_dir); +	} +	else +	{ +		data_dir = g_build_filename (priv->install_dir, +					     priv->data_dir_name, +					     NULL); +	} + +	g_free (gedit_lib_dir); + +	return data_dir; +} + +/** + * gedit_plugin_activate: + * @plugin: a #GeditPlugin + * @window: a #GeditWindow + *  + * Activates the plugin. + */ +void +gedit_plugin_activate (GeditPlugin *plugin, +		       GeditWindow *window) +{ +	g_return_if_fail (GEDIT_IS_PLUGIN (plugin)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	 +	GEDIT_PLUGIN_GET_CLASS (plugin)->activate (plugin, window); +} + +/** + * gedit_plugin_deactivate: + * @plugin: a #GeditPlugin + * @window: a #GeditWindow + *  + * Deactivates the plugin. + */ +void +gedit_plugin_deactivate	(GeditPlugin *plugin, +			 GeditWindow *window) +{ +	g_return_if_fail (GEDIT_IS_PLUGIN (plugin)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	GEDIT_PLUGIN_GET_CLASS (plugin)->deactivate (plugin, window); +} + +/** + * gedit_plugin_update_ui: + * @plugin: a #GeditPlugin + * @window: a #GeditWindow + * + * Triggers an update of the user interface to take into account state changes + * caused by the plugin. + */		  +void +gedit_plugin_update_ui	(GeditPlugin *plugin, +			 GeditWindow *window) +{ +	g_return_if_fail (GEDIT_IS_PLUGIN (plugin)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	GEDIT_PLUGIN_GET_CLASS (plugin)->update_ui (plugin, window); +} + +/** + * gedit_plugin_is_configurable: + * @plugin: a #GeditPlugin + * + * Whether the plugin is configurable. + * + * Returns: TRUE if the plugin is configurable: + */ +gboolean +gedit_plugin_is_configurable (GeditPlugin *plugin) +{ +	g_return_val_if_fail (GEDIT_IS_PLUGIN (plugin), FALSE); + +	return GEDIT_PLUGIN_GET_CLASS (plugin)->is_configurable (plugin); +} + +/** + * gedit_plugin_create_configure_dialog: + * @plugin: a #GeditPlugin + * + * Creates the configure dialog widget for the plugin. + * + * Returns: the configure dialog widget for the plugin. + */ +GtkWidget * +gedit_plugin_create_configure_dialog (GeditPlugin *plugin) +{ +	g_return_val_if_fail (GEDIT_IS_PLUGIN (plugin), NULL); +	 +	return GEDIT_PLUGIN_GET_CLASS (plugin)->create_configure_dialog (plugin); +} diff --git a/gedit/gedit-plugin.h b/gedit/gedit-plugin.h new file mode 100755 index 00000000..8cf41d80 --- /dev/null +++ b/gedit/gedit-plugin.h @@ -0,0 +1,240 @@ +/* + * gedit-plugin.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PLUGIN_H__ +#define __GEDIT_PLUGIN_H__ + +#include <glib-object.h> + +#include <gedit/gedit-window.h> +#include <gedit/gedit-debug.h> + +/* TODO: add a .h file that includes all the .h files normally needed to + * develop a plugin */  + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PLUGIN              (gedit_plugin_get_type()) +#define GEDIT_PLUGIN(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PLUGIN, GeditPlugin)) +#define GEDIT_PLUGIN_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PLUGIN, GeditPluginClass)) +#define GEDIT_IS_PLUGIN(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PLUGIN)) +#define GEDIT_IS_PLUGIN_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PLUGIN)) +#define GEDIT_PLUGIN_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PLUGIN, GeditPluginClass)) + +/* + * Main object structure + */ +typedef struct _GeditPlugin GeditPlugin; + +struct _GeditPlugin  +{ +	GObject parent; +}; + +/* + * Class definition + */ +typedef struct _GeditPluginClass GeditPluginClass; + +struct _GeditPluginClass  +{ +	GObjectClass parent_class; + +	/* Virtual public methods */ +	 +	void 		(*activate)		(GeditPlugin *plugin, +						 GeditWindow *window); +	void 		(*deactivate)		(GeditPlugin *plugin, +						 GeditWindow *window); + +	void 		(*update_ui)		(GeditPlugin *plugin, +						 GeditWindow *window); + +	GtkWidget 	*(*create_configure_dialog) +						(GeditPlugin *plugin); + +	/* Plugins should not override this, it's handled automatically by +	   the GeditPluginClass */ +	gboolean 	(*is_configurable) +						(GeditPlugin *plugin); + +	/* Padding for future expansion */ +	void		(*_gedit_reserved1)	(void); +	void		(*_gedit_reserved2)	(void); +	void		(*_gedit_reserved3)	(void); +	void		(*_gedit_reserved4)	(void); +}; + +/* + * Public methods + */ +GType 		 gedit_plugin_get_type 		(void) G_GNUC_CONST; + +gchar 		*gedit_plugin_get_install_dir	(GeditPlugin *plugin); +gchar 		*gedit_plugin_get_data_dir	(GeditPlugin *plugin); + +void 		 gedit_plugin_activate		(GeditPlugin *plugin, +						 GeditWindow *window); +void 		 gedit_plugin_deactivate	(GeditPlugin *plugin, +						 GeditWindow *window); +				  +void 		 gedit_plugin_update_ui		(GeditPlugin *plugin, +						 GeditWindow *window); + +gboolean	 gedit_plugin_is_configurable	(GeditPlugin *plugin); +GtkWidget	*gedit_plugin_create_configure_dialog		 +						(GeditPlugin *plugin); + +/** + * GEDIT_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, CODE): + * + * Utility macro used to register plugins with additional code. + */ +#define GEDIT_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, CODE) 	\ +	G_DEFINE_DYNAMIC_TYPE_EXTENDED (PluginName,				\ +					plugin_name,				\ +					GEDIT_TYPE_PLUGIN,			\ +					0,					\ +					GTypeModule *module G_GNUC_UNUSED = type_module; /* back compat */	\ +					CODE)					\ +										\ +/* This is not very nice, but G_DEFINE_DYNAMIC wants it and our old macro	\ + * did not support it */							\ +static void									\ +plugin_name##_class_finalize (PluginName##Class *klass)				\ +{										\ +}										\ +										\ +										\ +G_MODULE_EXPORT GType								\ +register_gedit_plugin (GTypeModule *type_module)				\ +{										\ +	plugin_name##_register_type (type_module);				\ +										\ +	return plugin_name##_get_type();					\ +} + +/** + * GEDIT_PLUGIN_REGISTER_TYPE(PluginName, plugin_name): + *  + * Utility macro used to register plugins. + */ +#define GEDIT_PLUGIN_REGISTER_TYPE(PluginName, plugin_name)			\ +	GEDIT_PLUGIN_REGISTER_TYPE_WITH_CODE(PluginName, plugin_name, ;) + +/** + * GEDIT_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, CODE): + * + * Utility macro used to register gobject types in plugins with additional code. + * + * Deprecated: use G_DEFINE_DYNAMIC_TYPE_EXTENDED instead + */ +#define GEDIT_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, CODE) \ +										\ +static GType g_define_type_id = 0;						\ +										\ +GType										\ +object_name##_get_type (void)							\ +{										\ +	return g_define_type_id;						\ +}										\ +										\ +static void     object_name##_init              (ObjectName        *self);	\ +static void     object_name##_class_init        (ObjectName##Class *klass);	\ +static gpointer object_name##_parent_class = NULL;				\ +static void     object_name##_class_intern_init (gpointer klass)		\ +{										\ +	object_name##_parent_class = g_type_class_peek_parent (klass);		\ +	object_name##_class_init ((ObjectName##Class *) klass);			\ +}										\ +										\ +GType										\ +object_name##_register_type (GTypeModule *type_module)				\ +{										\ +	GTypeModule *module G_GNUC_UNUSED = type_module; /* back compat */			\ +	static const GTypeInfo our_info =					\ +	{									\ +		sizeof (ObjectName##Class),					\ +		NULL, /* base_init */						\ +		NULL, /* base_finalize */					\ +		(GClassInitFunc) object_name##_class_intern_init,		\ +		NULL,								\ +		NULL, /* class_data */						\ +		sizeof (ObjectName),						\ +		0, /* n_preallocs */						\ +		(GInstanceInitFunc) object_name##_init				\ +	};									\ +										\ +	g_define_type_id = g_type_module_register_type (type_module,		\ +					   	        PARENT_TYPE,		\ +					                #ObjectName,		\ +					                &our_info,		\ +					                0);			\ +										\ +	CODE									\ +										\ +	return g_define_type_id;						\ +} + + +/** + * GEDIT_PLUGIN_DEFINE_TYPE(ObjectName, object_name, PARENT_TYPE): + * + * Utility macro used to register gobject types in plugins. + * + * Deprecated: use G_DEFINE_DYNAMIC instead + */ +#define GEDIT_PLUGIN_DEFINE_TYPE(ObjectName, object_name, PARENT_TYPE)		\ +	GEDIT_PLUGIN_DEFINE_TYPE_WITH_CODE(ObjectName, object_name, PARENT_TYPE, ;) + +/** + * GEDIT_PLUGIN_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init): + * + * Utility macro used to register interfaces for gobject types in plugins. + */ +#define GEDIT_PLUGIN_IMPLEMENT_INTERFACE(object_name, TYPE_IFACE, iface_init)	\ +	const GInterfaceInfo object_name##_interface_info = 			\ +	{ 									\ +		(GInterfaceInitFunc) iface_init,				\ +		NULL, 								\ +		NULL								\ +	};									\ +										\ +	g_type_module_add_interface (type_module, 					\ +				     g_define_type_id, 				\ +				     TYPE_IFACE, 				\ +				     &object_name##_interface_info); + +G_END_DECLS + +#endif  /* __GEDIT_PLUGIN_H__ */ diff --git a/gedit/gedit-plugins-engine.c b/gedit/gedit-plugins-engine.c new file mode 100755 index 00000000..1a34692b --- /dev/null +++ b/gedit/gedit-plugins-engine.c @@ -0,0 +1,861 @@ +/* + * gedit-plugins-engine.c + * This file is part of gedit + * + * Copyright (C) 2002-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> + +#include "gedit-plugins-engine.h" +#include "gedit-plugin-info-priv.h" +#include "gedit-plugin.h" +#include "gedit-debug.h" +#include "gedit-app.h" +#include "gedit-prefs-manager.h" +#include "gedit-plugin-loader.h" +#include "gedit-object-module.h" +#include "gedit-dirs.h" + +#define GEDIT_PLUGINS_ENGINE_BASE_KEY "/apps/gedit-2/plugins" +#define GEDIT_PLUGINS_ENGINE_KEY GEDIT_PLUGINS_ENGINE_BASE_KEY "/active-plugins" + +#define PLUGIN_EXT	".gedit-plugin" +#define LOADER_EXT	G_MODULE_SUFFIX + +typedef struct +{ +	GeditPluginLoader *loader; +	GeditObjectModule *module; +} LoaderInfo; + +/* Signals */ +enum +{ +	ACTIVATE_PLUGIN, +	DEACTIVATE_PLUGIN, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE(GeditPluginsEngine, gedit_plugins_engine, G_TYPE_OBJECT) + +struct _GeditPluginsEnginePrivate +{ +	GList *plugin_list; +	GHashTable *loaders; + +	gboolean activate_from_prefs; +}; + +GeditPluginsEngine *default_engine = NULL; + +static void	gedit_plugins_engine_activate_plugin_real (GeditPluginsEngine *engine, +							   GeditPluginInfo    *info); +static void	gedit_plugins_engine_deactivate_plugin_real (GeditPluginsEngine *engine, +							     GeditPluginInfo    *info); + +typedef gboolean (*LoadDirCallback)(GeditPluginsEngine *engine, const gchar *filename, gpointer userdata); + +static gboolean +load_dir_real (GeditPluginsEngine *engine, +	       const gchar        *dir, +	       const gchar        *suffix, +	       LoadDirCallback     callback, +	       gpointer            userdata) +{ +	GError *error = NULL; +	GDir *d; +	const gchar *dirent; +	gboolean ret = TRUE; + +	g_return_val_if_fail (dir != NULL, TRUE); + +	gedit_debug_message (DEBUG_PLUGINS, "DIR: %s", dir); + +	d = g_dir_open (dir, 0, &error); +	if (!d) +	{ +		g_warning ("%s", error->message); +		g_error_free (error); +		return TRUE; +	} +	 +	while ((dirent = g_dir_read_name (d))) +	{ +		gchar *filename; + +		if (!g_str_has_suffix (dirent, suffix)) +			continue; + +		filename = g_build_filename (dir, dirent, NULL); + +		ret = callback (engine, filename, userdata); + +		g_free (filename); + +		if (!ret) +			break; +	} + +	g_dir_close (d); +	return ret; +} + +static gboolean +load_plugin_info (GeditPluginsEngine *engine, +		  const gchar        *filename, +		  gpointer            userdata) +{ +	GeditPluginInfo *info; +	 +	info = _gedit_plugin_info_new (filename); + +	if (info == NULL) +		return TRUE; + +	/* If a plugin with this name has already been loaded +	 * drop this one (user plugins override system plugins) */ +	if (gedit_plugins_engine_get_plugin_info (engine, gedit_plugin_info_get_module_name (info)) != NULL) +	{ +		gedit_debug_message (DEBUG_PLUGINS, "Two or more plugins named '%s'. " +				     "Only the first will be considered.\n", +				     gedit_plugin_info_get_module_name (info)); + +		_gedit_plugin_info_unref (info); + +		return TRUE; +	} + +	engine->priv->plugin_list = g_list_prepend (engine->priv->plugin_list, info); + +	gedit_debug_message (DEBUG_PLUGINS, "Plugin %s loaded", info->name); +	return TRUE; +} + +static void +load_all_plugins (GeditPluginsEngine *engine) +{ +	gchar *plugin_dir; +	const gchar *pdirs_env = NULL; + +	/* load user plugins */ +	plugin_dir = gedit_dirs_get_user_plugins_dir (); +	if (g_file_test (plugin_dir, G_FILE_TEST_IS_DIR)) +	{ +		load_dir_real (engine, +			       plugin_dir, +			       PLUGIN_EXT, +			       load_plugin_info, +			       NULL); + +	} +	g_free (plugin_dir); + +	/* load system plugins */ +	pdirs_env = g_getenv ("GEDIT_PLUGINS_PATH"); + +	gedit_debug_message (DEBUG_PLUGINS, "GEDIT_PLUGINS_PATH=%s", pdirs_env); + +	if (pdirs_env != NULL) +	{ +		gchar **pdirs; +		gint i; + +		pdirs = g_strsplit (pdirs_env, G_SEARCHPATH_SEPARATOR_S, 0); + +		for (i = 0; pdirs[i] != NULL; i++) +		{ +			if (!load_dir_real (engine, +					    pdirs[i], +					    PLUGIN_EXT, +					    load_plugin_info, +					    NULL)) +			{ +				break; +			} +		} + +		g_strfreev (pdirs); +	} +	else +	{ +		plugin_dir = gedit_dirs_get_gedit_plugins_dir (); + +		load_dir_real (engine, +			       plugin_dir, +			       PLUGIN_EXT, +			       load_plugin_info, +			       NULL); + +		g_free (plugin_dir); +	} +} + +static guint +hash_lowercase (gconstpointer data) +{ +	gchar *lowercase; +	guint ret; +	 +	lowercase = g_ascii_strdown ((const gchar *)data, -1); +	ret = g_str_hash (lowercase); +	g_free (lowercase); +	 +	return ret; +} + +static gboolean +equal_lowercase (gconstpointer a, gconstpointer b) +{ +	return g_ascii_strcasecmp ((const gchar *)a, (const gchar *)b) == 0; +} + +static void +loader_destroy (LoaderInfo *info) +{ +	if (!info) +		return; +	 +	if (info->loader) +		g_object_unref (info->loader); +	 +	g_free (info); +} + +static void +add_loader (GeditPluginsEngine *engine, +	    const gchar        *loader_id, +	    GeditObjectModule  *module) +{ +	LoaderInfo *info; + +	info = g_new (LoaderInfo, 1); +	info->loader = NULL; +	info->module = module; + +	g_hash_table_insert (engine->priv->loaders, g_strdup (loader_id), info); +} + +static void +gedit_plugins_engine_init (GeditPluginsEngine *engine) +{ +	gedit_debug (DEBUG_PLUGINS); + +	if (!g_module_supported ()) +	{ +		g_warning ("gedit is not able to initialize the plugins engine."); +		return; +	} + +	engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, +						    GEDIT_TYPE_PLUGINS_ENGINE, +						    GeditPluginsEnginePrivate); + +	load_all_plugins (engine); + +	/* make sure that the first reactivation will read active plugins +	   from the prefs */ +	engine->priv->activate_from_prefs = TRUE; + +	/* mapping from loadername -> loader object */ +	engine->priv->loaders = g_hash_table_new_full (hash_lowercase, +						       equal_lowercase, +						       (GDestroyNotify)g_free, +						       (GDestroyNotify)loader_destroy); +} + +static void +loader_garbage_collect (const char *id, LoaderInfo *info) +{ +	if (info->loader) +		gedit_plugin_loader_garbage_collect (info->loader); +} + +void +gedit_plugins_engine_garbage_collect (GeditPluginsEngine *engine) +{ +	g_hash_table_foreach (engine->priv->loaders, +			      (GHFunc) loader_garbage_collect, +			      NULL); +} + +static void +gedit_plugins_engine_finalize (GObject *object) +{ +	GeditPluginsEngine *engine = GEDIT_PLUGINS_ENGINE (object); +	GList *item; +	 +	gedit_debug (DEBUG_PLUGINS); + +	/* Firs deactivate all plugins */ +	for (item = engine->priv->plugin_list; item; item = item->next) +	{ +		GeditPluginInfo *info = GEDIT_PLUGIN_INFO (item->data); +		 +		if (gedit_plugin_info_is_active (info)) +			gedit_plugins_engine_deactivate_plugin_real (engine, info); +	} +	 +	/* unref the loaders */	 +	g_hash_table_destroy (engine->priv->loaders); + +	/* and finally free the infos */ +	for (item = engine->priv->plugin_list; item; item = item->next) +	{ +		GeditPluginInfo *info = GEDIT_PLUGIN_INFO (item->data); + +		_gedit_plugin_info_unref (info); +	} + +	g_list_free (engine->priv->plugin_list); + +	G_OBJECT_CLASS (gedit_plugins_engine_parent_class)->finalize (object); +} + +static void +gedit_plugins_engine_class_init (GeditPluginsEngineClass *klass) +{ +	GType the_type = G_TYPE_FROM_CLASS (klass); +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_plugins_engine_finalize; +	klass->activate_plugin = gedit_plugins_engine_activate_plugin_real; +	klass->deactivate_plugin = gedit_plugins_engine_deactivate_plugin_real; + +	signals[ACTIVATE_PLUGIN] = +		g_signal_new ("activate-plugin", +			      the_type, +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditPluginsEngineClass, activate_plugin), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__BOXED, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_PLUGIN_INFO | G_SIGNAL_TYPE_STATIC_SCOPE); + +	signals[DEACTIVATE_PLUGIN] = +		g_signal_new ("deactivate-plugin", +			      the_type, +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditPluginsEngineClass, deactivate_plugin), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__BOXED, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_PLUGIN_INFO | G_SIGNAL_TYPE_STATIC_SCOPE); + +	g_type_class_add_private (klass, sizeof (GeditPluginsEnginePrivate)); +} + +static gboolean +load_loader (GeditPluginsEngine *engine, +	     const gchar        *filename, +	     gpointer		 data) +{ +	GeditObjectModule *module; +	gchar *base; +	gchar *path; +	const gchar *id; +	GType type; +	 +	/* try to load in the module */ +	path = g_path_get_dirname (filename); +	base = g_path_get_basename (filename); + +	/* for now they are all resident */ +	module = gedit_object_module_new (base, +					  path, +					  "register_gedit_plugin_loader", +					  TRUE); + +	g_free (base); +	g_free (path); + +	/* make sure to load the type definition */ +	if (!g_type_module_use (G_TYPE_MODULE (module))) +	{ +		g_object_unref (module); +		g_warning ("Plugin loader module `%s' could not be loaded", filename); + +		return TRUE; +	} + +	/* get the exported type and check the name as exported by the  +	 * loader interface */ +	type = gedit_object_module_get_object_type (module); +	id = gedit_plugin_loader_type_get_id (type); +	 +	add_loader (engine, id, module);	 +	g_type_module_unuse (G_TYPE_MODULE (module)); + +	return TRUE; +} + +static void +ensure_loader (LoaderInfo *info) +{ +	if (info->loader == NULL && info->module != NULL) +	{ +		/* create a new loader object */ +		GeditPluginLoader *loader; +		loader = (GeditPluginLoader *)gedit_object_module_new_object (info->module, NULL); +	 +		if (loader == NULL || !GEDIT_IS_PLUGIN_LOADER (loader)) +		{ +			g_warning ("Loader object is not a valid GeditPluginLoader instance"); +		 +			if (loader != NULL && G_IS_OBJECT (loader)) +				g_object_unref (loader); +		} +		else +		{ +			info->loader = loader; +		} +	} +} + +static GeditPluginLoader * +get_plugin_loader (GeditPluginsEngine *engine, GeditPluginInfo *info) +{ +	const gchar *loader_id; +	LoaderInfo *loader_info; + +	loader_id = info->loader; + +	loader_info = (LoaderInfo *)g_hash_table_lookup ( +			engine->priv->loaders,  +			loader_id); + +	if (loader_info == NULL) +	{ +		gchar *loader_dir; + +		loader_dir = gedit_dirs_get_gedit_plugin_loaders_dir (); + +		/* loader could not be found in the hash, try to find it by  +		   scanning */ +		load_dir_real (engine,  +			       loader_dir, +			       LOADER_EXT, +			       (LoadDirCallback)load_loader, +			       NULL); +		g_free (loader_dir); +		 +		loader_info = (LoaderInfo *)g_hash_table_lookup ( +				engine->priv->loaders,  +				loader_id); +	} + +	if (loader_info == NULL) +	{ +		/* cache non-existent so we don't scan again */ +		add_loader (engine, loader_id, NULL); +		return NULL; +	} +	 +	ensure_loader (loader_info); +	return loader_info->loader; +} + +GeditPluginsEngine * +gedit_plugins_engine_get_default (void) +{ +	if (default_engine != NULL) +		return default_engine; + +	default_engine = GEDIT_PLUGINS_ENGINE (g_object_new (GEDIT_TYPE_PLUGINS_ENGINE, NULL)); +	g_object_add_weak_pointer (G_OBJECT (default_engine), +				   (gpointer) &default_engine); +	return default_engine; +} + +const GList * +gedit_plugins_engine_get_plugin_list (GeditPluginsEngine *engine) +{ +	gedit_debug (DEBUG_PLUGINS); + +	return engine->priv->plugin_list; +} + +static gint +compare_plugin_info_and_name (GeditPluginInfo *info, +			      const gchar *module_name) +{ +	return strcmp (gedit_plugin_info_get_module_name (info), module_name); +} + +GeditPluginInfo * +gedit_plugins_engine_get_plugin_info (GeditPluginsEngine *engine, +				      const gchar        *name) +{ +	GList *l = g_list_find_custom (engine->priv->plugin_list, +				       name, +				       (GCompareFunc) compare_plugin_info_and_name); + +	return l == NULL ? NULL : (GeditPluginInfo *) l->data; +} + +static void +save_active_plugin_list (GeditPluginsEngine *engine) +{ +	GSList *active_plugins = NULL; +	GList *l; + +	for (l = engine->priv->plugin_list; l != NULL; l = l->next) +	{ +		GeditPluginInfo *info = (GeditPluginInfo *) l->data; + +		if (gedit_plugin_info_is_active (info)) +		{ +			active_plugins = g_slist_prepend (active_plugins, +							  (gpointer)gedit_plugin_info_get_module_name (info)); +		} +	} + +	gedit_prefs_manager_set_active_plugins (active_plugins); + +	g_slist_free (active_plugins); +} + +static gboolean +load_plugin (GeditPluginsEngine *engine, +	     GeditPluginInfo    *info) +{ +	GeditPluginLoader *loader; +	gchar *path; + +	if (gedit_plugin_info_is_active (info)) +		return TRUE; +	 +	if (!gedit_plugin_info_is_available (info)) +		return FALSE; + +	loader = get_plugin_loader (engine, info); +	 +	if (loader == NULL) +	{ +		g_warning ("Could not find loader `%s' for plugin `%s'", info->loader, info->name); +		info->available = FALSE; +		return FALSE; +	} +	 +	path = g_path_get_dirname (info->file); +	g_return_val_if_fail (path != NULL, FALSE); + +	info->plugin = gedit_plugin_loader_load (loader, info, path); +	 +	g_free (path); +	 +	if (info->plugin == NULL) +	{ +		g_warning ("Error loading plugin '%s'", info->name); +		info->available = FALSE; +		return FALSE; +	} +	 +	return TRUE; +} + +static void +gedit_plugins_engine_activate_plugin_real (GeditPluginsEngine *engine, +					   GeditPluginInfo *info) +{ +	const GList *wins; + +	if (!load_plugin (engine, info)) +		return; + +	for (wins = gedit_app_get_windows (gedit_app_get_default ()); +	     wins != NULL; +	     wins = wins->next) +	{ +		gedit_plugin_activate (info->plugin, GEDIT_WINDOW (wins->data)); +	} +} + +gboolean +gedit_plugins_engine_activate_plugin (GeditPluginsEngine *engine, +				      GeditPluginInfo    *info) +{ +	gedit_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (!gedit_plugin_info_is_available (info)) +		return FALSE; +		 +	if (gedit_plugin_info_is_active (info)) +		return TRUE; + +	g_signal_emit (engine, signals[ACTIVATE_PLUGIN], 0, info); + +	if (gedit_plugin_info_is_active (info)) +		save_active_plugin_list (engine); + +	return gedit_plugin_info_is_active (info); +} + +static void +call_plugin_deactivate (GeditPlugin *plugin,  +			GeditWindow *window) +{ +	gedit_plugin_deactivate (plugin, window); + +	/* ensure update of ui manager, because we suspect it does something +	   with expected static strings in the type module (when unloaded the +	   strings don't exist anymore, and ui manager updates in an idle +	   func) */ +	gtk_ui_manager_ensure_update (gedit_window_get_ui_manager (window)); +} + +static void +gedit_plugins_engine_deactivate_plugin_real (GeditPluginsEngine *engine, +					     GeditPluginInfo *info) +{ +	const GList *wins; +	GeditPluginLoader *loader; + +	if (!gedit_plugin_info_is_active (info) ||  +	    !gedit_plugin_info_is_available (info)) +		return; + +	for (wins = gedit_app_get_windows (gedit_app_get_default ()); +	     wins != NULL; +	     wins = wins->next) +	{ +		call_plugin_deactivate (info->plugin, GEDIT_WINDOW (wins->data)); +	} + +	/* first unref the plugin (the loader still has one) */ +	g_object_unref (info->plugin); +	 +	/* find the loader and tell it to gc and unload the plugin */ +	loader = get_plugin_loader (engine, info); +	 +	gedit_plugin_loader_garbage_collect (loader); +	gedit_plugin_loader_unload (loader, info); +	 +	info->plugin = NULL; +} + +gboolean +gedit_plugins_engine_deactivate_plugin (GeditPluginsEngine *engine, +					GeditPluginInfo    *info) +{ +	gedit_debug (DEBUG_PLUGINS); + +	g_return_val_if_fail (info != NULL, FALSE); + +	if (!gedit_plugin_info_is_active (info)) +		return TRUE; + +	g_signal_emit (engine, signals[DEACTIVATE_PLUGIN], 0, info); +	if (!gedit_plugin_info_is_active (info)) +		save_active_plugin_list (engine); + +	return !gedit_plugin_info_is_active (info); +} + +void +gedit_plugins_engine_activate_plugins (GeditPluginsEngine *engine, +					GeditWindow        *window) +{ +	GSList *active_plugins = NULL; +	GList *pl; + +	gedit_debug (DEBUG_PLUGINS); + +	g_return_if_fail (GEDIT_IS_PLUGINS_ENGINE (engine)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	/* the first time, we get the 'active' plugins from mateconf */ +	if (engine->priv->activate_from_prefs) +	{ +		active_plugins = gedit_prefs_manager_get_active_plugins (); +	} + +	for (pl = engine->priv->plugin_list; pl; pl = pl->next) +	{ +		GeditPluginInfo *info = (GeditPluginInfo*)pl->data; +		 +		if (engine->priv->activate_from_prefs &&  +		    g_slist_find_custom (active_plugins, +					 gedit_plugin_info_get_module_name (info), +					 (GCompareFunc)strcmp) == NULL) +			continue; +		 +		/* If plugin is not active, don't try to activate/load it */ +		if (!engine->priv->activate_from_prefs &&  +		    !gedit_plugin_info_is_active (info)) +			continue; + +		if (load_plugin (engine, info)) +			gedit_plugin_activate (info->plugin, +					       window); +	} +	 +	if (engine->priv->activate_from_prefs) +	{ +		g_slist_foreach (active_plugins, (GFunc) g_free, NULL); +		g_slist_free (active_plugins); +		engine->priv->activate_from_prefs = FALSE; +	} +	 +	gedit_debug_message (DEBUG_PLUGINS, "End"); + +	/* also call update_ui after activation */ +	gedit_plugins_engine_update_plugins_ui (engine, window); +} + +void +gedit_plugins_engine_deactivate_plugins (GeditPluginsEngine *engine, +					  GeditWindow        *window) +{ +	GList *pl; +	 +	gedit_debug (DEBUG_PLUGINS); + +	g_return_if_fail (GEDIT_IS_PLUGINS_ENGINE (engine)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	 +	for (pl = engine->priv->plugin_list; pl; pl = pl->next) +	{ +		GeditPluginInfo *info = (GeditPluginInfo*)pl->data; +		 +		/* check if the plugin is actually active */ +		if (!gedit_plugin_info_is_active (info)) +			continue; +		 +		/* call deactivate for the plugin for this window */ +		gedit_plugin_deactivate (info->plugin, window); +	} +	 +	gedit_debug_message (DEBUG_PLUGINS, "End"); +} + +void +gedit_plugins_engine_update_plugins_ui (GeditPluginsEngine *engine, +					 GeditWindow        *window) +{ +	GList *pl; + +	gedit_debug (DEBUG_PLUGINS); + +	g_return_if_fail (GEDIT_IS_PLUGINS_ENGINE (engine)); +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	/* call update_ui for all active plugins */ +	for (pl = engine->priv->plugin_list; pl; pl = pl->next) +	{ +		GeditPluginInfo *info = (GeditPluginInfo*)pl->data; + +		if (!gedit_plugin_info_is_active (info)) +			continue; +			 +	       	gedit_debug_message (DEBUG_PLUGINS, "Updating UI of %s", info->name); +		gedit_plugin_update_ui (info->plugin, window); +	} +} + +void 	  +gedit_plugins_engine_configure_plugin (GeditPluginsEngine *engine, +				       GeditPluginInfo    *info, +				       GtkWindow          *parent) +{ +	GtkWidget *conf_dlg; +	 +	GtkWindowGroup *wg; +	 +	gedit_debug (DEBUG_PLUGINS); + +	g_return_if_fail (info != NULL); + +	conf_dlg = gedit_plugin_create_configure_dialog (info->plugin); +	g_return_if_fail (conf_dlg != NULL); +	gtk_window_set_transient_for (GTK_WINDOW (conf_dlg), +				      parent); + +	wg = gtk_window_get_group (parent); +	if (wg == NULL) +	{ +		wg = gtk_window_group_new (); +		gtk_window_group_add_window (wg, parent); +	} +			 +	gtk_window_group_add_window (wg, +				     GTK_WINDOW (conf_dlg)); +		 +	gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE);		      +	gtk_widget_show (conf_dlg); +} + +void  +gedit_plugins_engine_active_plugins_changed (GeditPluginsEngine *engine) +{ +	gboolean to_activate; +	GSList *active_plugins; +	GList *pl; + +	gedit_debug (DEBUG_PLUGINS); + +	active_plugins = gedit_prefs_manager_get_active_plugins (); + +	for (pl = engine->priv->plugin_list; pl; pl = pl->next) +	{ +		GeditPluginInfo *info = (GeditPluginInfo*)pl->data; + +		if (!gedit_plugin_info_is_available (info)) +			continue; + +		to_activate = (g_slist_find_custom (active_plugins, +						    gedit_plugin_info_get_module_name (info), +						    (GCompareFunc)strcmp) != NULL); + +		if (!gedit_plugin_info_is_active (info) && to_activate) +			g_signal_emit (engine, signals[ACTIVATE_PLUGIN], 0, info); +		else if (gedit_plugin_info_is_active (info) && !to_activate) +			g_signal_emit (engine, signals[DEACTIVATE_PLUGIN], 0, info); +	} + +	g_slist_foreach (active_plugins, (GFunc) g_free, NULL); +	g_slist_free (active_plugins); +} + +void +gedit_plugins_engine_rescan_plugins (GeditPluginsEngine *engine) +{ +	gedit_debug (DEBUG_PLUGINS); +	 +	load_all_plugins (engine); +} diff --git a/gedit/gedit-plugins-engine.h b/gedit/gedit-plugins-engine.h new file mode 100755 index 00000000..f2ebff63 --- /dev/null +++ b/gedit/gedit-plugins-engine.h @@ -0,0 +1,107 @@ +/* + * gedit-plugins-engine.h + * This file is part of gedit + * + * Copyright (C) 2002-2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PLUGINS_ENGINE_H__ +#define __GEDIT_PLUGINS_ENGINE_H__ + +#include <glib.h> +#include "gedit-window.h" +#include "gedit-plugin-info.h" +#include "gedit-plugin.h" + +G_BEGIN_DECLS + +#define GEDIT_TYPE_PLUGINS_ENGINE              (gedit_plugins_engine_get_type ()) +#define GEDIT_PLUGINS_ENGINE(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PLUGINS_ENGINE, GeditPluginsEngine)) +#define GEDIT_PLUGINS_ENGINE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PLUGINS_ENGINE, GeditPluginsEngineClass)) +#define GEDIT_IS_PLUGINS_ENGINE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PLUGINS_ENGINE)) +#define GEDIT_IS_PLUGINS_ENGINE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PLUGINS_ENGINE)) +#define GEDIT_PLUGINS_ENGINE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PLUGINS_ENGINE, GeditPluginsEngineClass)) + +typedef struct _GeditPluginsEngine		GeditPluginsEngine; +typedef struct _GeditPluginsEnginePrivate	GeditPluginsEnginePrivate; + +struct _GeditPluginsEngine +{ +	GObject parent; +	GeditPluginsEnginePrivate *priv; +}; + +typedef struct _GeditPluginsEngineClass		GeditPluginsEngineClass; + +struct _GeditPluginsEngineClass +{ +	GObjectClass parent_class; + +	void	 (* activate_plugin)		(GeditPluginsEngine *engine, +						 GeditPluginInfo    *info); + +	void	 (* deactivate_plugin)		(GeditPluginsEngine *engine, +						 GeditPluginInfo    *info); +}; + +GType			 gedit_plugins_engine_get_type		(void) G_GNUC_CONST; + +GeditPluginsEngine	*gedit_plugins_engine_get_default	(void); + +void		 gedit_plugins_engine_garbage_collect	(GeditPluginsEngine *engine); + +const GList	*gedit_plugins_engine_get_plugin_list 	(GeditPluginsEngine *engine); + +GeditPluginInfo	*gedit_plugins_engine_get_plugin_info	(GeditPluginsEngine *engine, +							 const gchar        *name); + +/* plugin load and unloading (overall, for all windows) */ +gboolean 	 gedit_plugins_engine_activate_plugin 	(GeditPluginsEngine *engine, +							 GeditPluginInfo    *info); +gboolean 	 gedit_plugins_engine_deactivate_plugin	(GeditPluginsEngine *engine, +							 GeditPluginInfo    *info); + +void	 	 gedit_plugins_engine_configure_plugin	(GeditPluginsEngine *engine, +							 GeditPluginInfo    *info, +							 GtkWindow          *parent); + +/* plugin activation/deactivation per window, private to GeditWindow */ +void 		 gedit_plugins_engine_activate_plugins   (GeditPluginsEngine *engine, +							  GeditWindow        *window); +void 		 gedit_plugins_engine_deactivate_plugins (GeditPluginsEngine *engine, +							  GeditWindow        *window); +void		 gedit_plugins_engine_update_plugins_ui  (GeditPluginsEngine *engine, +							  GeditWindow        *window); + +/* private for mateconf notification */ +void		 gedit_plugins_engine_active_plugins_changed +							(GeditPluginsEngine *engine); + +void		 gedit_plugins_engine_rescan_plugins	(GeditPluginsEngine *engine); + +G_END_DECLS + +#endif  /* __GEDIT_PLUGINS_ENGINE_H__ */ diff --git a/gedit/gedit-prefs-manager-app.c b/gedit/gedit-prefs-manager-app.c new file mode 100755 index 00000000..ce35db8d --- /dev/null +++ b/gedit/gedit-prefs-manager-app.c @@ -0,0 +1,1620 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-prefs-manager.c + * This file is part of gedit + * + * Copyright (C) 2002-2005  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2003. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "gedit-prefs-manager.h" +#include "gedit-prefs-manager-private.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-app.h" +#include "gedit-debug.h" +#include "gedit-view.h" +#include "gedit-window.h" +#include "gedit-window-private.h" +#include "gedit-plugins-engine.h" +#include "gedit-style-scheme-manager.h" +#include "gedit-dirs.h" + +static void gedit_prefs_manager_editor_font_changed	(MateConfClient *client, +							 guint        cnxn_id, +							 MateConfEntry  *entry, +							 gpointer     user_data); + +static void gedit_prefs_manager_system_font_changed	(MateConfClient *client, +							 guint        cnxn_id, +							 MateConfEntry  *entry, +							 gpointer     user_data); + +static void gedit_prefs_manager_tabs_size_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_wrap_mode_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_line_numbers_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_auto_indent_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_undo_changed		(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_right_margin_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_smart_home_end_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_hl_current_line_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); +							  +static void gedit_prefs_manager_bracket_matching_changed(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_syntax_hl_enable_changed(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_search_hl_enable_changed(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_source_style_scheme_changed +							(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_max_recents_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_auto_save_changed	(MateConfClient *client, +							 guint        cnxn_id, +							 MateConfEntry  *entry, +							 gpointer     user_data); + +static void gedit_prefs_manager_active_plugins_changed	(MateConfClient *client, +							 guint        cnxn_id,  +							 MateConfEntry  *entry,  +							 gpointer     user_data); + +static void gedit_prefs_manager_lockdown_changed	(MateConfClient *client, +							 guint        cnxn_id, +							 MateConfEntry  *entry, +							 gpointer     user_data); + +/* GUI state is serialized to a .desktop file, not in mateconf */ + +#define GEDIT_STATE_DEFAULT_WINDOW_STATE	0 +#define GEDIT_STATE_DEFAULT_WINDOW_WIDTH	650 +#define GEDIT_STATE_DEFAULT_WINDOW_HEIGHT	500 +#define GEDIT_STATE_DEFAULT_SIDE_PANEL_SIZE	200 +#define GEDIT_STATE_DEFAULT_BOTTOM_PANEL_SIZE	140 + +#define GEDIT_STATE_FILE_LOCATION "gedit-2" + +#define GEDIT_STATE_WINDOW_GROUP "window" +#define GEDIT_STATE_WINDOW_STATE "state" +#define GEDIT_STATE_WINDOW_HEIGHT "height" +#define GEDIT_STATE_WINDOW_WIDTH "width" +#define GEDIT_STATE_SIDE_PANEL_SIZE "side_panel_size" +#define GEDIT_STATE_BOTTOM_PANEL_SIZE "bottom_panel_size" +#define GEDIT_STATE_SIDE_PANEL_ACTIVE_PAGE "side_panel_active_page" +#define GEDIT_STATE_BOTTOM_PANEL_ACTIVE_PAGE "bottom_panel_active_page" + +#define GEDIT_STATE_FILEFILTER_GROUP "filefilter" +#define GEDIT_STATE_FILEFILTER_ID "id" + +static gint window_state = -1; +static gint window_height = -1; +static gint window_width = -1; +static gint side_panel_size = -1; +static gint bottom_panel_size = -1; +static gint side_panel_active_page = -1; +static gint bottom_panel_active_page = -1; +static gint active_file_filter = -1; + + +static gchar * +get_state_filename (void) +{ +	gchar *config_dir; +	gchar *filename = NULL; + +	config_dir = gedit_dirs_get_user_config_dir (); + +	if (config_dir != NULL) +	{ +		filename = g_build_filename (config_dir, +					     GEDIT_STATE_FILE_LOCATION, +					     NULL); +		g_free (config_dir); +	} + +	return filename; +} + +static GKeyFile * +get_gedit_state_file (void) +{ +	static GKeyFile *state_file = NULL; + +	if (state_file == NULL) +	{ +		gchar *filename; +		GError *err = NULL; + +		state_file = g_key_file_new (); + +		filename = get_state_filename (); + +		if (!g_key_file_load_from_file (state_file, +						filename, +						G_KEY_FILE_NONE, +						&err)) +		{ +			if (err->domain != G_FILE_ERROR || +			    err->code != G_FILE_ERROR_NOENT) +			{ +				g_warning ("Could not load gedit state file: %s\n", +					   err->message); +			} + +			g_error_free (err); +		} + +		g_free (filename); +	} + +	return state_file; +} + +static void +gedit_state_get_int (const gchar *group, +		     const gchar *key, +		     gint         defval, +		     gint        *result) +{ +	GKeyFile *state_file; +	gint res; +	GError *err = NULL; + +	state_file = get_gedit_state_file (); +	res = g_key_file_get_integer (state_file, +				      group, +				      key, +				      &err); + +	if (err != NULL) +	{ +		if ((err->domain != G_KEY_FILE_ERROR) || +		    ((err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND && +		      err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND))) +		{ +			g_warning ("Could not get state value %s::%s : %s\n", +				   group, +				   key, +				   err->message); +		} + +		*result = defval; +		g_error_free (err); +	} +	else +	{ +		*result = res; +	} +} + +static void +gedit_state_set_int (const gchar *group, +		     const gchar *key, +		     gint         value) +{ +	GKeyFile *state_file; + +	state_file = get_gedit_state_file (); +	g_key_file_set_integer (state_file, +				group, +				key, +				value); +} + +static gboolean +gedit_state_file_sync (void) +{ +	GKeyFile *state_file; +	gchar *config_dir; +	gchar *filename = NULL; +	gchar *content = NULL; +	gsize length; +	gint res; +	GError *err = NULL; +	gboolean ret = FALSE; + +	state_file = get_gedit_state_file (); +	g_return_val_if_fail (state_file != NULL, FALSE); + +	config_dir = gedit_dirs_get_user_config_dir (); +	if (config_dir == NULL) +	{ +		g_warning ("Could not get config directory\n"); +		return ret; +	} + +	res = g_mkdir_with_parents (config_dir, 0755); +	if (res < 0) +	{ +		g_warning ("Could not create config directory\n"); +		goto out; +	} + +	content = g_key_file_to_data (state_file, +				      &length, +				      &err); + +	if (err != NULL) +	{ +		g_warning ("Could not get data from state file: %s\n", +			   err->message); +		goto out; +	} + +	if (content != NULL) +	{ +		filename = get_state_filename (); +		if (!g_file_set_contents (filename, +					  content, +					  length, +					  &err)) +		{ +			g_warning ("Could not write gedit state file: %s\n", +				   err->message); +			goto out; +		} +	} + +	ret = TRUE; + + out: +	if (err != NULL) +		g_error_free (err); + +	g_free (config_dir); +	g_free (filename); +	g_free (content); + +	return ret; +} + +/* Window state */ +gint +gedit_prefs_manager_get_window_state (void) +{ +	if (window_state == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_WINDOW_STATE, +				     GEDIT_STATE_DEFAULT_WINDOW_STATE, +				     &window_state); +	} + +	return window_state; +} +			 +void +gedit_prefs_manager_set_window_state (gint ws) +{ +	g_return_if_fail (ws > -1); +	 +	window_state = ws; + +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_WINDOW_STATE, +			     ws); +} + +gboolean +gedit_prefs_manager_window_state_can_set (void) +{ +	return TRUE; +} + +/* Window size */ +void +gedit_prefs_manager_get_window_size (gint *width, gint *height) +{ +	g_return_if_fail (width != NULL && height != NULL); + +	if (window_width == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_WINDOW_WIDTH, +				     GEDIT_STATE_DEFAULT_WINDOW_WIDTH, +				     &window_width); +	} + +	if (window_height == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_WINDOW_HEIGHT, +				     GEDIT_STATE_DEFAULT_WINDOW_HEIGHT, +				     &window_height); +	} + +	*width = window_width; +	*height = window_height; +} + +void +gedit_prefs_manager_get_default_window_size (gint *width, gint *height) +{ +	g_return_if_fail (width != NULL && height != NULL); + +	*width = GEDIT_STATE_DEFAULT_WINDOW_WIDTH; +	*height = GEDIT_STATE_DEFAULT_WINDOW_HEIGHT; +} + +void +gedit_prefs_manager_set_window_size (gint width, gint height) +{ +	g_return_if_fail (width > -1 && height > -1); + +	window_width = width; +	window_height = height; + +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_WINDOW_WIDTH, +			     width); +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_WINDOW_HEIGHT, +			     height); +} + +gboolean  +gedit_prefs_manager_window_size_can_set (void) +{ +	return TRUE; +} + +/* Side panel */ +gint +gedit_prefs_manager_get_side_panel_size (void) +{ +	if (side_panel_size == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_SIDE_PANEL_SIZE, +				     GEDIT_STATE_DEFAULT_SIDE_PANEL_SIZE, +				     &side_panel_size); +	} + +	return side_panel_size; +} + +gint  +gedit_prefs_manager_get_default_side_panel_size (void) +{ +	return GEDIT_STATE_DEFAULT_SIDE_PANEL_SIZE; +} + +void  +gedit_prefs_manager_set_side_panel_size (gint ps) +{ +	g_return_if_fail (ps > -1); +	 +	if (side_panel_size == ps) +		return; +		 +	side_panel_size = ps; +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_SIDE_PANEL_SIZE, +			     ps); +} + +gboolean  +gedit_prefs_manager_side_panel_size_can_set (void) +{ +	return TRUE; +} + +gint +gedit_prefs_manager_get_side_panel_active_page (void) +{ +	if (side_panel_active_page == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_SIDE_PANEL_ACTIVE_PAGE, +				     0, +				     &side_panel_active_page); +	} + +	return side_panel_active_page; +} + +void +gedit_prefs_manager_set_side_panel_active_page (gint id) +{ +	if (side_panel_active_page == id) +		return; + +	side_panel_active_page = id; +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_SIDE_PANEL_ACTIVE_PAGE, +			     id); +} + +gboolean  +gedit_prefs_manager_side_panel_active_page_can_set (void) +{ +	return TRUE; +} + +/* Bottom panel */ +gint +gedit_prefs_manager_get_bottom_panel_size (void) +{ +	if (bottom_panel_size == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_BOTTOM_PANEL_SIZE, +				     GEDIT_STATE_DEFAULT_BOTTOM_PANEL_SIZE, +				     &bottom_panel_size); +	} + +	return bottom_panel_size; +} + +gint  +gedit_prefs_manager_get_default_bottom_panel_size (void) +{ +	return GEDIT_STATE_DEFAULT_BOTTOM_PANEL_SIZE; +} + +void  +gedit_prefs_manager_set_bottom_panel_size (gint ps) +{ +	g_return_if_fail (ps > -1); + +	if (bottom_panel_size == ps) +		return; +	 +	bottom_panel_size = ps; +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_BOTTOM_PANEL_SIZE, +			     ps); +} + +gboolean  +gedit_prefs_manager_bottom_panel_size_can_set (void) +{ +	return TRUE; +} + +gint +gedit_prefs_manager_get_bottom_panel_active_page (void) +{ +	if (bottom_panel_active_page == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_WINDOW_GROUP, +				     GEDIT_STATE_BOTTOM_PANEL_ACTIVE_PAGE, +				     0, +				     &bottom_panel_active_page); +	} + +	return bottom_panel_active_page; +} + +void +gedit_prefs_manager_set_bottom_panel_active_page (gint id) +{ +	if (bottom_panel_active_page == id) +		return; + +	bottom_panel_active_page = id; +	gedit_state_set_int (GEDIT_STATE_WINDOW_GROUP, +			     GEDIT_STATE_BOTTOM_PANEL_ACTIVE_PAGE, +			     id); +} + +gboolean  +gedit_prefs_manager_bottom_panel_active_page_can_set (void) +{ +	return TRUE; +} + +/* File filter */ +gint +gedit_prefs_manager_get_active_file_filter (void) +{ +	if (active_file_filter == -1) +	{ +		gedit_state_get_int (GEDIT_STATE_FILEFILTER_GROUP, +				     GEDIT_STATE_FILEFILTER_ID, +				     0, +				     &active_file_filter); +	} + +	return active_file_filter; +} + +void +gedit_prefs_manager_set_active_file_filter (gint id) +{ +	g_return_if_fail (id >= 0); +	 +	if (active_file_filter == id) +		return; + +	active_file_filter = id; +	gedit_state_set_int (GEDIT_STATE_FILEFILTER_GROUP, +			     GEDIT_STATE_FILEFILTER_ID, +			     id); +} + +gboolean  +gedit_prefs_manager_active_file_filter_can_set (void) +{ +	return TRUE; +} + +/* Normal prefs are stored in MateConf */ + +gboolean +gedit_prefs_manager_app_init (void) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager == NULL, FALSE); + +	gedit_prefs_manager_init (); + +	if (gedit_prefs_manager != NULL) +	{ +		/* TODO: notify, add dirs */ +		mateconf_client_add_dir (gedit_prefs_manager->mateconf_client, +				GPM_PREFS_DIR, +				MATECONF_CLIENT_PRELOAD_RECURSIVE, +				NULL); + +		mateconf_client_add_dir (gedit_prefs_manager->mateconf_client, +				GPM_PLUGINS_DIR, +				MATECONF_CLIENT_PRELOAD_RECURSIVE, +				NULL); +		 +		mateconf_client_add_dir (gedit_prefs_manager->mateconf_client, +				GPM_LOCKDOWN_DIR, +				MATECONF_CLIENT_PRELOAD_RECURSIVE, +				NULL); +		 +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_FONT_DIR, +				gedit_prefs_manager_editor_font_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SYSTEM_FONT, +				gedit_prefs_manager_system_font_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_TABS_DIR, +				gedit_prefs_manager_tabs_size_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_WRAP_MODE_DIR, +				gedit_prefs_manager_wrap_mode_changed, +				NULL, NULL, NULL); +		 +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_LINE_NUMBERS_DIR, +				gedit_prefs_manager_line_numbers_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_AUTO_INDENT_DIR, +				gedit_prefs_manager_auto_indent_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_UNDO_DIR, +				gedit_prefs_manager_undo_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_RIGHT_MARGIN_DIR, +				gedit_prefs_manager_right_margin_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SMART_HOME_END_DIR, +				gedit_prefs_manager_smart_home_end_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_CURRENT_LINE_DIR, +				gedit_prefs_manager_hl_current_line_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_BRACKET_MATCHING_DIR, +				gedit_prefs_manager_bracket_matching_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SYNTAX_HL_ENABLE, +				gedit_prefs_manager_syntax_hl_enable_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SEARCH_HIGHLIGHTING_ENABLE, +				gedit_prefs_manager_search_hl_enable_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SOURCE_STYLE_DIR, +				gedit_prefs_manager_source_style_scheme_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_MAX_RECENTS, +				gedit_prefs_manager_max_recents_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_SAVE_DIR, +				gedit_prefs_manager_auto_save_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_ACTIVE_PLUGINS, +				gedit_prefs_manager_active_plugins_changed, +				NULL, NULL, NULL); + +		mateconf_client_notify_add (gedit_prefs_manager->mateconf_client, +				GPM_LOCKDOWN_DIR, +				gedit_prefs_manager_lockdown_changed, +				NULL, NULL, NULL); +	} + +	return gedit_prefs_manager != NULL;	 +} + +/* This function must be called before exiting gedit */ +void +gedit_prefs_manager_app_shutdown (void) +{ +	gedit_debug (DEBUG_PREFS); + +	gedit_prefs_manager_shutdown (); + +	gedit_state_file_sync (); +} + + +static void  +gedit_prefs_manager_editor_font_changed (MateConfClient *client, +					 guint        cnxn_id,  +					 MateConfEntry  *entry,  +					 gpointer     user_data) +{ +	GList *views; +	GList *l; +	gchar *font = NULL; +	gboolean def = TRUE; +	gint ts; + +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_USE_DEFAULT_FONT) == 0) +	{ +		if (entry->value->type == MATECONF_VALUE_BOOL) +			def = mateconf_value_get_bool (entry->value); +		else +			def = GPM_DEFAULT_USE_DEFAULT_FONT; +		 +		if (def) +			font = gedit_prefs_manager_get_system_font (); +		else +			font = gedit_prefs_manager_get_editor_font (); +	} +	else if (strcmp (entry->key, GPM_EDITOR_FONT) == 0) +	{ +		if (entry->value->type == MATECONF_VALUE_STRING) +			font = g_strdup (mateconf_value_get_string (entry->value)); +		else +			font = g_strdup (GPM_DEFAULT_EDITOR_FONT); +				 +		def = gedit_prefs_manager_get_use_default_font (); +	} +	else +		return; + +	g_return_if_fail (font != NULL); +	 +	ts = gedit_prefs_manager_get_tabs_size (); + +	views = gedit_app_get_views (gedit_app_get_default ()); +	l = views; + +	while (l != NULL) +	{ +		/* Note: we use def=FALSE to avoid GeditView to query mateconf */ +		gedit_view_set_font (GEDIT_VIEW (l->data), FALSE,  font); +		gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (l->data), ts); + +		l = l->next; +	} + +	g_list_free (views); +	g_free (font); +} + +static void  +gedit_prefs_manager_system_font_changed (MateConfClient *client, +					 guint        cnxn_id,  +					 MateConfEntry  *entry,  +					 gpointer     user_data) +{ +	GList *views; +	GList *l; +	gchar *font; +	gint ts; + +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_SYSTEM_FONT) != 0) +		return; + +	if (!gedit_prefs_manager_get_use_default_font ()) +		return; + +	if (entry->value->type == MATECONF_VALUE_STRING) +		font = g_strdup (mateconf_value_get_string (entry->value)); +	else +		font = g_strdup (GPM_DEFAULT_SYSTEM_FONT); + +	ts = gedit_prefs_manager_get_tabs_size (); + +	views = gedit_app_get_views (gedit_app_get_default ()); +	l = views; + +	while (l != NULL) +	{ +		/* Note: we use def=FALSE to avoid GeditView to query mateconf */ +		gedit_view_set_font (GEDIT_VIEW (l->data), FALSE, font); + +		gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (l->data), ts); +		l = l->next; +	} + +	g_list_free (views); +	g_free (font); +} + +static void  +gedit_prefs_manager_tabs_size_changed (MateConfClient *client, +				       guint        cnxn_id, +				       MateConfEntry  *entry,  +				       gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_TABS_SIZE) == 0) +	{ +		gint tab_width; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_INT) +			tab_width = mateconf_value_get_int (entry->value); +		else +			tab_width = GPM_DEFAULT_TABS_SIZE; +	 +		tab_width = CLAMP (tab_width, 1, 24); + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (l->data),  +						       tab_width); + +			l = l->next; +		} + +		g_list_free (views); +	} +	else if (strcmp (entry->key, GPM_INSERT_SPACES) == 0) +	{ +		gboolean enable; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			enable = mateconf_value_get_bool (entry->value);	 +		else +			enable = GPM_DEFAULT_INSERT_SPACES; + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_insert_spaces_instead_of_tabs ( +					GTK_SOURCE_VIEW (l->data),  +					enable); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static GtkWrapMode  +get_wrap_mode_from_string (const gchar* str) +{ +	GtkWrapMode res; + +	g_return_val_if_fail (str != NULL, GTK_WRAP_WORD); +	 +	if (strcmp (str, "GTK_WRAP_NONE") == 0) +		res = GTK_WRAP_NONE; +	else +	{ +		if (strcmp (str, "GTK_WRAP_CHAR") == 0) +			res = GTK_WRAP_CHAR; +		else +			res = GTK_WRAP_WORD; +	} + +	return res; +} + +static void  +gedit_prefs_manager_wrap_mode_changed (MateConfClient *client, +	                               guint        cnxn_id,  +	                               MateConfEntry  *entry,  +	                               gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_WRAP_MODE) == 0) +	{ +		GtkWrapMode wrap_mode; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_STRING) +			wrap_mode =  +				get_wrap_mode_from_string (mateconf_value_get_string (entry->value));	 +		else +			wrap_mode = get_wrap_mode_from_string (GPM_DEFAULT_WRAP_MODE); + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (l->data), +						     wrap_mode); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static void  +gedit_prefs_manager_line_numbers_changed (MateConfClient *client, +					  guint        cnxn_id,  +					  MateConfEntry  *entry,  +					  gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_DISPLAY_LINE_NUMBERS) == 0) +	{ +		gboolean dln; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			dln = mateconf_value_get_bool (entry->value);	 +		else +			dln = GPM_DEFAULT_DISPLAY_LINE_NUMBERS; + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_show_line_numbers (GTK_SOURCE_VIEW (l->data),  +							       dln); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static void  +gedit_prefs_manager_hl_current_line_changed (MateConfClient *client, +					     guint        cnxn_id,  +					     MateConfEntry  *entry,  +					     gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_HIGHLIGHT_CURRENT_LINE) == 0) +	{ +		gboolean hl; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			hl = mateconf_value_get_bool (entry->value);	 +		else +			hl = GPM_DEFAULT_HIGHLIGHT_CURRENT_LINE; + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (l->data),  +								    hl); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static void  +gedit_prefs_manager_bracket_matching_changed (MateConfClient *client, +					      guint        cnxn_id,  +					      MateConfEntry  *entry,  +					      gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_BRACKET_MATCHING) == 0) +	{ +		gboolean enable; +		GList *docs; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			enable = mateconf_value_get_bool (entry->value); +		else +			enable = GPM_DEFAULT_BRACKET_MATCHING; + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; + +		while (l != NULL) +		{ +			gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (l->data), +									   enable); + +			l = l->next; +		} + +		g_list_free (docs); +	} +} + +static void  +gedit_prefs_manager_auto_indent_changed (MateConfClient *client, +					 guint        cnxn_id,  +					 MateConfEntry  *entry,  +					 gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_AUTO_INDENT) == 0) +	{ +		gboolean enable; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			enable = mateconf_value_get_bool (entry->value);	 +		else +			enable = GPM_DEFAULT_AUTO_INDENT; +	 +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{		 +			gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (l->data),  +							 enable); +			 +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static void  +gedit_prefs_manager_undo_changed (MateConfClient *client, +				  guint        cnxn_id,  +				  MateConfEntry  *entry,  +				  gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_UNDO_ACTIONS_LIMIT) == 0) +	{ +		gint ul; +		GList *docs; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_INT) +			ul = mateconf_value_get_int (entry->value); +		else +			ul = GPM_DEFAULT_UNDO_ACTIONS_LIMIT; +	 +		ul = CLAMP (ul, -1, 250); + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; +		 +		while (l != NULL) +		{ +			gtk_source_buffer_set_max_undo_levels (GTK_SOURCE_BUFFER (l->data),  +							       ul); + +			l = l->next; +		} + +		g_list_free (docs); +	} +} + +static void  +gedit_prefs_manager_right_margin_changed (MateConfClient *client, +					  guint cnxn_id, +					  MateConfEntry *entry, +					  gpointer user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_RIGHT_MARGIN_POSITION) == 0) +	{ +		gint pos; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_INT) +			pos = mateconf_value_get_int (entry->value); +		else +			pos = GPM_DEFAULT_RIGHT_MARGIN_POSITION; + +		pos = CLAMP (pos, 1, 160); + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_right_margin_position (GTK_SOURCE_VIEW (l->data), +								   pos); + +			l = l->next; +		} + +		g_list_free (views); +	} +	else if (strcmp (entry->key, GPM_DISPLAY_RIGHT_MARGIN) == 0) +	{ +		gboolean display; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			display = mateconf_value_get_bool (entry->value);	 +		else +			display = GPM_DEFAULT_DISPLAY_RIGHT_MARGIN; + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_show_right_margin (GTK_SOURCE_VIEW (l->data), +							       display); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static GtkSourceSmartHomeEndType +get_smart_home_end_from_string (const gchar *str) +{ +	GtkSourceSmartHomeEndType res; + +	g_return_val_if_fail (str != NULL, GTK_SOURCE_SMART_HOME_END_AFTER); + +	if (strcmp (str, "DISABLED") == 0) +		res = GTK_SOURCE_SMART_HOME_END_DISABLED; +	else if (strcmp (str, "BEFORE") == 0) +		res = GTK_SOURCE_SMART_HOME_END_BEFORE; +	else if (strcmp (str, "ALWAYS") == 0) +		res = GTK_SOURCE_SMART_HOME_END_ALWAYS; +	else +		res = GTK_SOURCE_SMART_HOME_END_AFTER; + +	return res; +} + +static void +gedit_prefs_manager_smart_home_end_changed (MateConfClient *client, +					    guint        cnxn_id, +					    MateConfEntry  *entry, +					    gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_SMART_HOME_END) == 0) +	{ +		GtkSourceSmartHomeEndType smart_he; +		GList *views; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_STRING) +			smart_he =  +				get_smart_home_end_from_string (mateconf_value_get_string (entry->value));	 +		else +			smart_he = get_smart_home_end_from_string (GPM_DEFAULT_SMART_HOME_END); + +		views = gedit_app_get_views (gedit_app_get_default ()); +		l = views; + +		while (l != NULL) +		{ +			gtk_source_view_set_smart_home_end (GTK_SOURCE_VIEW (l->data), +							    smart_he); + +			l = l->next; +		} + +		g_list_free (views); +	} +} + +static void +gedit_prefs_manager_syntax_hl_enable_changed (MateConfClient *client, +					      guint        cnxn_id, +					      MateConfEntry  *entry, +					      gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_SYNTAX_HL_ENABLE) == 0) +	{ +		gboolean enable; +		GList *docs; +		GList *l; +		const GList *windows; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			enable = mateconf_value_get_bool (entry->value); +		else +			enable = GPM_DEFAULT_SYNTAX_HL_ENABLE; + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; + +		while (l != NULL) +		{ +			g_return_if_fail (GTK_IS_SOURCE_BUFFER (l->data)); + +			gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (l->data), +								enable); + +			l = l->next; +		} + +		g_list_free (docs); + +		/* update the sensitivity of the Higlight Mode menu item */ +		windows = gedit_app_get_windows (gedit_app_get_default ()); +		while (windows != NULL) +		{ +			GtkUIManager *ui; +			GtkAction *a; + +			ui = gedit_window_get_ui_manager (GEDIT_WINDOW (windows->data)); + +			a = gtk_ui_manager_get_action (ui, +						       "/MenuBar/ViewMenu/ViewHighlightModeMenu"); + +			gtk_action_set_sensitive (a, enable); + +			windows = g_list_next (windows); +		} +	} +} + +static void +gedit_prefs_manager_search_hl_enable_changed (MateConfClient *client, +					      guint        cnxn_id, +					      MateConfEntry  *entry, +					      gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_SEARCH_HIGHLIGHTING_ENABLE) == 0) +	{ +		gboolean enable; +		GList *docs; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			enable = mateconf_value_get_bool (entry->value); +		else +			enable = GPM_DEFAULT_SEARCH_HIGHLIGHTING_ENABLE; + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; + +		while (l != NULL) +		{ +			g_return_if_fail (GEDIT_IS_DOCUMENT (l->data)); + +			gedit_document_set_enable_search_highlighting  (GEDIT_DOCUMENT (l->data), +									enable); + +			l = l->next; +		} + +		g_list_free (docs); +	} +} + +static void +gedit_prefs_manager_source_style_scheme_changed (MateConfClient *client, +						 guint        cnxn_id, +						 MateConfEntry  *entry, +						 gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_SOURCE_STYLE_SCHEME) == 0) +	{ +		static gchar *old_scheme = NULL; +		const gchar *scheme; +		GtkSourceStyleScheme *style; +		GList *docs; +		GList *l; + +		if (entry->value->type == MATECONF_VALUE_STRING) +			scheme = mateconf_value_get_string (entry->value); +		else +			scheme = GPM_DEFAULT_SOURCE_STYLE_SCHEME; + +		if (old_scheme != NULL && (strcmp (scheme, old_scheme) == 0)) +		    	return; + +		g_free (old_scheme); +		old_scheme = g_strdup (scheme); + +		style = gtk_source_style_scheme_manager_get_scheme ( +				gedit_get_style_scheme_manager (), +				scheme); + +		if (style == NULL) +		{ +			g_warning ("Default style scheme '%s' not found, falling back to 'classic'", scheme); +			 +			style = gtk_source_style_scheme_manager_get_scheme ( +				gedit_get_style_scheme_manager (), +				"classic"); + +			if (style == NULL)  +			{ +				g_warning ("Style scheme 'classic' cannot be found, check your GtkSourceView installation."); +				return; +			} +		} + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		for (l = docs; l != NULL; l = l->next) +		{ +			g_return_if_fail (GTK_IS_SOURCE_BUFFER (l->data)); + +			gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (l->data), +							    style); +		} + +		g_list_free (docs); +	} +} + +static void +gedit_prefs_manager_max_recents_changed (MateConfClient *client, +					 guint        cnxn_id, +					 MateConfEntry  *entry, +					 gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_MAX_RECENTS) == 0) +	{ +		const GList *windows; +		gint max; + +		if (entry->value->type == MATECONF_VALUE_INT) +		{ +			max = mateconf_value_get_int (entry->value); + +			if (max < 0) +				max = GPM_DEFAULT_MAX_RECENTS; +		} +		else +			max = GPM_DEFAULT_MAX_RECENTS; + +		windows = gedit_app_get_windows (gedit_app_get_default ()); +		while (windows != NULL) +		{ +			GeditWindow *w = windows->data; + +			gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (w->priv->toolbar_recent_menu), +						      max); + +			windows = g_list_next (windows); +		} + +		/* FIXME: we have no way at the moment to trigger the +		 * update of the inline recents in the File menu */ +	} +} + +static void +gedit_prefs_manager_auto_save_changed (MateConfClient *client, +				       guint        cnxn_id, +				       MateConfEntry  *entry, +				       gpointer     user_data) +{ +	GList *docs; +	GList *l; + +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_AUTO_SAVE) == 0) +	{ +		gboolean auto_save; + +		if (entry->value->type == MATECONF_VALUE_BOOL) +			auto_save = mateconf_value_get_bool (entry->value); +		else +			auto_save = GPM_DEFAULT_AUTO_SAVE; + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; + +		while (l != NULL) +		{ +			GeditDocument *doc = GEDIT_DOCUMENT (l->data); +			GeditTab *tab = gedit_tab_get_from_document (doc); + +			gedit_tab_set_auto_save_enabled (tab, auto_save); + +			l = l->next; +		} + +		g_list_free (docs); +	} +	else if (strcmp (entry->key,  GPM_AUTO_SAVE_INTERVAL) == 0) +	{ +		gint auto_save_interval; + +		if (entry->value->type == MATECONF_VALUE_INT) +		{ +			auto_save_interval = mateconf_value_get_int (entry->value); + +			if (auto_save_interval <= 0) +				auto_save_interval = GPM_DEFAULT_AUTO_SAVE_INTERVAL; +		} +		else +			auto_save_interval = GPM_DEFAULT_AUTO_SAVE_INTERVAL; + +		docs = gedit_app_get_documents (gedit_app_get_default ()); +		l = docs; + +		while (l != NULL) +		{ +			GeditDocument *doc = GEDIT_DOCUMENT (l->data); +			GeditTab *tab = gedit_tab_get_from_document (doc); + +			gedit_tab_set_auto_save_interval (tab, auto_save_interval); + +			l = l->next; +		} + +		g_list_free (docs); +	} +} + +static void  +gedit_prefs_manager_active_plugins_changed (MateConfClient *client, +					    guint        cnxn_id, +					    MateConfEntry  *entry, +					    gpointer     user_data) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (strcmp (entry->key, GPM_ACTIVE_PLUGINS) == 0) +	{ +		if ((entry->value->type == MATECONF_VALUE_LIST) &&  +		    (mateconf_value_get_list_type (entry->value) == MATECONF_VALUE_STRING)) +		{ +			GeditPluginsEngine *engine; + +			engine = gedit_plugins_engine_get_default (); + +			gedit_plugins_engine_active_plugins_changed (engine); +		} +	} +} + +static void +gedit_prefs_manager_lockdown_changed (MateConfClient *client, +				      guint        cnxn_id, +				      MateConfEntry  *entry, +				      gpointer     user_data) +{ +	GeditApp *app; +	gboolean locked; + +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (entry->key != NULL); +	g_return_if_fail (entry->value != NULL); + +	if (entry->value->type == MATECONF_VALUE_BOOL) +		locked = mateconf_value_get_bool (entry->value); +	else +		locked = FALSE; + +	app = gedit_app_get_default (); + +	if (strcmp (entry->key, GPM_LOCKDOWN_COMMAND_LINE) == 0) +		_gedit_app_set_lockdown_bit (app,  +					     GEDIT_LOCKDOWN_COMMAND_LINE, +					     locked); + +	else if (strcmp (entry->key, GPM_LOCKDOWN_PRINTING) == 0) +		_gedit_app_set_lockdown_bit (app,  +					     GEDIT_LOCKDOWN_PRINTING, +					     locked); + +	else if (strcmp (entry->key, GPM_LOCKDOWN_PRINT_SETUP) == 0) +		_gedit_app_set_lockdown_bit (app,  +					     GEDIT_LOCKDOWN_PRINT_SETUP, +					     locked); + +	else if (strcmp (entry->key, GPM_LOCKDOWN_SAVE_TO_DISK) == 0) +		_gedit_app_set_lockdown_bit (app,  +					     GEDIT_LOCKDOWN_SAVE_TO_DISK, +					     locked); +} diff --git a/gedit/gedit-prefs-manager-app.h b/gedit/gedit-prefs-manager-app.h new file mode 100755 index 00000000..592b643f --- /dev/null +++ b/gedit/gedit-prefs-manager-app.h @@ -0,0 +1,84 @@ +/* + * gedit-prefs-manager-app.h + * This file is part of gedit + * + * Copyright (C) 2002-2005  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + * + */ + +#ifndef __GEDIT_PREFS_MANAGER_APP_H__ +#define __GEDIT_PREFS_MANAGER_APP_H__ + +#include <glib.h> +#include <gedit/gedit-prefs-manager.h> + +/** LIFE CYCLE MANAGEMENT FUNCTIONS **/ + +gboolean	 gedit_prefs_manager_app_init			(void); + +/* This function must be called before exiting gedit */ +void		 gedit_prefs_manager_app_shutdown		(void); + + +/* Window state */ +gint		 gedit_prefs_manager_get_window_state		(void); +void 		 gedit_prefs_manager_set_window_state		(gint ws); +gboolean	 gedit_prefs_manager_window_state_can_set	(void); + +/* Window size */ +void		 gedit_prefs_manager_get_window_size		(gint *width, +								 gint *height); +void		 gedit_prefs_manager_get_default_window_size	(gint *width, +								 gint *height); +void 		 gedit_prefs_manager_set_window_size		(gint width, +								 gint height); +gboolean	 gedit_prefs_manager_window_size_can_set	(void); + +/* Side panel */ +gint	 	 gedit_prefs_manager_get_side_panel_size	(void); +gint	 	 gedit_prefs_manager_get_default_side_panel_size(void); +void 		 gedit_prefs_manager_set_side_panel_size	(gint ps); +gboolean	 gedit_prefs_manager_side_panel_size_can_set	(void); +gint		 gedit_prefs_manager_get_side_panel_active_page (void); +void 		 gedit_prefs_manager_set_side_panel_active_page (gint id); +gboolean	 gedit_prefs_manager_side_panel_active_page_can_set (void); + +/* Bottom panel */ +gint	 	 gedit_prefs_manager_get_bottom_panel_size	(void); +gint	 	 gedit_prefs_manager_get_default_bottom_panel_size(void); +void 		 gedit_prefs_manager_set_bottom_panel_size	(gint ps); +gboolean	 gedit_prefs_manager_bottom_panel_size_can_set	(void); +gint		 gedit_prefs_manager_get_bottom_panel_active_page (void); +void 		 gedit_prefs_manager_set_bottom_panel_active_page (gint id); +gboolean	 gedit_prefs_manager_bottom_panel_active_page_can_set (void); + +/* File filter */ +gint		 gedit_prefs_manager_get_active_file_filter	(void); +void 		 gedit_prefs_manager_set_active_file_filter	(gint id); +gboolean	 gedit_prefs_manager_active_file_filter_can_set	(void); + + +#endif /* __GEDIT_PREFS_MANAGER_APP_H__ */ diff --git a/gedit/gedit-prefs-manager-private.h b/gedit/gedit-prefs-manager-private.h new file mode 100755 index 00000000..7a8e94a5 --- /dev/null +++ b/gedit/gedit-prefs-manager-private.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-prefs-manager-private.h + * This file is part of gedit + * + * Copyright (C) 2002  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef __GEDIT_PREFS_MANAGER_PRIVATE_H__ +#define __GEDIT_PREFS_MANAGER_PRIVATE_H__ + +#include <mateconf/mateconf-client.h> + +typedef struct _GeditPrefsManager 	GeditPrefsManager; + +struct _GeditPrefsManager { +	MateConfClient *mateconf_client; +}; + +extern GeditPrefsManager *gedit_prefs_manager; + +#endif /* __GEDIT_PREFS_MANAGER_PRIVATE_H__ */ + + diff --git a/gedit/gedit-prefs-manager.c b/gedit/gedit-prefs-manager.c new file mode 100755 index 00000000..c606e748 --- /dev/null +++ b/gedit/gedit-prefs-manager.c @@ -0,0 +1,1241 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-prefs-manager.c + * This file is part of gedit + * + * Copyright (C) 2002  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> +#include <mateconf/mateconf-value.h> + +#include "gedit-prefs-manager.h" +#include "gedit-prefs-manager-private.h" +#include "gedit-debug.h" +#include "gedit-encodings.h" +#include "gedit-utils.h" + +#define DEFINE_BOOL_PREF(name, key, def) gboolean 			\ +gedit_prefs_manager_get_ ## name (void)					\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_get_bool (key,			\ +					     (def));			\ +}									\ +									\ +void 									\ +gedit_prefs_manager_set_ ## name (gboolean v)				\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	gedit_prefs_manager_set_bool (key,				\ +				      v);				\ +}									\ +				      					\ +gboolean								\ +gedit_prefs_manager_ ## name ## _can_set (void)				\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_key_is_writable (key);		\ +}	 + + + +#define DEFINE_INT_PREF(name, key, def) gint	 			\ +gedit_prefs_manager_get_ ## name (void)			 		\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_get_int (key,			\ +					    (def));			\ +}									\ +									\ +void 									\ +gedit_prefs_manager_set_ ## name (gint v)				\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	gedit_prefs_manager_set_int (key,				\ +				     v);				\ +}									\ +				      					\ +gboolean								\ +gedit_prefs_manager_ ## name ## _can_set (void)				\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_key_is_writable (key);		\ +}		 + + + +#define DEFINE_STRING_PREF(name, key, def) gchar*	 		\ +gedit_prefs_manager_get_ ## name (void)			 		\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_get_string (key,			\ +					       def);			\ +}									\ +									\ +void 									\ +gedit_prefs_manager_set_ ## name (const gchar* v)			\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	gedit_prefs_manager_set_string (key,				\ +				        v);				\ +}									\ +				      					\ +gboolean								\ +gedit_prefs_manager_ ## name ## _can_set (void)				\ +{									\ +	gedit_debug (DEBUG_PREFS);					\ +									\ +	return gedit_prefs_manager_key_is_writable (key);		\ +}		 + + +GeditPrefsManager *gedit_prefs_manager = NULL; + + +static GtkWrapMode 	 get_wrap_mode_from_string 		(const gchar* str); + +static gboolean 	 mateconf_client_get_bool_with_default 	(MateConfClient* client,  +								 const gchar* key,  +								 gboolean def,  +								 GError** err); + +static gchar		*mateconf_client_get_string_with_default 	(MateConfClient* client,  +								 const gchar* key, +								 const gchar* def,  +								 GError** err); + +static gint		 mateconf_client_get_int_with_default 	(MateConfClient* client,  +								 const gchar* key, +								 gint def,  +								 GError** err); + +static gboolean		 gedit_prefs_manager_get_bool		(const gchar* key,  +								 gboolean def); + +static gint		 gedit_prefs_manager_get_int		(const gchar* key,  +								 gint def); + +static gchar		*gedit_prefs_manager_get_string		(const gchar* key,  +								 const gchar* def); + + +gboolean +gedit_prefs_manager_init (void) +{ +	gedit_debug (DEBUG_PREFS); + +	if (gedit_prefs_manager == NULL) +	{ +		MateConfClient *mateconf_client; + +		mateconf_client = mateconf_client_get_default (); +		if (mateconf_client == NULL) +		{ +			g_warning (_("Cannot initialize preferences manager.")); +			return FALSE; +		} + +		gedit_prefs_manager = g_new0 (GeditPrefsManager, 1); + +		gedit_prefs_manager->mateconf_client = mateconf_client; +	} + +	if (gedit_prefs_manager->mateconf_client == NULL) +	{ +		g_free (gedit_prefs_manager); +		gedit_prefs_manager = NULL; +	} + +	return gedit_prefs_manager != NULL; +	 +} + +void +gedit_prefs_manager_shutdown (void) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (gedit_prefs_manager != NULL); + +	g_object_unref (gedit_prefs_manager->mateconf_client); +	gedit_prefs_manager->mateconf_client = NULL; +} + +static gboolean		  +gedit_prefs_manager_get_bool (const gchar* key, gboolean def) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, def); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, def); + +	return mateconf_client_get_bool_with_default (gedit_prefs_manager->mateconf_client, +						   key, +						   def, +						   NULL); +} + +static gint  +gedit_prefs_manager_get_int (const gchar* key, gint def) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, def); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, def); + +	return mateconf_client_get_int_with_default (gedit_prefs_manager->mateconf_client, +						  key, +						  def, +						  NULL); +}	 + +static gchar * +gedit_prefs_manager_get_string (const gchar* key, const gchar* def) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL,  +			      def ? g_strdup (def) : NULL); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL,  +			      def ? g_strdup (def) : NULL); + +	return mateconf_client_get_string_with_default (gedit_prefs_manager->mateconf_client, +						     key, +						     def, +						     NULL); +}	 + +static void		  +gedit_prefs_manager_set_bool (const gchar* key, gboolean value) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (gedit_prefs_manager != NULL); +	g_return_if_fail (gedit_prefs_manager->mateconf_client != NULL); +	g_return_if_fail (mateconf_client_key_is_writable ( +				gedit_prefs_manager->mateconf_client, key, NULL)); +			 +	mateconf_client_set_bool (gedit_prefs_manager->mateconf_client, key, value, NULL); +} + +static void		  +gedit_prefs_manager_set_int (const gchar* key, gint value) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (gedit_prefs_manager != NULL); +	g_return_if_fail (gedit_prefs_manager->mateconf_client != NULL); +	g_return_if_fail (mateconf_client_key_is_writable ( +				gedit_prefs_manager->mateconf_client, key, NULL)); +			 +	mateconf_client_set_int (gedit_prefs_manager->mateconf_client, key, value, NULL); +} + +static void		  +gedit_prefs_manager_set_string (const gchar* key, const gchar* value) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_if_fail (value != NULL); +	 +	g_return_if_fail (gedit_prefs_manager != NULL); +	g_return_if_fail (gedit_prefs_manager->mateconf_client != NULL); +	g_return_if_fail (mateconf_client_key_is_writable ( +				gedit_prefs_manager->mateconf_client, key, NULL)); +			 +	mateconf_client_set_string (gedit_prefs_manager->mateconf_client, key, value, NULL); +} + +static gboolean  +gedit_prefs_manager_key_is_writable (const gchar* key) +{ +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, FALSE); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, FALSE); + +	return mateconf_client_key_is_writable (gedit_prefs_manager->mateconf_client, key, NULL); +} + +/* Use default font */ +DEFINE_BOOL_PREF (use_default_font, +		  GPM_USE_DEFAULT_FONT, +		  GPM_DEFAULT_USE_DEFAULT_FONT) + +/* Editor font */ +DEFINE_STRING_PREF (editor_font, +		    GPM_EDITOR_FONT, +		    GPM_DEFAULT_EDITOR_FONT) + +/* System font */ +gchar * +gedit_prefs_manager_get_system_font (void) +{ +	gedit_debug (DEBUG_PREFS); + +	return gedit_prefs_manager_get_string (GPM_SYSTEM_FONT, +					       GPM_DEFAULT_SYSTEM_FONT); +} + +/* Create backup copy */ +DEFINE_BOOL_PREF (create_backup_copy, +		  GPM_CREATE_BACKUP_COPY, +		  GPM_DEFAULT_CREATE_BACKUP_COPY) + +/* Auto save */ +DEFINE_BOOL_PREF (auto_save, +		  GPM_AUTO_SAVE, +		  GPM_DEFAULT_AUTO_SAVE) + +/* Auto save interval */ +DEFINE_INT_PREF (auto_save_interval, +		 GPM_AUTO_SAVE_INTERVAL, +		 GPM_DEFAULT_AUTO_SAVE_INTERVAL) + + +/* Undo actions limit: if < 1 then no limits */ +DEFINE_INT_PREF (undo_actions_limit, +		 GPM_UNDO_ACTIONS_LIMIT, +		 GPM_DEFAULT_UNDO_ACTIONS_LIMIT) + +static GtkWrapMode  +get_wrap_mode_from_string (const gchar* str) +{ +	GtkWrapMode res; + +	g_return_val_if_fail (str != NULL, GTK_WRAP_WORD); +	 +	if (strcmp (str, "GTK_WRAP_NONE") == 0) +		res = GTK_WRAP_NONE; +	else +	{ +		if (strcmp (str, "GTK_WRAP_CHAR") == 0) +			res = GTK_WRAP_CHAR; +		else +			res = GTK_WRAP_WORD; +	} + +	return res; +} + +/* Wrap mode */ +GtkWrapMode +gedit_prefs_manager_get_wrap_mode (void) +{ +	gchar *str; +	GtkWrapMode res; +	 +	gedit_debug (DEBUG_PREFS); +	 +	str = gedit_prefs_manager_get_string (GPM_WRAP_MODE, +					      GPM_DEFAULT_WRAP_MODE); + +	res = get_wrap_mode_from_string (str); + +	g_free (str); + +	return res; +} +	 +void +gedit_prefs_manager_set_wrap_mode (GtkWrapMode wp) +{ +	const gchar * str; +	 +	gedit_debug (DEBUG_PREFS); + +	switch (wp) +	{ +		case GTK_WRAP_NONE: +			str = "GTK_WRAP_NONE"; +			break; + +		case GTK_WRAP_CHAR: +			str = "GTK_WRAP_CHAR"; +			break; + +		default: /* GTK_WRAP_WORD */ +			str = "GTK_WRAP_WORD"; +	} + +	gedit_prefs_manager_set_string (GPM_WRAP_MODE, +					str); +} +	 +gboolean +gedit_prefs_manager_wrap_mode_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); +	 +	return gedit_prefs_manager_key_is_writable (GPM_WRAP_MODE); +} + + +/* Tabs size */ +DEFINE_INT_PREF (tabs_size,  +		 GPM_TABS_SIZE,  +		 GPM_DEFAULT_TABS_SIZE) + +/* Insert spaces */ +DEFINE_BOOL_PREF (insert_spaces,  +		  GPM_INSERT_SPACES,  +		  GPM_DEFAULT_INSERT_SPACES) + +/* Auto indent */ +DEFINE_BOOL_PREF (auto_indent,  +		  GPM_AUTO_INDENT,  +		  GPM_DEFAULT_AUTO_INDENT) + +/* Display line numbers */ +DEFINE_BOOL_PREF (display_line_numbers,  +		  GPM_DISPLAY_LINE_NUMBERS,  +		  GPM_DEFAULT_DISPLAY_LINE_NUMBERS) + +/* Toolbar visibility */ +DEFINE_BOOL_PREF (toolbar_visible, +		  GPM_TOOLBAR_VISIBLE, +		  GPM_DEFAULT_TOOLBAR_VISIBLE) + + +/* Toolbar suttons style */ +GeditToolbarSetting  +gedit_prefs_manager_get_toolbar_buttons_style (void)  +{ +	gchar *str; +	GeditToolbarSetting res; +	 +	gedit_debug (DEBUG_PREFS); +	 +	str = gedit_prefs_manager_get_string (GPM_TOOLBAR_BUTTONS_STYLE, +					      GPM_DEFAULT_TOOLBAR_BUTTONS_STYLE); + +	if (strcmp (str, "GEDIT_TOOLBAR_ICONS") == 0) +		res = GEDIT_TOOLBAR_ICONS; +	else +	{ +		if (strcmp (str, "GEDIT_TOOLBAR_ICONS_AND_TEXT") == 0) +			res = GEDIT_TOOLBAR_ICONS_AND_TEXT; +		else  +		{ +			if (strcmp (str, "GEDIT_TOOLBAR_ICONS_BOTH_HORIZ") == 0) +				res = GEDIT_TOOLBAR_ICONS_BOTH_HORIZ; +			else +				res = GEDIT_TOOLBAR_SYSTEM; +		} +	} + +	g_free (str); + +	return res; +} + +void +gedit_prefs_manager_set_toolbar_buttons_style (GeditToolbarSetting tbs) +{ +	const gchar * str; +	 +	gedit_debug (DEBUG_PREFS); + +	switch (tbs) +	{ +		case GEDIT_TOOLBAR_ICONS: +			str = "GEDIT_TOOLBAR_ICONS"; +			break; + +		case GEDIT_TOOLBAR_ICONS_AND_TEXT: +			str = "GEDIT_TOOLBAR_ICONS_AND_TEXT"; +			break; + +	        case GEDIT_TOOLBAR_ICONS_BOTH_HORIZ: +			str = "GEDIT_TOOLBAR_ICONS_BOTH_HORIZ"; +			break; +		default: /* GEDIT_TOOLBAR_SYSTEM */ +			str = "GEDIT_TOOLBAR_SYSTEM"; +	} + +	gedit_prefs_manager_set_string (GPM_TOOLBAR_BUTTONS_STYLE, +					str); + +} + +gboolean +gedit_prefs_manager_toolbar_buttons_style_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); +	 +	return gedit_prefs_manager_key_is_writable (GPM_TOOLBAR_BUTTONS_STYLE); + +} + +/* Statusbar visiblity */ +DEFINE_BOOL_PREF (statusbar_visible, +		  GPM_STATUSBAR_VISIBLE, +		  GPM_DEFAULT_STATUSBAR_VISIBLE) +		   +/* Side Pane visiblity */ +DEFINE_BOOL_PREF (side_pane_visible, +		  GPM_SIDE_PANE_VISIBLE, +		  GPM_DEFAULT_SIDE_PANE_VISIBLE) +		   +/* Bottom Panel visiblity */ +DEFINE_BOOL_PREF (bottom_panel_visible, +		  GPM_BOTTOM_PANEL_VISIBLE, +		  GPM_DEFAULT_BOTTOM_PANEL_VISIBLE)		  		   + +/* Print syntax highlighting */ +DEFINE_BOOL_PREF (print_syntax_hl, +		  GPM_PRINT_SYNTAX, +		  GPM_DEFAULT_PRINT_SYNTAX) + +/* Print header */ +DEFINE_BOOL_PREF (print_header, +		  GPM_PRINT_HEADER, +		  GPM_DEFAULT_PRINT_HEADER) + + +/* Print Wrap mode */ +GtkWrapMode +gedit_prefs_manager_get_print_wrap_mode (void) +{ +	gchar *str; +	GtkWrapMode res; +	 +	gedit_debug (DEBUG_PREFS); +	 +	str = gedit_prefs_manager_get_string (GPM_PRINT_WRAP_MODE, +					      GPM_DEFAULT_PRINT_WRAP_MODE); + +	if (strcmp (str, "GTK_WRAP_NONE") == 0) +		res = GTK_WRAP_NONE; +	else +	{ +		if (strcmp (str, "GTK_WRAP_WORD") == 0) +			res = GTK_WRAP_WORD; +		else +			res = GTK_WRAP_CHAR; +	} + +	g_free (str); + +	return res; +} +	 +void +gedit_prefs_manager_set_print_wrap_mode (GtkWrapMode pwp) +{ +	const gchar *str; + +	gedit_debug (DEBUG_PREFS); + +	switch (pwp) +	{ +		case GTK_WRAP_NONE: +			str = "GTK_WRAP_NONE"; +			break; + +		case GTK_WRAP_WORD: +			str = "GTK_WRAP_WORD"; +			break; + +		default: /* GTK_WRAP_CHAR */ +			str = "GTK_WRAP_CHAR"; +	} + +	gedit_prefs_manager_set_string (GPM_PRINT_WRAP_MODE, str); +} + +gboolean +gedit_prefs_manager_print_wrap_mode_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); +	 +	return gedit_prefs_manager_key_is_writable (GPM_PRINT_WRAP_MODE); +} + +/* Print line numbers */	 +DEFINE_INT_PREF (print_line_numbers, +		 GPM_PRINT_LINE_NUMBERS, +		 GPM_DEFAULT_PRINT_LINE_NUMBERS) + +/* Printing fonts */ +DEFINE_STRING_PREF (print_font_body, +		    GPM_PRINT_FONT_BODY, +		    GPM_DEFAULT_PRINT_FONT_BODY) + +const gchar * +gedit_prefs_manager_get_default_print_font_body (void) +{ +	return GPM_DEFAULT_PRINT_FONT_BODY; +} + +DEFINE_STRING_PREF (print_font_header, +		    GPM_PRINT_FONT_HEADER, +		    GPM_DEFAULT_PRINT_FONT_HEADER) + +const gchar * +gedit_prefs_manager_get_default_print_font_header (void) +{ +	return GPM_DEFAULT_PRINT_FONT_HEADER; +} + +DEFINE_STRING_PREF (print_font_numbers, +		    GPM_PRINT_FONT_NUMBERS, +		    GPM_DEFAULT_PRINT_FONT_NUMBERS) + +const gchar * +gedit_prefs_manager_get_default_print_font_numbers (void) +{ +	return GPM_DEFAULT_PRINT_FONT_NUMBERS; +} + +/* Max number of files in "Recent Files" menu.  + * This is configurable only using mateconftool or mateconf-editor  + */ +gint +gedit_prefs_manager_get_max_recents (void) +{ +	gedit_debug (DEBUG_PREFS); + +	return gedit_prefs_manager_get_int (GPM_MAX_RECENTS,	 +					    GPM_DEFAULT_MAX_RECENTS); + +} + +/* Encodings */ + +static gboolean +data_exists (GSList         *list, +	    const gpointer  data) +{ +	while (list != NULL) +	{ +      		if (list->data == data) +			return TRUE; + +		list = g_slist_next (list); +    	} + +  	return FALSE; +} + +GSList * +gedit_prefs_manager_get_auto_detected_encodings (void) +{ +	GSList *strings; +	GSList *res = NULL; + +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, NULL); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, NULL); + +	strings = mateconf_client_get_list (gedit_prefs_manager->mateconf_client, +				GPM_AUTO_DETECTED_ENCODINGS, +				MATECONF_VALUE_STRING,  +				NULL); + +	if (strings == NULL) +	{ +		gint i = 0; +		const gchar* s[] = GPM_DEFAULT_AUTO_DETECTED_ENCODINGS; + +		while (s[i] != NULL) +		{ +			strings = g_slist_prepend (strings, g_strdup (s[i])); + +			++i; +		} + + +		strings = g_slist_reverse (strings); +	} + +	if (strings != NULL) +	{	 +		GSList *tmp; +		const GeditEncoding *enc; + +		tmp = strings; +		 +		while (tmp) +		{ +		      const char *charset = tmp->data; +       +		      if (strcmp (charset, "CURRENT") == 0) +			      g_get_charset (&charset); + +		      g_return_val_if_fail (charset != NULL, NULL); +		      enc = gedit_encoding_get_from_charset (charset); + +		      if (enc != NULL) +		      { +			      if (!data_exists (res, (gpointer)enc)) +				      res = g_slist_prepend (res, (gpointer)enc); + +		      } + +		      tmp = g_slist_next (tmp); +		} + +		g_slist_foreach (strings, (GFunc) g_free, NULL); +		g_slist_free (strings);     + +	 	res = g_slist_reverse (res); +	} + +	gedit_debug_message (DEBUG_PREFS, "Done"); + +	return res; +} + +GSList * +gedit_prefs_manager_get_shown_in_menu_encodings (void) +{ +	GSList *strings; +	GSList *res = NULL; + +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, NULL); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, NULL); + +	strings = mateconf_client_get_list (gedit_prefs_manager->mateconf_client, +				GPM_SHOWN_IN_MENU_ENCODINGS, +				MATECONF_VALUE_STRING,  +				NULL); + +	if (strings != NULL) +	{	 +		GSList *tmp; +		const GeditEncoding *enc; + +		tmp = strings; +		 +		while (tmp) +		{ +		      const char *charset = tmp->data; + +		      if (strcmp (charset, "CURRENT") == 0) +			      g_get_charset (&charset); + +		      g_return_val_if_fail (charset != NULL, NULL); +		      enc = gedit_encoding_get_from_charset (charset); + +		      if (enc != NULL) +		      { +			      if (!data_exists (res, (gpointer)enc)) +				      res = g_slist_prepend (res, (gpointer)enc); +		      } + +		      tmp = g_slist_next (tmp); +		} + +		g_slist_foreach (strings, (GFunc) g_free, NULL); +		g_slist_free (strings);     + +	 	res = g_slist_reverse (res); +	} + +	return res; +} + +void +gedit_prefs_manager_set_shown_in_menu_encodings (const GSList *encs) +{	 +	GSList *list = NULL; +	 +	g_return_if_fail (gedit_prefs_manager != NULL); +	g_return_if_fail (gedit_prefs_manager->mateconf_client != NULL); +	g_return_if_fail (gedit_prefs_manager_shown_in_menu_encodings_can_set ()); + +	while (encs != NULL) +	{ +		const GeditEncoding *enc; +		const gchar *charset; +		 +		enc = (const GeditEncoding *)encs->data; + +		charset = gedit_encoding_get_charset (enc); +		g_return_if_fail (charset != NULL); + +		list = g_slist_prepend (list, (gpointer)charset); + +		encs = g_slist_next (encs); +	} + +	list = g_slist_reverse (list); +		 +	mateconf_client_set_list (gedit_prefs_manager->mateconf_client, +			GPM_SHOWN_IN_MENU_ENCODINGS, +			MATECONF_VALUE_STRING, +		       	list, +			NULL); + +	g_slist_free (list); +} + +gboolean +gedit_prefs_manager_shown_in_menu_encodings_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); +	 +	return gedit_prefs_manager_key_is_writable (GPM_SHOWN_IN_MENU_ENCODINGS); + +} + +/* Highlight current line */ +DEFINE_BOOL_PREF (highlight_current_line, +		  GPM_HIGHLIGHT_CURRENT_LINE, +		  GPM_DEFAULT_HIGHLIGHT_CURRENT_LINE) + +/* Highlight matching bracket */ +DEFINE_BOOL_PREF (bracket_matching, +		  GPM_BRACKET_MATCHING, +		  GPM_DEFAULT_BRACKET_MATCHING) +	 +/* Display Right Margin */ +DEFINE_BOOL_PREF (display_right_margin, +		  GPM_DISPLAY_RIGHT_MARGIN, +		  GPM_DEFAULT_DISPLAY_RIGHT_MARGIN) + +/* Right Margin Position */	 +DEFINE_INT_PREF (right_margin_position, +		 GPM_RIGHT_MARGIN_POSITION, +		 GPM_DEFAULT_RIGHT_MARGIN_POSITION) + +static GtkSourceSmartHomeEndType +get_smart_home_end_from_string (const gchar *str) +{ +	GtkSourceSmartHomeEndType res; + +	g_return_val_if_fail (str != NULL, GTK_SOURCE_SMART_HOME_END_AFTER); + +	if (strcmp (str, "DISABLED") == 0) +		res = GTK_SOURCE_SMART_HOME_END_DISABLED; +	else if (strcmp (str, "BEFORE") == 0) +		res = GTK_SOURCE_SMART_HOME_END_BEFORE; +	else if (strcmp (str, "ALWAYS") == 0) +		res = GTK_SOURCE_SMART_HOME_END_ALWAYS; +	else +		res = GTK_SOURCE_SMART_HOME_END_AFTER; + +	return res; +} + +GtkSourceSmartHomeEndType +gedit_prefs_manager_get_smart_home_end (void) +{ +	gchar *str; +	GtkSourceSmartHomeEndType res; + +	gedit_debug (DEBUG_PREFS); + +	str = gedit_prefs_manager_get_string (GPM_SMART_HOME_END, +					      GPM_DEFAULT_SMART_HOME_END); + +	res = get_smart_home_end_from_string (str); + +	g_free (str); + +	return res; +} +	 +void +gedit_prefs_manager_set_smart_home_end (GtkSourceSmartHomeEndType smart_he) +{ +	const gchar *str; + +	gedit_debug (DEBUG_PREFS); + +	switch (smart_he) +	{ +		case GTK_SOURCE_SMART_HOME_END_DISABLED: +			str = "DISABLED"; +			break; + +		case GTK_SOURCE_SMART_HOME_END_BEFORE: +			str = "BEFORE"; +			break; + +		case GTK_SOURCE_SMART_HOME_END_ALWAYS: +			str = "ALWAYS"; +			break; + +		default: /* GTK_SOURCE_SMART_HOME_END_AFTER */ +			str = "AFTER"; +	} + +	gedit_prefs_manager_set_string (GPM_WRAP_MODE, str); +} + +gboolean +gedit_prefs_manager_smart_home_end_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); +	 +	return gedit_prefs_manager_key_is_writable (GPM_SMART_HOME_END); +} + +/* Enable syntax highlighting */ +DEFINE_BOOL_PREF (enable_syntax_highlighting, +		  GPM_SYNTAX_HL_ENABLE, +		  GPM_DEFAULT_SYNTAX_HL_ENABLE) + +/* Enable search highlighting */ +DEFINE_BOOL_PREF (enable_search_highlighting, +		  GPM_SEARCH_HIGHLIGHTING_ENABLE, +		  GPM_DEFAULT_SEARCH_HIGHLIGHTING_ENABLE) + +/* Source style scheme */ +DEFINE_STRING_PREF (source_style_scheme, +		    GPM_SOURCE_STYLE_SCHEME, +		    GPM_DEFAULT_SOURCE_STYLE_SCHEME) + +GSList * +gedit_prefs_manager_get_writable_vfs_schemes (void) +{ +	GSList *strings; +	 +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, NULL); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, NULL); + +	strings = mateconf_client_get_list (gedit_prefs_manager->mateconf_client, +				GPM_WRITABLE_VFS_SCHEMES, +				MATECONF_VALUE_STRING,  +				NULL); + +	if (strings == NULL) +	{ +		gint i = 0; +		const gchar* s[] = GPM_DEFAULT_WRITABLE_VFS_SCHEMES; + +		while (s[i] != NULL) +		{ +			strings = g_slist_prepend (strings, g_strdup (s[i])); + +			++i; +		} + +		strings = g_slist_reverse (strings); +	} + +	/* The 'file' scheme is writable by default. */ +	strings = g_slist_prepend (strings, g_strdup ("file"));  +	 +	gedit_debug_message (DEBUG_PREFS, "Done"); + +	return strings; +} + +gboolean +gedit_prefs_manager_get_restore_cursor_position (void) +{ +	gedit_debug (DEBUG_PREFS); + +	return gedit_prefs_manager_get_bool (GPM_RESTORE_CURSOR_POSITION, +					     GPM_DEFAULT_RESTORE_CURSOR_POSITION); +} + +/* Plugins: we just store/return a list of strings, all the magic has to + * happen in the plugin engine */ + +GSList * +gedit_prefs_manager_get_active_plugins (void) +{ +	GSList *plugins; + +	gedit_debug (DEBUG_PREFS); + +	g_return_val_if_fail (gedit_prefs_manager != NULL, NULL); +	g_return_val_if_fail (gedit_prefs_manager->mateconf_client != NULL, NULL); + +	plugins = mateconf_client_get_list (gedit_prefs_manager->mateconf_client, +					 GPM_ACTIVE_PLUGINS, +					 MATECONF_VALUE_STRING,  +					 NULL); + +	return plugins; +} + +void +gedit_prefs_manager_set_active_plugins (const GSList *plugins) +{	 +	g_return_if_fail (gedit_prefs_manager != NULL); +	g_return_if_fail (gedit_prefs_manager->mateconf_client != NULL); +	g_return_if_fail (gedit_prefs_manager_active_plugins_can_set ()); + +	mateconf_client_set_list (gedit_prefs_manager->mateconf_client, +			       GPM_ACTIVE_PLUGINS, +			       MATECONF_VALUE_STRING, +		       	       (GSList *) plugins, +			       NULL); +} + +gboolean +gedit_prefs_manager_active_plugins_can_set (void) +{ +	gedit_debug (DEBUG_PREFS); + +	return gedit_prefs_manager_key_is_writable (GPM_ACTIVE_PLUGINS); +} + +/* Global Lockdown */ + +GeditLockdownMask +gedit_prefs_manager_get_lockdown (void) +{ +	guint lockdown = 0; + +	if (gedit_prefs_manager_get_bool (GPM_LOCKDOWN_COMMAND_LINE, FALSE)) +		lockdown |= GEDIT_LOCKDOWN_COMMAND_LINE; + +	if (gedit_prefs_manager_get_bool (GPM_LOCKDOWN_PRINTING, FALSE)) +		lockdown |= GEDIT_LOCKDOWN_PRINTING; + +	if (gedit_prefs_manager_get_bool (GPM_LOCKDOWN_PRINT_SETUP, FALSE)) +		lockdown |= GEDIT_LOCKDOWN_PRINT_SETUP; + +	if (gedit_prefs_manager_get_bool (GPM_LOCKDOWN_SAVE_TO_DISK, FALSE)) +		lockdown |= GEDIT_LOCKDOWN_SAVE_TO_DISK; + +	return lockdown; +} + +/* The following functions are taken from mateconf-client.c  + * and partially modified.  + * The licensing terms on these is:  + * + *  + * MateConf + * Copyright (C) 1999, 2000, 2000 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +static const gchar*  +mateconf_value_type_to_string(MateConfValueType type) +{ +  switch (type) +    { +    case MATECONF_VALUE_INT: +      return "int"; +      break; +    case MATECONF_VALUE_STRING: +      return "string"; +      break; +    case MATECONF_VALUE_FLOAT: +      return "float"; +      break; +    case MATECONF_VALUE_BOOL: +      return "bool"; +      break; +    case MATECONF_VALUE_SCHEMA: +      return "schema"; +      break; +    case MATECONF_VALUE_LIST: +      return "list"; +      break; +    case MATECONF_VALUE_PAIR: +      return "pair"; +      break; +    case MATECONF_VALUE_INVALID: +      return "*invalid*"; +      break; +    default: +      g_return_val_if_reached (NULL); +      break; +    } +} + +/* Emit the proper signals for the error, and fill in err */ +static gboolean +handle_error (MateConfClient* client, GError* error, GError** err) +{ +  if (error != NULL) +    { +      mateconf_client_error(client, error); +       +      if (err == NULL) +        { +          mateconf_client_unreturned_error(client, error); + +          g_error_free(error); +        } +      else +        *err = error; + +      return TRUE; +    } +  else +    return FALSE; +} + +static gboolean +check_type (const gchar* key, MateConfValue* val, MateConfValueType t, GError** err) +{ +  if (val->type != t) +    { +      /* +      mateconf_set_error(err, MATECONF_ERROR_TYPE_MISMATCH, +                      _("Expected `%s' got, `%s' for key %s"), +                      mateconf_value_type_to_string(t), +                      mateconf_value_type_to_string(val->type), +                      key); +      */ +      g_set_error (err, MATECONF_ERROR, MATECONF_ERROR_TYPE_MISMATCH, +	  	   _("Expected `%s', got `%s' for key %s"), +                   mateconf_value_type_to_string(t), +                   mateconf_value_type_to_string(val->type), +                   key); +	       +      return FALSE; +    } +  else +    return TRUE; +} + +static gboolean +mateconf_client_get_bool_with_default (MateConfClient* client, const gchar* key, +                        	    gboolean def, GError** err) +{ +  GError* error = NULL; +  MateConfValue* val; + +  g_return_val_if_fail (err == NULL || *err == NULL, def); + +  val = mateconf_client_get (client, key, &error); + +  if (val != NULL) +    { +      gboolean retval = def; + +      g_return_val_if_fail (error == NULL, retval); +       +      if (check_type (key, val, MATECONF_VALUE_BOOL, &error)) +        retval = mateconf_value_get_bool (val); +      else +        handle_error (client, error, err); + +      mateconf_value_free (val); + +      return retval; +    } +  else +    { +      if (error != NULL) +        handle_error (client, error, err); +      return def; +    } +} + +static gchar* +mateconf_client_get_string_with_default (MateConfClient* client, const gchar* key, +                        	      const gchar* def, GError** err) +{ +  GError* error = NULL; +  gchar* val; + +  g_return_val_if_fail (err == NULL || *err == NULL, def ? g_strdup (def) : NULL); + +  val = mateconf_client_get_string (client, key, &error); + +  if (val != NULL) +    { +      g_return_val_if_fail (error == NULL, def ? g_strdup (def) : NULL); +       +      return val; +    } +  else +    { +      if (error != NULL) +        handle_error (client, error, err); +      return def ? g_strdup (def) : NULL; +    } +} + +static gint +mateconf_client_get_int_with_default (MateConfClient* client, const gchar* key, +                        	   gint def, GError** err) +{ +  GError* error = NULL; +  MateConfValue* val; + +  g_return_val_if_fail (err == NULL || *err == NULL, def); + +  val = mateconf_client_get (client, key, &error); + +  if (val != NULL) +    { +      gint retval = def; + +      g_return_val_if_fail (error == NULL, def); +       +      if (check_type (key, val, MATECONF_VALUE_INT, &error)) +        retval = mateconf_value_get_int(val); +      else +        handle_error (client, error, err); + +      mateconf_value_free (val); + +      return retval; +    } +  else +    { +      if (error != NULL) +        handle_error (client, error, err); +      return def; +    } +} + diff --git a/gedit/gedit-prefs-manager.h b/gedit/gedit-prefs-manager.h new file mode 100755 index 00000000..82041617 --- /dev/null +++ b/gedit/gedit-prefs-manager.h @@ -0,0 +1,423 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-prefs-manager.h + * This file is part of gedit + * + * Copyright (C) 2002  Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 2002. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef __GEDIT_PREFS_MANAGER_H__ +#define __GEDIT_PREFS_MANAGER_H__ + +#include <gtk/gtk.h> +#include <glib.h> +#include <gtksourceview/gtksourceview.h> +#include "gedit-app.h" + +#define GEDIT_BASE_KEY	"/apps/gedit-2" + +#define GPM_PREFS_DIR			GEDIT_BASE_KEY "/preferences" + +/* Editor */ +#define GPM_FONT_DIR			GPM_PREFS_DIR "/editor/font" +#define GPM_USE_DEFAULT_FONT		GPM_FONT_DIR "/use_default_font" +#define GPM_EDITOR_FONT			GPM_FONT_DIR "/editor_font" +#define GPM_SYSTEM_FONT			"/desktop/mate/interface/monospace_font_name" + +#define GPM_SAVE_DIR			GPM_PREFS_DIR  "/editor/save" +#define GPM_CREATE_BACKUP_COPY  	GPM_SAVE_DIR "/create_backup_copy" + +#define GPM_AUTO_SAVE			GPM_SAVE_DIR "/auto_save" +#define GPM_AUTO_SAVE_INTERVAL		GPM_SAVE_DIR "/auto_save_interval" + +#define GPM_UNDO_DIR			GPM_PREFS_DIR  "/editor/undo" +#define GPM_UNDO_ACTIONS_LIMIT		GPM_UNDO_DIR "/max_undo_actions" + +#define GPM_WRAP_MODE_DIR		GPM_PREFS_DIR "/editor/wrap_mode" +#define GPM_WRAP_MODE			GPM_WRAP_MODE_DIR "/wrap_mode" + +#define GPM_TABS_DIR			GPM_PREFS_DIR "/editor/tabs" +#define GPM_TABS_SIZE			GPM_TABS_DIR "/tabs_size" +#define GPM_INSERT_SPACES		GPM_TABS_DIR "/insert_spaces" + +#define GPM_AUTO_INDENT_DIR		GPM_PREFS_DIR "/editor/auto_indent" +#define GPM_AUTO_INDENT			GPM_AUTO_INDENT_DIR "/auto_indent" + +#define GPM_LINE_NUMBERS_DIR		GPM_PREFS_DIR "/editor/line_numbers" +#define GPM_DISPLAY_LINE_NUMBERS 	GPM_LINE_NUMBERS_DIR "/display_line_numbers" + +#define GPM_CURRENT_LINE_DIR		GPM_PREFS_DIR "/editor/current_line" +#define GPM_HIGHLIGHT_CURRENT_LINE 	GPM_CURRENT_LINE_DIR "/highlight_current_line" + +#define GPM_BRACKET_MATCHING_DIR	GPM_PREFS_DIR "/editor/bracket_matching" +#define GPM_BRACKET_MATCHING		GPM_BRACKET_MATCHING_DIR "/bracket_matching" + +#define GPM_RIGHT_MARGIN_DIR		GPM_PREFS_DIR "/editor/right_margin" +#define GPM_DISPLAY_RIGHT_MARGIN	GPM_RIGHT_MARGIN_DIR "/display_right_margin" +#define GPM_RIGHT_MARGIN_POSITION	GPM_RIGHT_MARGIN_DIR "/right_margin_position" + +#define GPM_SMART_HOME_END_DIR		GPM_PREFS_DIR "/editor/smart_home_end" +#define GPM_SMART_HOME_END		GPM_SMART_HOME_END_DIR "/smart_home_end" + +#define GPM_CURSOR_POSITION_DIR		GPM_PREFS_DIR  "/editor/cursor_position" +#define GPM_RESTORE_CURSOR_POSITION	GPM_CURSOR_POSITION_DIR "/restore_cursor_position" + +#define GPM_SEARCH_HIGHLIGHTING_DIR	GPM_PREFS_DIR "/editor/search_highlighting" +#define GPM_SEARCH_HIGHLIGHTING_ENABLE	GPM_SEARCH_HIGHLIGHTING_DIR "/enable" + +#define GPM_SOURCE_STYLE_DIR		GPM_PREFS_DIR "/editor/colors" +#define GPM_SOURCE_STYLE_SCHEME		GPM_SOURCE_STYLE_DIR "/scheme" + +/* UI */ +#define GPM_TOOLBAR_DIR			GPM_PREFS_DIR "/ui/toolbar" +#define GPM_TOOLBAR_VISIBLE	 	GPM_TOOLBAR_DIR "/toolbar_visible" +#define GPM_TOOLBAR_BUTTONS_STYLE 	GPM_TOOLBAR_DIR "/toolbar_buttons_style" + +#define GPM_STATUSBAR_DIR		GPM_PREFS_DIR "/ui/statusbar" +#define GPM_STATUSBAR_VISIBLE		GPM_STATUSBAR_DIR "/statusbar_visible" + +#define GPM_SIDE_PANE_DIR		GPM_PREFS_DIR "/ui/side_pane" +#define GPM_SIDE_PANE_VISIBLE		GPM_SIDE_PANE_DIR "/side_pane_visible" + +#define GPM_BOTTOM_PANEL_DIR		GPM_PREFS_DIR "/ui/bottom_panel" +#define GPM_BOTTOM_PANEL_VISIBLE	GPM_BOTTOM_PANEL_DIR "/bottom_panel_visible" + +#define GPM_RECENTS_DIR			GPM_PREFS_DIR "/ui/recents" +#define GPM_MAX_RECENTS			GPM_RECENTS_DIR "/max_recents" + +/* Print */ +#define GPM_PRINT_PAGE_DIR		GPM_PREFS_DIR "/print/page" +#define GPM_PRINT_SYNTAX		GPM_PRINT_PAGE_DIR "/print_syntax_highlighting" +#define GPM_PRINT_HEADER		GPM_PRINT_PAGE_DIR "/print_header" +#define GPM_PRINT_WRAP_MODE		GPM_PRINT_PAGE_DIR "/print_wrap_mode" +#define GPM_PRINT_LINE_NUMBERS		GPM_PRINT_PAGE_DIR "/print_line_numbers" + +#define GPM_PRINT_FONT_DIR		GPM_PREFS_DIR "/print/fonts" +#define GPM_PRINT_FONT_BODY		GPM_PRINT_FONT_DIR "/print_font_body_pango" +#define GPM_PRINT_FONT_HEADER		GPM_PRINT_FONT_DIR "/print_font_header_pango" +#define GPM_PRINT_FONT_NUMBERS		GPM_PRINT_FONT_DIR "/print_font_numbers_pango" + +/* Encodings */ +#define GPM_ENCODINGS_DIR		GPM_PREFS_DIR "/encodings" +#define GPM_AUTO_DETECTED_ENCODINGS	GPM_ENCODINGS_DIR "/auto_detected" +#define GPM_SHOWN_IN_MENU_ENCODINGS	GPM_ENCODINGS_DIR "/shown_in_menu" + +/* Syntax highlighting */ +#define GPM_SYNTAX_HL_DIR		GPM_PREFS_DIR "/syntax_highlighting" +#define GPM_SYNTAX_HL_ENABLE		GPM_SYNTAX_HL_DIR "/enable" + +/* White list of writable mate-vfs methods */ +#define GPM_WRITABLE_VFS_SCHEMES 	GPM_SAVE_DIR "/writable_vfs_schemes" + +/* Plugins */ +#define GPM_PLUGINS_DIR			GEDIT_BASE_KEY "/plugins" +#define GPM_ACTIVE_PLUGINS		GPM_PLUGINS_DIR "/active-plugins" + +/* Global Lockdown keys */ +#define GPM_LOCKDOWN_DIR		"/desktop/mate/lockdown" +#define GPM_LOCKDOWN_COMMAND_LINE	GPM_LOCKDOWN_DIR "/disable_command_line" +#define GPM_LOCKDOWN_PRINTING		GPM_LOCKDOWN_DIR "/disable_printing" +#define GPM_LOCKDOWN_PRINT_SETUP	GPM_LOCKDOWN_DIR "/disable_print_setup" +#define GPM_LOCKDOWN_SAVE_TO_DISK	GPM_LOCKDOWN_DIR "/disable_save_to_disk" + +/* Fallback default values. Keep in sync with gedit.schemas */ + +#define GPM_DEFAULT_USE_DEFAULT_FONT 	1 /* TRUE */ + +#ifndef PLATFORM_OSX +#define GPM_DEFAULT_EDITOR_FONT 	(const gchar*) "Monospace 12" +#define GPM_DEFAULT_SYSTEM_FONT 	(const gchar*) "Monospace 10" +#else +#define GPM_DEFAULT_EDITOR_FONT		(const gchar*) "Monaco 12" +#define GPM_DEFAULT_SYSTEM_FONT		(const gchar*) "Monaco 12" +#endif + +#define GPM_DEFAULT_CREATE_BACKUP_COPY	0 /* FALSE */ + +#define GPM_DEFAULT_AUTO_SAVE		0 /* FALSE */ +#define GPM_DEFAULT_AUTO_SAVE_INTERVAL	10 /* minutes */ + +#define GPM_DEFAULT_UNDO_ACTIONS_LIMIT	2000 /* actions */ + +#define GPM_DEFAULT_WRAP_MODE		"GTK_WRAP_WORD" + +#define GPM_DEFAULT_TABS_SIZE		8 +#define GPM_DEFAULT_INSERT_SPACES	0 /* FALSE */ + +#define GPM_DEFAULT_AUTO_INDENT		0 /* FALSE */ + +#define GPM_DEFAULT_DISPLAY_LINE_NUMBERS 0 /* FALSE */ + +#define GPM_DEFAULT_AUTO_DETECTED_ENCODINGS {"UTF-8", "CURRENT", "ISO-8859-15", NULL} +       	 +#define GPM_DEFAULT_TOOLBAR_VISIBLE	1 /* TRUE */ +#define GPM_DEFAULT_TOOLBAR_BUTTONS_STYLE "GEDIT_TOOLBAR_SYSTEM" +#define GPM_DEFAULT_TOOLBAR_SHOW_TOOLTIPS 1 /* TRUE */ + +#define GPM_DEFAULT_STATUSBAR_VISIBLE	1 /* TRUE */ +#define GPM_DEFAULT_SIDE_PANE_VISIBLE	0 /* FALSE */ +#define GPM_DEFAULT_BOTTOM_PANEL_VISIBLE 0 /* FALSE */ + +#define GPM_DEFAULT_PRINT_SYNTAX	1 /* TRUE */ +#define GPM_DEFAULT_PRINT_HEADER	1 /* TRUE */ +#define GPM_DEFAULT_PRINT_WRAP_MODE	"GTK_WRAP_WORD" +#define GPM_DEFAULT_PRINT_LINE_NUMBERS	0 /* No numbers */ + +#ifndef PLATFORM_OSX +#define GPM_DEFAULT_PRINT_FONT_BODY 	(const gchar*) "Monospace 9" +#else +#define GPM_DEFAULT_PRINT_FONT_BODY	(const gchar*) "Monaco 10" +#endif + +#define GPM_DEFAULT_PRINT_FONT_HEADER	(const gchar*) "Sans 11" +#define GPM_DEFAULT_PRINT_FONT_NUMBERS	(const gchar*) "Sans 8" + +#define GPM_DEFAULT_MAX_RECENTS		5 + +#define GPM_DEFAULT_HIGHLIGHT_CURRENT_LINE  1 /* TRUE */ + +#define GPM_DEFAULT_BRACKET_MATCHING	  0 /* FALSE */ + +#define GPM_DEFAULT_DISPLAY_RIGHT_MARGIN  0 /* FALSE */ +#define GPM_DEFAULT_RIGHT_MARGIN_POSITION 80 + +#define GPM_DEFAULT_SMART_HOME_END	"AFTER" + +#define GPM_DEFAULT_SYNTAX_HL_ENABLE	1 /* TRUE */ + +#define GPM_DEFAULT_WRITABLE_VFS_SCHEMES {"ssh", "sftp", "smb", "dav", "davs", NULL} + +#define GPM_DEFAULT_RESTORE_CURSOR_POSITION 1 /* TRUE */ + +#define GPM_DEFAULT_SEARCH_HIGHLIGHTING_ENABLE 1 /* TRUE */ + +#define GPM_DEFAULT_SOURCE_STYLE_SCHEME "classic" + +typedef enum { +	GEDIT_TOOLBAR_SYSTEM = 0, +	GEDIT_TOOLBAR_ICONS, +	GEDIT_TOOLBAR_ICONS_AND_TEXT, +	GEDIT_TOOLBAR_ICONS_BOTH_HORIZ +} GeditToolbarSetting; + +/** LIFE CYCLE MANAGEMENT FUNCTIONS **/ + +gboolean		 gedit_prefs_manager_init (void); + +/* This function must be called before exiting gedit */ +void			 gedit_prefs_manager_shutdown (void); + + +/** PREFS MANAGEMENT FUNCTIONS **/ + +/* Use default font */ +gboolean 		 gedit_prefs_manager_get_use_default_font 	(void); +void			 gedit_prefs_manager_set_use_default_font 	(gboolean udf); +gboolean		 gedit_prefs_manager_use_default_font_can_set	(void); + +/* Editor font */ +gchar 			*gedit_prefs_manager_get_editor_font		(void); +void			 gedit_prefs_manager_set_editor_font 		(const gchar *font); +gboolean		 gedit_prefs_manager_editor_font_can_set	(void); + +/* System font */ +gchar 			*gedit_prefs_manager_get_system_font		(void); + +/* Create backup copy */ +gboolean		 gedit_prefs_manager_get_create_backup_copy	(void); +void			 gedit_prefs_manager_set_create_backup_copy	(gboolean cbc); +gboolean		 gedit_prefs_manager_create_backup_copy_can_set	(void); + +/* Auto save */ +gboolean		 gedit_prefs_manager_get_auto_save		(void); +void			 gedit_prefs_manager_set_auto_save		(gboolean as); +gboolean		 gedit_prefs_manager_auto_save_can_set		(void); + +/* Auto save interval */ +gint			 gedit_prefs_manager_get_auto_save_interval	(void); +void			 gedit_prefs_manager_set_auto_save_interval	(gint asi); +gboolean		 gedit_prefs_manager_auto_save_interval_can_set	(void); + +/* Undo actions limit: if < 1 then no limits */ +gint 			 gedit_prefs_manager_get_undo_actions_limit	(void); +void			 gedit_prefs_manager_set_undo_actions_limit	(gint ual); +gboolean		 gedit_prefs_manager_undo_actions_limit_can_set	(void); + +/* Wrap mode */ +GtkWrapMode		 gedit_prefs_manager_get_wrap_mode		(void); +void			 gedit_prefs_manager_set_wrap_mode		(GtkWrapMode wp); +gboolean		 gedit_prefs_manager_wrap_mode_can_set		(void); + +/* Tabs size */ +gint			 gedit_prefs_manager_get_tabs_size		(void); +void			 gedit_prefs_manager_set_tabs_size		(gint ts); +gboolean		 gedit_prefs_manager_tabs_size_can_set		(void); + +/* Insert spaces */ +gboolean		 gedit_prefs_manager_get_insert_spaces	 	(void); +void			 gedit_prefs_manager_set_insert_spaces	 	(gboolean ai); +gboolean		 gedit_prefs_manager_insert_spaces_can_set 	(void); + +/* Auto indent */ +gboolean		 gedit_prefs_manager_get_auto_indent	 	(void); +void			 gedit_prefs_manager_set_auto_indent	 	(gboolean ai); +gboolean		 gedit_prefs_manager_auto_indent_can_set 	(void); + +/* Display line numbers */ +gboolean		 gedit_prefs_manager_get_display_line_numbers 	(void); +void			 gedit_prefs_manager_set_display_line_numbers 	(gboolean dln); +gboolean		 gedit_prefs_manager_display_line_numbers_can_set (void); + +/* Toolbar visible */ +gboolean		 gedit_prefs_manager_get_toolbar_visible	(void); +void			 gedit_prefs_manager_set_toolbar_visible	(gboolean tv); +gboolean		 gedit_prefs_manager_toolbar_visible_can_set	(void); + +/* Toolbar buttons style */ +GeditToolbarSetting 	 gedit_prefs_manager_get_toolbar_buttons_style	(void);  +void 			 gedit_prefs_manager_set_toolbar_buttons_style	(GeditToolbarSetting tbs);  +gboolean		 gedit_prefs_manager_toolbar_buttons_style_can_set (void); + +/* Statusbar visible */ +gboolean		 gedit_prefs_manager_get_statusbar_visible	(void); +void			 gedit_prefs_manager_set_statusbar_visible	(gboolean sv); +gboolean		 gedit_prefs_manager_statusbar_visible_can_set	(void); + +/* Side pane visible */ +gboolean		 gedit_prefs_manager_get_side_pane_visible	(void); +void			 gedit_prefs_manager_set_side_pane_visible	(gboolean tv); +gboolean		 gedit_prefs_manager_side_pane_visible_can_set	(void); + +/* Bottom panel visible */ +gboolean		 gedit_prefs_manager_get_bottom_panel_visible	(void); +void			 gedit_prefs_manager_set_bottom_panel_visible	(gboolean tv); +gboolean		 gedit_prefs_manager_bottom_panel_visible_can_set(void); +/* Print syntax highlighting */ +gboolean		 gedit_prefs_manager_get_print_syntax_hl	(void); +void			 gedit_prefs_manager_set_print_syntax_hl	(gboolean ps); +gboolean		 gedit_prefs_manager_print_syntax_hl_can_set	(void); + +/* Print header */ +gboolean		 gedit_prefs_manager_get_print_header		(void); +void			 gedit_prefs_manager_set_print_header		(gboolean ph); +gboolean		 gedit_prefs_manager_print_header_can_set	(void); +	 +/* Wrap mode while printing */ +GtkWrapMode		 gedit_prefs_manager_get_print_wrap_mode	(void); +void			 gedit_prefs_manager_set_print_wrap_mode	(GtkWrapMode pwm); +gboolean		 gedit_prefs_manager_print_wrap_mode_can_set	(void); + +/* Print line numbers */	 +gint		 	 gedit_prefs_manager_get_print_line_numbers	(void); +void 			 gedit_prefs_manager_set_print_line_numbers	(gint pln); +gboolean		 gedit_prefs_manager_print_line_numbers_can_set	(void); + +/* Font used to print the body of documents */ +gchar			*gedit_prefs_manager_get_print_font_body	(void); +void			 gedit_prefs_manager_set_print_font_body	(const gchar *font); +gboolean		 gedit_prefs_manager_print_font_body_can_set	(void); +const gchar		*gedit_prefs_manager_get_default_print_font_body (void); + +/* Font used to print headers */ +gchar			*gedit_prefs_manager_get_print_font_header	(void); +void			 gedit_prefs_manager_set_print_font_header	(const gchar *font); +gboolean		 gedit_prefs_manager_print_font_header_can_set	(void); +const gchar		*gedit_prefs_manager_get_default_print_font_header (void); + +/* Font used to print line numbers */ +gchar			*gedit_prefs_manager_get_print_font_numbers	(void); +void			 gedit_prefs_manager_set_print_font_numbers	(const gchar *font); +gboolean		 gedit_prefs_manager_print_font_numbers_can_set	(void); +const gchar		*gedit_prefs_manager_get_default_print_font_numbers (void); + +/* Max number of files in "Recent Files" menu.  + * This is configurable only using mateconftool or mateconf-editor  + */ +gint		 	 gedit_prefs_manager_get_max_recents		(void); + +/* Encodings */ +GSList 			*gedit_prefs_manager_get_auto_detected_encodings (void); + +GSList			*gedit_prefs_manager_get_shown_in_menu_encodings (void); +void			 gedit_prefs_manager_set_shown_in_menu_encodings (const GSList *encs); +gboolean 		 gedit_prefs_manager_shown_in_menu_encodings_can_set (void); + +/* Highlight current line */ +gboolean		 gedit_prefs_manager_get_highlight_current_line	(void); +void			 gedit_prefs_manager_set_highlight_current_line	(gboolean hl); +gboolean		 gedit_prefs_manager_highlight_current_line_can_set (void); + +/* Highlight matching bracket */ +gboolean		 gedit_prefs_manager_get_bracket_matching	(void); +void			 gedit_prefs_manager_set_bracket_matching	(gboolean bm); +gboolean		 gedit_prefs_manager_bracket_matching_can_set (void); + +/* Display right margin */ +gboolean		 gedit_prefs_manager_get_display_right_margin	(void); +void			 gedit_prefs_manager_set_display_right_margin	(gboolean drm); +gboolean		 gedit_prefs_manager_display_right_margin_can_set (void); + +/* Right margin position */ +gint		 	 gedit_prefs_manager_get_right_margin_position	(void); +void 			 gedit_prefs_manager_set_right_margin_position	(gint rmp); +gboolean		 gedit_prefs_manager_right_margin_position_can_set (void); + +/* Smart home end */ +GtkSourceSmartHomeEndType +		 	 gedit_prefs_manager_get_smart_home_end		(void); +void 			 gedit_prefs_manager_set_smart_home_end		(GtkSourceSmartHomeEndType  smart_he); +gboolean		 gedit_prefs_manager_smart_home_end_can_set	(void); + +/* Enable syntax highlighting */ +gboolean 		 gedit_prefs_manager_get_enable_syntax_highlighting (void); +void			 gedit_prefs_manager_set_enable_syntax_highlighting (gboolean esh); +gboolean		 gedit_prefs_manager_enable_syntax_highlighting_can_set (void); + +/* Writable VFS schemes */ +GSList			*gedit_prefs_manager_get_writable_vfs_schemes	(void); + +/* Restore cursor position */ +gboolean 		 gedit_prefs_manager_get_restore_cursor_position (void); + +/* Enable search highlighting */ +gboolean 		 gedit_prefs_manager_get_enable_search_highlighting (void); +void			 gedit_prefs_manager_set_enable_search_highlighting (gboolean esh); +gboolean		 gedit_prefs_manager_enable_search_highlighting_can_set (void); + +/* Style scheme */ +gchar			*gedit_prefs_manager_get_source_style_scheme	(void); +void			 gedit_prefs_manager_set_source_style_scheme	(const gchar *scheme); +gboolean		 gedit_prefs_manager_source_style_scheme_can_set(void); + +/* Plugins */ +GSList			*gedit_prefs_manager_get_active_plugins		(void); +void			 gedit_prefs_manager_set_active_plugins		(const GSList *plugins); +gboolean 		 gedit_prefs_manager_active_plugins_can_set	(void); + +/* Global lockdown */ +GeditLockdownMask	 gedit_prefs_manager_get_lockdown			(void); + +#endif  /* __GEDIT_PREFS_MANAGER_H__ */ + + diff --git a/gedit/gedit-print-job.c b/gedit/gedit-print-job.c new file mode 100755 index 00000000..00acba27 --- /dev/null +++ b/gedit/gedit-print-job.c @@ -0,0 +1,865 @@ +/* + * gedit-print.c + * This file is part of gedit + * + * Copyright (C) 2000-2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2008 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id: gedit-print.c 6022 2007-12-09 14:38:57Z pborelli $ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtksourceview/gtksourceprintcompositor.h> + +#include "gedit-print-job.h" +#include "gedit-debug.h" +#include "gedit-prefs-manager.h" +#include "gedit-print-preview.h" +#include "gedit-marshal.h" +#include "gedit-utils.h" +#include "gedit-dirs.h" + + +#define GEDIT_PRINT_JOB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), \ +					    GEDIT_TYPE_PRINT_JOB, \ +					    GeditPrintJobPrivate)) + +struct _GeditPrintJobPrivate +{ +	GeditView                *view; +	GeditDocument            *doc; + +	GtkPrintOperation        *operation; +	GtkSourcePrintCompositor *compositor; + +	GtkPrintSettings         *settings; + +	GtkWidget                *preview; + +	GeditPrintJobStatus       status; +	 +	gchar                    *status_string; + +	gdouble			  progress; + +	gboolean                  is_preview; + +	/* widgets part of the custom print preferences widget. +	 * These pointers are valid just when the dialog is displayed */ +	GtkWidget *syntax_checkbutton; +	GtkWidget *page_header_checkbutton; +	GtkWidget *line_numbers_checkbutton; +	GtkWidget *line_numbers_hbox; +	GtkWidget *line_numbers_spinbutton; +	GtkWidget *text_wrapping_checkbutton; +	GtkWidget *do_not_split_checkbutton; +	GtkWidget *fonts_table; +	GtkWidget *body_font_label; +	GtkWidget *headers_font_label; +	GtkWidget *numbers_font_label; +	GtkWidget *body_fontbutton; +	GtkWidget *headers_fontbutton; +	GtkWidget *numbers_fontbutton; +	GtkWidget *restore_button; +}; + +enum +{ +	PROP_0, +	PROP_VIEW +}; + +enum  +{ +	PRINTING, +	SHOW_PREVIEW, +	DONE, +	LAST_SIGNAL +}; + +static guint print_job_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GeditPrintJob, gedit_print_job, G_TYPE_OBJECT) + +static void +set_view (GeditPrintJob *job, GeditView *view) +{ +	job->priv->view = view; +	job->priv->doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +} + +static void  +gedit_print_job_get_property (GObject    *object, +			      guint       prop_id, +			      GValue     *value, +			      GParamSpec *pspec) +{ +	GeditPrintJob *job = GEDIT_PRINT_JOB (object); + +	switch (prop_id) +	{ +		case PROP_VIEW: +			g_value_set_object (value, job->priv->view); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void  +gedit_print_job_set_property (GObject      *object, +			      guint         prop_id, +			      const GValue *value, +			      GParamSpec   *pspec) +{ +	GeditPrintJob *job = GEDIT_PRINT_JOB (object); + +	switch (prop_id) +	{ +		case PROP_VIEW: +			set_view (job, g_value_get_object (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_print_job_finalize (GObject *object) +{ +	GeditPrintJob *job = GEDIT_PRINT_JOB (object); + +	g_free (job->priv->status_string); +	 +	if (job->priv->compositor != NULL) +		g_object_unref (job->priv->compositor); + +	if (job->priv->operation != NULL) +		g_object_unref (job->priv->operation); + +	G_OBJECT_CLASS (gedit_print_job_parent_class)->finalize (object); +} + +static void  +gedit_print_job_class_init (GeditPrintJobClass *klass) +{ +	GObjectClass *object_class; + +	object_class = G_OBJECT_CLASS (klass); + +	object_class->get_property = gedit_print_job_get_property; +	object_class->set_property = gedit_print_job_set_property; +	object_class->finalize = gedit_print_job_finalize; + +	g_object_class_install_property (object_class, +					 PROP_VIEW, +					 g_param_spec_object ("view", +							      "Gedit View", +							      "Gedit View to print", +							      GEDIT_TYPE_VIEW, +							      G_PARAM_READWRITE | +							      G_PARAM_STATIC_STRINGS | +							      G_PARAM_CONSTRUCT_ONLY)); + +	print_job_signals[PRINTING] = +		g_signal_new ("printing", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditPrintJobClass, printing), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__UINT, +			      G_TYPE_NONE, +			      1, +			      G_TYPE_UINT); + +	print_job_signals[SHOW_PREVIEW] = +		g_signal_new ("show-preview", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditPrintJobClass, show_preview), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GTK_TYPE_WIDGET); + +	print_job_signals[DONE] = +		g_signal_new ("done", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditPrintJobClass, done), +			      NULL, NULL, +			      gedit_marshal_VOID__UINT_POINTER, +			      G_TYPE_NONE, +			      2, +			      G_TYPE_UINT, +			      G_TYPE_POINTER); +			      			       +	g_type_class_add_private (object_class, sizeof (GeditPrintJobPrivate)); +} + +static void +line_numbers_checkbutton_toggled (GtkToggleButton *button, +				  GeditPrintJob   *job) +{ +	if (gtk_toggle_button_get_active (button)) +	{ +		gtk_widget_set_sensitive (job->priv->line_numbers_hbox,  +					  gedit_prefs_manager_print_line_numbers_can_set ()); +	} +	else +	{ +		gtk_widget_set_sensitive (job->priv->line_numbers_hbox, FALSE); +	} +} + +static void +wrap_mode_checkbutton_toggled (GtkToggleButton *button, +			       GeditPrintJob   *job) +{ +	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->text_wrapping_checkbutton))) +	{ +		gtk_widget_set_sensitive (job->priv->do_not_split_checkbutton,  +					  FALSE); +		gtk_toggle_button_set_inconsistent ( +			GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton), +					   TRUE); +	} +	else +	{ +		gtk_widget_set_sensitive (job->priv->do_not_split_checkbutton, +					  TRUE); +		gtk_toggle_button_set_inconsistent ( +			GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton), +					   FALSE); +	} +} + +static void +restore_button_clicked (GtkButton     *button, +			GeditPrintJob *job) + +{ +	if (gedit_prefs_manager_print_font_body_can_set ()) +	{ +		const gchar *font; + +		font = gedit_prefs_manager_get_default_print_font_body (); + +		gtk_font_button_set_font_name ( +				GTK_FONT_BUTTON (job->priv->body_fontbutton), +				font); +	} +	 +	if (gedit_prefs_manager_print_font_header_can_set ()) +	{ +		const gchar *font; + +		font = gedit_prefs_manager_get_default_print_font_header (); + +		gtk_font_button_set_font_name ( +				GTK_FONT_BUTTON (job->priv->headers_fontbutton), +				font); +	} + +	if (gedit_prefs_manager_print_font_numbers_can_set ()) +	{ +		const gchar *font; + +		font = gedit_prefs_manager_get_default_print_font_numbers (); + +		gtk_font_button_set_font_name ( +				GTK_FONT_BUTTON (job->priv->numbers_fontbutton), +				font); +	} +} + +static GObject * +create_custom_widget_cb (GtkPrintOperation *operation,  +			 GeditPrintJob     *job) +{ +	gboolean ret; +	GtkWidget *widget; +	GtkWidget *error_widget; +	gchar *font; +	gint line_numbers; +	gboolean can_set; +	GtkWrapMode wrap_mode; +	gchar *file; +	gchar *root_objects[] = { +		"adjustment1", +		"contents", +		NULL +	}; + +	file = gedit_dirs_get_ui_file ("gedit-print-preferences.ui"); +	ret = gedit_utils_get_ui_objects (file, +					  root_objects, +					  &error_widget, +					  "contents", &widget, +					  "syntax_checkbutton", &job->priv->syntax_checkbutton, +					  "line_numbers_checkbutton", &job->priv->line_numbers_checkbutton, +					  "line_numbers_hbox", &job->priv->line_numbers_hbox, +					  "line_numbers_spinbutton", &job->priv->line_numbers_spinbutton, +					  "page_header_checkbutton", &job->priv->page_header_checkbutton, +					  "text_wrapping_checkbutton", &job->priv->text_wrapping_checkbutton, +					  "do_not_split_checkbutton", &job->priv->do_not_split_checkbutton, +					  "fonts_table", &job->priv->fonts_table, +					  "body_font_label", &job->priv->body_font_label, +					  "body_fontbutton", &job->priv->body_fontbutton, +					  "headers_font_label", &job->priv->headers_font_label, +					  "headers_fontbutton", &job->priv->headers_fontbutton, +					  "numbers_font_label", &job->priv->numbers_font_label, +					  "numbers_fontbutton", &job->priv->numbers_fontbutton, +					  "restore_button", &job->priv->restore_button, +					  NULL); +	g_free (file); + +	if (!ret) +	{ +		return G_OBJECT (error_widget); +	} + +	/* Print syntax */ +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (job->priv->syntax_checkbutton), +				      gedit_prefs_manager_get_print_syntax_hl ()); +	gtk_widget_set_sensitive (job->priv->syntax_checkbutton, +				  gedit_prefs_manager_print_syntax_hl_can_set ()); + +	/* Print page headers */ +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (job->priv->page_header_checkbutton), +				      gedit_prefs_manager_get_print_header ()); +	gtk_widget_set_sensitive (job->priv->page_header_checkbutton, +				  gedit_prefs_manager_print_header_can_set ()); + +	/* Line numbers */ +	line_numbers =  gedit_prefs_manager_get_print_line_numbers (); +	can_set = gedit_prefs_manager_print_line_numbers_can_set (); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (job->priv->line_numbers_checkbutton), +				      line_numbers > 0); +	gtk_widget_set_sensitive (job->priv->line_numbers_checkbutton, can_set); + +	if (line_numbers > 0) +	{ +		gtk_spin_button_set_value (GTK_SPIN_BUTTON (job->priv->line_numbers_spinbutton), +					   (guint) line_numbers); +		gtk_widget_set_sensitive (job->priv->line_numbers_hbox, can_set);	 +	} +	else +	{ +		gtk_spin_button_set_value (GTK_SPIN_BUTTON (job->priv->line_numbers_spinbutton), +					   1); +		gtk_widget_set_sensitive (job->priv->line_numbers_hbox, FALSE); +	} + +	/* Text wrapping */ +	wrap_mode = gedit_prefs_manager_get_print_wrap_mode (); + +	switch (wrap_mode) +	{ +		case GTK_WRAP_WORD: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (job->priv->text_wrapping_checkbutton), TRUE); +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton), TRUE); +			break; +		case GTK_WRAP_CHAR: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (job->priv->text_wrapping_checkbutton), TRUE); +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton), FALSE); +			break; +		default: +			gtk_toggle_button_set_active ( +				GTK_TOGGLE_BUTTON (job->priv->text_wrapping_checkbutton), FALSE); +			gtk_toggle_button_set_inconsistent ( +				GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton), TRUE); +	} + +	can_set = gedit_prefs_manager_print_wrap_mode_can_set (); + +	gtk_widget_set_sensitive (job->priv->text_wrapping_checkbutton, can_set); +	gtk_widget_set_sensitive (job->priv->do_not_split_checkbutton,  +				  can_set && (wrap_mode != GTK_WRAP_NONE)); + +	/* Set initial values */ +	font = gedit_prefs_manager_get_print_font_body (); +	gtk_font_button_set_font_name (GTK_FONT_BUTTON (job->priv->body_fontbutton), +				       font); +	g_free (font); + +	font = gedit_prefs_manager_get_print_font_header (); +	gtk_font_button_set_font_name (GTK_FONT_BUTTON (job->priv->headers_fontbutton), +				       font); +	g_free (font); + +	font = gedit_prefs_manager_get_print_font_numbers (); +	gtk_font_button_set_font_name (GTK_FONT_BUTTON (job->priv->numbers_fontbutton), +				       font); +	g_free (font); + +	can_set = gedit_prefs_manager_print_font_body_can_set (); +	gtk_widget_set_sensitive (job->priv->body_fontbutton, can_set); +	gtk_widget_set_sensitive (job->priv->body_font_label, can_set); + +	can_set = gedit_prefs_manager_print_font_header_can_set (); +	gtk_widget_set_sensitive (job->priv->headers_fontbutton, can_set); +	gtk_widget_set_sensitive (job->priv->headers_font_label, can_set); + +	can_set = gedit_prefs_manager_print_font_numbers_can_set (); +	gtk_widget_set_sensitive (job->priv->numbers_fontbutton, can_set); +	gtk_widget_set_sensitive (job->priv->numbers_font_label, can_set); + +	g_signal_connect (job->priv->line_numbers_checkbutton, +			  "toggled", +			  G_CALLBACK (line_numbers_checkbutton_toggled), +			  job); +	g_signal_connect (job->priv->text_wrapping_checkbutton, +			  "toggled", +			  G_CALLBACK (wrap_mode_checkbutton_toggled), +			  job); +	g_signal_connect (job->priv->do_not_split_checkbutton, +			  "toggled", +			  G_CALLBACK (wrap_mode_checkbutton_toggled), +			  job); +	g_signal_connect (job->priv->restore_button, +			  "clicked", +			  G_CALLBACK (restore_button_clicked), +			  job); + +	return G_OBJECT (widget); +} + +static void +custom_widget_apply_cb (GtkPrintOperation *operation, +			GtkWidget         *widget, +			GeditPrintJob     *job) +{ +	gedit_prefs_manager_set_print_syntax_hl (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->syntax_checkbutton))); + +	gedit_prefs_manager_set_print_header (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->page_header_checkbutton))); + +	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->line_numbers_checkbutton))) +	{ +		gedit_prefs_manager_set_print_line_numbers ( +			MAX (1, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (job->priv->line_numbers_spinbutton)))); +	} +	else +	{ +		gedit_prefs_manager_set_print_line_numbers (0); +	} + +	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->text_wrapping_checkbutton))) +	{ +		gedit_prefs_manager_set_print_wrap_mode (GTK_WRAP_NONE); +	} +	else +	{ +		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (job->priv->do_not_split_checkbutton))) +		{ +			gedit_prefs_manager_set_print_wrap_mode (GTK_WRAP_WORD); +		} +		else +		{ +			gedit_prefs_manager_set_print_wrap_mode (GTK_WRAP_CHAR); +		}	 +	} + +	gedit_prefs_manager_set_print_font_body (gtk_font_button_get_font_name (GTK_FONT_BUTTON (job->priv->body_fontbutton))); +	gedit_prefs_manager_set_print_font_header (gtk_font_button_get_font_name (GTK_FONT_BUTTON (job->priv->headers_fontbutton))); +	gedit_prefs_manager_set_print_font_numbers (gtk_font_button_get_font_name (GTK_FONT_BUTTON (job->priv->numbers_fontbutton))); +} + +static void +create_compositor (GeditPrintJob *job) +{ +	gchar *print_font_body; +	gchar *print_font_header; +	gchar *print_font_numbers; +	 +	/* Create and initialize print compositor */ +	print_font_body = gedit_prefs_manager_get_print_font_body (); +	print_font_header = gedit_prefs_manager_get_print_font_header (); +	print_font_numbers = gedit_prefs_manager_get_print_font_numbers (); +	 +	job->priv->compositor = GTK_SOURCE_PRINT_COMPOSITOR ( +					g_object_new (GTK_TYPE_SOURCE_PRINT_COMPOSITOR, +						     "buffer", GTK_SOURCE_BUFFER (job->priv->doc), +						     "tab-width", gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (job->priv->view)), +						     "highlight-syntax", gtk_source_buffer_get_highlight_syntax (GTK_SOURCE_BUFFER (job->priv->doc)) && +					   				 gedit_prefs_manager_get_print_syntax_hl (), +						     "wrap-mode", gedit_prefs_manager_get_print_wrap_mode (), +						     "print-line-numbers", gedit_prefs_manager_get_print_line_numbers (), +						     "print-header", gedit_prefs_manager_get_print_header (), +						     "print-footer", FALSE, +						     "body-font-name", print_font_body, +						     "line-numbers-font-name", print_font_numbers, +						     "header-font-name", print_font_header, +						     NULL)); + +	g_free (print_font_body); +	g_free (print_font_header); +	g_free (print_font_numbers); +	 +	if (gedit_prefs_manager_get_print_header ()) +	{ +		gchar *doc_name; +		gchar *name_to_display; +		gchar *left; + +		doc_name = gedit_document_get_uri_for_display (job->priv->doc); +		name_to_display = gedit_utils_str_middle_truncate (doc_name, 60); + +		left = g_strdup_printf (_("File: %s"), name_to_display); + +		/* Translators: %N is the current page number, %Q is the total +		 * number of pages (ex. Page 2 of 10)  +		 */ +		gtk_source_print_compositor_set_header_format (job->priv->compositor, +							       TRUE, +							       left, +							       NULL, +							       _("Page %N of %Q")); + +		g_free (doc_name); +		g_free (name_to_display); +		g_free (left); +	}		 +} + +static void +begin_print_cb (GtkPrintOperation *operation,  +	        GtkPrintContext   *context, +	        GeditPrintJob     *job) +{ +	create_compositor (job); + +	job->priv->status = GEDIT_PRINT_JOB_STATUS_PAGINATING; + +	job->priv->progress = 0.0; +	 +	g_signal_emit (job, print_job_signals[PRINTING], 0, job->priv->status); +} + +static void +preview_ready (GtkPrintOperationPreview *gtk_preview, +	       GtkPrintContext          *context, +	       GeditPrintJob            *job) +{ +	job->priv->is_preview = TRUE; + +	g_signal_emit (job, print_job_signals[SHOW_PREVIEW], 0, job->priv->preview); +} + +static void +preview_destroyed (GtkWidget                *preview, +		   GtkPrintOperationPreview *gtk_preview) +{ +	gtk_print_operation_preview_end_preview (gtk_preview); +} + +static gboolean  +preview_cb (GtkPrintOperation        *op, +	    GtkPrintOperationPreview *gtk_preview, +	    GtkPrintContext          *context, +	    GtkWindow                *parent, +	    GeditPrintJob            *job) +{ +	job->priv->preview = gedit_print_preview_new (op, gtk_preview, context); + +	g_signal_connect_after (gtk_preview, +			        "ready", +				G_CALLBACK (preview_ready), +				job); + +	/* FIXME: should this go in the preview widget itself? */ +	g_signal_connect (job->priv->preview, +			  "destroy", +			  G_CALLBACK (preview_destroyed), +			  gtk_preview); + +	return TRUE; +} + +static gboolean +paginate_cb (GtkPrintOperation *operation,  +	     GtkPrintContext   *context, +	     GeditPrintJob     *job) +{ +	gboolean res;	 +	 +	job->priv->status = GEDIT_PRINT_JOB_STATUS_PAGINATING; +	 +	res = gtk_source_print_compositor_paginate (job->priv->compositor, context); +		 +	if (res) +	{ +		gint n_pages; + +		n_pages = gtk_source_print_compositor_get_n_pages (job->priv->compositor); +		gtk_print_operation_set_n_pages (job->priv->operation, n_pages); +	} + +	job->priv->progress = gtk_source_print_compositor_get_pagination_progress (job->priv->compositor); + +	/* When previewing, the progress is just for pagination, when printing +	 * it's split between pagination and rendering */ +	if (!job->priv->is_preview) +		job->priv->progress /= 2.0; + +	g_signal_emit (job, print_job_signals[PRINTING], 0, job->priv->status); + +	return res; +} + +static void +draw_page_cb (GtkPrintOperation *operation, +	      GtkPrintContext   *context, +	      gint               page_nr, +	      GeditPrintJob     *job) +{ +	gint n_pages; + +	/* In preview, pages are drawn on the fly, so rendering is +	 * not part of the progress */ +	if (!job->priv->is_preview) +	{ +		g_free (job->priv->status_string); +	 +		n_pages = gtk_source_print_compositor_get_n_pages (job->priv->compositor); +	 +		job->priv->status = GEDIT_PRINT_JOB_STATUS_DRAWING; +	 +		job->priv->status_string = g_strdup_printf ("Rendering page %d of %d...",  +							    page_nr + 1, +							    n_pages); +	 +		job->priv->progress = page_nr / (2.0 * n_pages) + 0.5; + +		g_signal_emit (job, print_job_signals[PRINTING], 0, job->priv->status); +	}	 + +	gtk_source_print_compositor_draw_page (job->priv->compositor, context, page_nr); +} + +static void +end_print_cb (GtkPrintOperation *operation,  +	      GtkPrintContext   *context, +	      GeditPrintJob     *job) +{ +	g_object_unref (job->priv->compositor); +	job->priv->compositor = NULL; +} + +static void +done_cb (GtkPrintOperation       *operation, +	 GtkPrintOperationResult  result, +	 GeditPrintJob           *job) +{ +	GError *error = NULL; +	GeditPrintJobResult print_result; + +	switch (result)  +	{ +		case GTK_PRINT_OPERATION_RESULT_CANCEL: +			print_result = GEDIT_PRINT_JOB_RESULT_CANCEL; +			break; + +		case GTK_PRINT_OPERATION_RESULT_APPLY: +			print_result = GEDIT_PRINT_JOB_RESULT_OK; +			break; + +		case GTK_PRINT_OPERATION_RESULT_ERROR: +			print_result = GEDIT_PRINT_JOB_RESULT_ERROR; +			gtk_print_operation_get_error (operation, &error); +			break; + +		default: +			g_return_if_reached (); +	} +	 +	/* Avoid job is destroyed in the handler of the "done" message */ +	g_object_ref (job); +	 +	g_signal_emit (job, print_job_signals[DONE], 0, print_result, error); +	 +	g_object_unref (operation); +	job->priv->operation = NULL; +	 +	g_object_unref (job); +} + +/* Note that gedit_print_job_print can can only be called once on a given GeditPrintJob */ +GtkPrintOperationResult	  +gedit_print_job_print (GeditPrintJob            *job, +		       GtkPrintOperationAction   action, +		       GtkPageSetup             *page_setup, +		       GtkPrintSettings         *settings, +		       GtkWindow                *parent, +		       GError                  **error) +{ +	GeditPrintJobPrivate *priv; +	gchar *job_name; + +	g_return_val_if_fail (job->priv->compositor == NULL, GTK_PRINT_OPERATION_RESULT_ERROR); + +	priv = job->priv; + +	/* Check if we are previewing */ +	priv->is_preview = (action == GTK_PRINT_OPERATION_ACTION_PREVIEW); + +	/* Create print operation */ +	job->priv->operation = gtk_print_operation_new (); + +	if (settings) +		gtk_print_operation_set_print_settings (priv->operation, +							settings); + +	if (page_setup != NULL) +		gtk_print_operation_set_default_page_setup (priv->operation, +							    page_setup); + +	job_name = gedit_document_get_short_name_for_display (priv->doc); +	gtk_print_operation_set_job_name (priv->operation, job_name); +	g_free (job_name); + +#if GTK_CHECK_VERSION (2, 17, 4) +	gtk_print_operation_set_embed_page_setup (priv->operation, TRUE); +#endif + +	gtk_print_operation_set_custom_tab_label (priv->operation, +						  _("Text Editor")); + +	gtk_print_operation_set_allow_async (priv->operation, TRUE); + +	g_signal_connect (priv->operation, +			  "create-custom-widget",  +			  G_CALLBACK (create_custom_widget_cb), +			  job); +	g_signal_connect (priv->operation, +			  "custom-widget-apply",  +			  G_CALLBACK (custom_widget_apply_cb),  +			  job); +  	g_signal_connect (priv->operation, +			  "begin-print",  +			  G_CALLBACK (begin_print_cb), +			  job); +	g_signal_connect (priv->operation, +			  "preview", +			  G_CALLBACK (preview_cb), +			  job); +  	g_signal_connect (priv->operation, +			  "paginate",  +			  G_CALLBACK (paginate_cb), +			  job); +	g_signal_connect (priv->operation, +			  "draw-page",  +			  G_CALLBACK (draw_page_cb), +			  job); +	g_signal_connect (priv->operation, +			  "end-print",  +			  G_CALLBACK (end_print_cb), +			  job); +	g_signal_connect (priv->operation, +			  "done",  +			  G_CALLBACK (done_cb), +			  job);			   + +	return gtk_print_operation_run (priv->operation,  +					action, +					parent, +					error); +} + +static void +gedit_print_job_init (GeditPrintJob *job) +{ +	job->priv = GEDIT_PRINT_JOB_GET_PRIVATE (job); +	 +	job->priv->status = GEDIT_PRINT_JOB_STATUS_INIT; +	 +	job->priv->status_string = g_strdup (_("Preparing...")); +} + +GeditPrintJob * +gedit_print_job_new (GeditView *view) +{ +	GeditPrintJob *job; +	 +	g_return_val_if_fail (GEDIT_IS_VIEW (view), NULL); +	 +	job = GEDIT_PRINT_JOB (g_object_new (GEDIT_TYPE_PRINT_JOB, +					     "view", view, +					      NULL)); + +	return job; +} + +void +gedit_print_job_cancel (GeditPrintJob *job) +{ +	g_return_if_fail (GEDIT_IS_PRINT_JOB (job)); + +	gtk_print_operation_cancel (job->priv->operation); +} + +const gchar * +gedit_print_job_get_status_string (GeditPrintJob *job) +{ +	g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL); +	g_return_val_if_fail (job->priv->status_string != NULL, NULL); +	 +	return job->priv->status_string; +} + +gdouble +gedit_print_job_get_progress (GeditPrintJob *job) +{ +	g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), 0.0); + +	return job->priv->progress; +} + +GtkPrintSettings * +gedit_print_job_get_print_settings (GeditPrintJob *job) +{ +	g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL); + +	return gtk_print_operation_get_print_settings (job->priv->operation); +} + +GtkPageSetup * +gedit_print_job_get_page_setup (GeditPrintJob *job) +{ +	g_return_val_if_fail (GEDIT_IS_PRINT_JOB (job), NULL); + +	return gtk_print_operation_get_default_page_setup (job->priv->operation); +} diff --git a/gedit/gedit-print-job.h b/gedit/gedit-print-job.h new file mode 100755 index 00000000..956dc730 --- /dev/null +++ b/gedit/gedit-print-job.h @@ -0,0 +1,133 @@ +/* + * gedit-print-job.h + * This file is part of gedit + * + * Copyright (C) 2000-2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2008 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PRINT_JOB_H__ +#define __GEDIT_PRINT_JOB_H__ + +#include <gtk/gtk.h> +#include <gedit/gedit-view.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PRINT_JOB              (gedit_print_job_get_type()) +#define GEDIT_PRINT_JOB(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PRINT_JOB, GeditPrintJob)) +#define GEDIT_PRINT_JOB_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PRINT_JOB, GeditPrintJobClass)) +#define GEDIT_IS_PRINT_JOB(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PRINT_JOB)) +#define GEDIT_IS_PRINT_JOB_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PRINT_JOB)) +#define GEDIT_PRINT_JOB_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PRINT_JOB, GeditPrintJobClass)) + + +typedef enum +{ +	GEDIT_PRINT_JOB_STATUS_INIT, +	GEDIT_PRINT_JOB_STATUS_PAGINATING, +	GEDIT_PRINT_JOB_STATUS_DRAWING, +	GEDIT_PRINT_JOB_STATUS_DONE +} GeditPrintJobStatus; + +typedef enum +{ +	GEDIT_PRINT_JOB_RESULT_OK, +	GEDIT_PRINT_JOB_RESULT_CANCEL, +	GEDIT_PRINT_JOB_RESULT_ERROR	 +} GeditPrintJobResult; + +/* Private structure type */ +typedef struct _GeditPrintJobPrivate GeditPrintJobPrivate; + +/* + * Main object structure + */ +typedef struct _GeditPrintJob GeditPrintJob; + + +struct _GeditPrintJob  +{ +	GObject parent; +	 +	/* <private> */ +	GeditPrintJobPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditPrintJobClass GeditPrintJobClass; + +struct _GeditPrintJobClass  +{ +	GObjectClass parent_class; + +        /* Signals */ +	void (* printing) (GeditPrintJob       *job, +	                   GeditPrintJobStatus  status); + +	void (* show_preview) (GeditPrintJob   *job, +	                       GtkWidget       *preview); + +        void (*done)      (GeditPrintJob       *job, +		           GeditPrintJobResult  result, +                           const GError        *error); +}; + +/* + * Public methods + */ +GType			 gedit_print_job_get_type		(void) G_GNUC_CONST; + +GeditPrintJob		*gedit_print_job_new			(GeditView                *view); + +void			 gedit_print_job_set_export_filename	(GeditPrintJob            *job, +								 const gchar              *filename); + +GtkPrintOperationResult	 gedit_print_job_print			(GeditPrintJob            *job, +								 GtkPrintOperationAction   action, +								 GtkPageSetup             *page_setup, +								 GtkPrintSettings         *settings, +								 GtkWindow                *parent, +								 GError                  **error); + +void			 gedit_print_job_cancel			(GeditPrintJob            *job); + +const gchar		*gedit_print_job_get_status_string	(GeditPrintJob            *job); + +gdouble			 gedit_print_job_get_progress		(GeditPrintJob            *job); + +GtkPrintSettings	*gedit_print_job_get_print_settings	(GeditPrintJob            *job); + +GtkPageSetup		*gedit_print_job_get_page_setup		(GeditPrintJob            *job); + +G_END_DECLS + +#endif /* __GEDIT_PRINT_JOB_H__ */ diff --git a/gedit/gedit-print-preferences.ui b/gedit/gedit-print-preferences.ui new file mode 100755 index 00000000..3917eeba --- /dev/null +++ b/gedit/gedit-print-preferences.ui @@ -0,0 +1,497 @@ +<?xml version="1.0"?> +<interface> +  <requires lib="gtk+" version="2.16"/> +  <!-- interface-naming-policy toplevel-contextual --> +  <object class="GtkAdjustment" id="adjustment1"> +    <property name="value">1</property> +    <property name="lower">1</property> +    <property name="upper">100</property> +    <property name="step_increment">1</property> +    <property name="page_increment">10</property> +  </object> +  <object class="GtkWindow" id="window1"> +    <property name="visible">True</property> +    <property name="title" translatable="yes">window1</property> +    <child> +      <object class="GtkHBox" id="contents"> +        <property name="visible">True</property> +        <property name="border_width">12</property> +        <property name="spacing">18</property> +        <child> +          <object class="GtkVBox" id="vbox20"> +            <property name="visible">True</property> +            <property name="orientation">vertical</property> +            <property name="spacing">18</property> +            <child> +              <object class="GtkVBox" id="vbox26"> +                <property name="visible">True</property> +                <property name="orientation">vertical</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="label33"> +                    <property name="visible">True</property> +                    <property name="xalign">0</property> +                    <property name="label" translatable="yes">Syntax Highlighting</property> +                    <attributes> +                      <attribute name="weight" value="bold"/> +                    </attributes> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkAlignment" id="alignment1"> +                    <property name="visible">True</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="left_padding">12</property> +                    <child> +                      <object class="GtkCheckButton" id="syntax_checkbutton"> +                        <property name="label" translatable="yes">Print synta_x highlighting</property> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="receives_default">False</property> +                        <property name="use_underline">True</property> +                        <property name="draw_indicator">True</property> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="expand">False</property> +                <property name="position">0</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox22"> +                <property name="visible">True</property> +                <property name="orientation">vertical</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="label27"> +                    <property name="visible">True</property> +                    <property name="xalign">0</property> +                    <property name="label" translatable="yes">Line Numbers</property> +                    <attributes> +                      <attribute name="weight" value="bold"/> +                    </attributes> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkAlignment" id="alignment2"> +                    <property name="visible">True</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="left_padding">12</property> +                    <child> +                      <object class="GtkVBox" id="vbox2"> +                        <property name="visible">True</property> +                        <property name="orientation">vertical</property> +                        <property name="spacing">6</property> +                        <child> +                          <object class="GtkCheckButton" id="line_numbers_checkbutton"> +                            <property name="label" translatable="yes">Print line nu_mbers</property> +                            <property name="visible">True</property> +                            <property name="can_focus">True</property> +                            <property name="receives_default">False</property> +                            <property name="use_underline">True</property> +                            <property name="draw_indicator">True</property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkHBox" id="line_numbers_hbox"> +                            <property name="visible">True</property> +                            <property name="spacing">6</property> +                            <child> +                              <object class="GtkLabel" id="label3"> +                                <property name="visible">True</property> +                                <property name="xalign">0.47999998927116394</property> +                                <property name="label" translatable="yes" comments="'Number every' from 'Number every 3 lines' in the 'Text Editor' tab of the print preferences.">_Number every</property> +                                <property name="use_underline">True</property> +                                <property name="mnemonic_widget">line_numbers_spinbutton</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkSpinButton" id="line_numbers_spinbutton"> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="adjustment">adjustment1</property> +                                <property name="climb_rate">1</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">1</property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkLabel" id="label4"> +                                <property name="visible">True</property> +                                <property name="label" translatable="yes" comments="'lines' from 'Number every 3 lines' in the 'Text Editor' tab of the print preferences.">lines</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">2</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="expand">False</property> +                <property name="position">1</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox24"> +                <property name="visible">True</property> +                <property name="orientation">vertical</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="label31"> +                    <property name="visible">True</property> +                    <property name="xalign">0</property> +                    <property name="label" translatable="yes">Text Wrapping</property> +                    <attributes> +                      <attribute name="weight" value="bold"/> +                    </attributes> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkAlignment" id="alignment3"> +                    <property name="visible">True</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="left_padding">12</property> +                    <child> +                      <object class="GtkVBox" id="vbox25"> +                        <property name="visible">True</property> +                        <property name="orientation">vertical</property> +                        <property name="spacing">6</property> +                        <child> +                          <object class="GtkCheckButton" id="text_wrapping_checkbutton"> +                            <property name="label" translatable="yes">Enable text _wrapping</property> +                            <property name="visible">True</property> +                            <property name="can_focus">True</property> +                            <property name="receives_default">False</property> +                            <property name="use_underline">True</property> +                            <property name="draw_indicator">True</property> +                          </object> +                          <packing> +                            <property name="expand">False</property> +                            <property name="fill">False</property> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkHBox" id="hbox20"> +                            <property name="visible">True</property> +                            <child> +                              <object class="GtkCheckButton" id="do_not_split_checkbutton"> +                                <property name="label" translatable="yes">Do not _split words over two lines</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                                <property name="draw_indicator">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="expand">False</property> +                <property name="fill">False</property> +                <property name="position">2</property> +              </packing> +            </child> +            <child> +              <object class="GtkVBox" id="vbox39"> +                <property name="visible">True</property> +                <property name="orientation">vertical</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="label45"> +                    <property name="visible">True</property> +                    <property name="xalign">0</property> +                    <property name="label" translatable="yes">Page header</property> +                    <attributes> +                      <attribute name="weight" value="bold"/> +                    </attributes> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkAlignment" id="alignment4"> +                    <property name="visible">True</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="left_padding">12</property> +                    <child> +                      <object class="GtkCheckButton" id="page_header_checkbutton"> +                        <property name="label" translatable="yes">Print page _headers</property> +                        <property name="visible">True</property> +                        <property name="can_focus">True</property> +                        <property name="receives_default">False</property> +                        <property name="use_underline">True</property> +                        <property name="draw_indicator">True</property> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="position">3</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="expand">False</property> +            <property name="position">0</property> +          </packing> +        </child> +        <child> +          <object class="GtkVBox" id="vbox36"> +            <property name="visible">True</property> +            <property name="orientation">vertical</property> +            <property name="spacing">18</property> +            <child> +              <object class="GtkVBox" id="vbox37"> +                <property name="visible">True</property> +                <property name="orientation">vertical</property> +                <property name="spacing">6</property> +                <child> +                  <object class="GtkLabel" id="label43"> +                    <property name="visible">True</property> +                    <property name="xalign">0</property> +                    <property name="label" translatable="yes">Fonts</property> +                    <attributes> +                      <attribute name="weight" value="bold"/> +                    </attributes> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="fill">False</property> +                    <property name="position">0</property> +                  </packing> +                </child> +                <child> +                  <object class="GtkAlignment" id="alignment5"> +                    <property name="visible">True</property> +                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                    <property name="left_padding">12</property> +                    <child> +                      <object class="GtkVBox" id="vbox4"> +                        <property name="visible">True</property> +                        <property name="orientation">vertical</property> +                        <property name="spacing">12</property> +                        <child> +                          <object class="GtkTable" id="fonts_table"> +                            <property name="visible">True</property> +                            <property name="n_rows">3</property> +                            <property name="n_columns">2</property> +                            <property name="column_spacing">12</property> +                            <property name="row_spacing">12</property> +                            <child> +                              <object class="GtkLabel" id="body_font_label"> +                                <property name="visible">True</property> +                                <property name="xalign">0</property> +                                <property name="label" translatable="yes">_Body:</property> +                                <property name="use_underline">True</property> +                                <property name="mnemonic_widget">body_fontbutton</property> +                              </object> +                              <packing> +                                <property name="x_options">GTK_FILL</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkLabel" id="numbers_font_label"> +                                <property name="visible">True</property> +                                <property name="xalign">0</property> +                                <property name="label" translatable="yes">_Line numbers:</property> +                                <property name="use_underline">True</property> +                                <property name="mnemonic_widget">numbers_fontbutton</property> +                              </object> +                              <packing> +                                <property name="top_attach">1</property> +                                <property name="bottom_attach">2</property> +                                <property name="x_options">GTK_FILL</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkLabel" id="headers_font_label"> +                                <property name="visible">True</property> +                                <property name="xalign">0</property> +                                <property name="label" translatable="yes">He_aders and footers:</property> +                                <property name="use_underline">True</property> +                                <property name="mnemonic_widget">headers_fontbutton</property> +                              </object> +                              <packing> +                                <property name="top_attach">2</property> +                                <property name="bottom_attach">3</property> +                                <property name="x_options">GTK_FILL</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkFontButton" id="body_fontbutton"> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_font">True</property> +                                <property name="show_style">False</property> +                              </object> +                              <packing> +                                <property name="left_attach">1</property> +                                <property name="right_attach">2</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkFontButton" id="numbers_fontbutton"> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_font">True</property> +                                <property name="show_style">False</property> +                              </object> +                              <packing> +                                <property name="left_attach">1</property> +                                <property name="right_attach">2</property> +                                <property name="top_attach">1</property> +                                <property name="bottom_attach">2</property> +                                <property name="x_options">GTK_FILL</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                            <child> +                              <object class="GtkFontButton" id="headers_fontbutton"> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_font">True</property> +                                <property name="show_style">False</property> +                              </object> +                              <packing> +                                <property name="left_attach">1</property> +                                <property name="right_attach">2</property> +                                <property name="top_attach">2</property> +                                <property name="bottom_attach">3</property> +                                <property name="x_options">GTK_FILL</property> +                                <property name="y_options"></property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">0</property> +                          </packing> +                        </child> +                        <child> +                          <object class="GtkHButtonBox" id="hbuttonbox1"> +                            <property name="visible">True</property> +                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> +                            <property name="layout_style">end</property> +                            <child> +                              <object class="GtkButton" id="restore_button"> +                                <property name="label" translatable="yes">_Restore Default Fonts</property> +                                <property name="visible">True</property> +                                <property name="can_focus">True</property> +                                <property name="receives_default">False</property> +                                <property name="use_underline">True</property> +                              </object> +                              <packing> +                                <property name="expand">False</property> +                                <property name="fill">False</property> +                                <property name="position">0</property> +                              </packing> +                            </child> +                          </object> +                          <packing> +                            <property name="position">1</property> +                          </packing> +                        </child> +                      </object> +                    </child> +                  </object> +                  <packing> +                    <property name="expand">False</property> +                    <property name="position">1</property> +                  </packing> +                </child> +              </object> +              <packing> +                <property name="position">0</property> +              </packing> +            </child> +          </object> +          <packing> +            <property name="expand">False</property> +            <property name="fill">False</property> +            <property name="position">1</property> +          </packing> +        </child> +      </object> +    </child> +  </object> +</interface> diff --git a/gedit/gedit-print-preview.c b/gedit/gedit-print-preview.c new file mode 100755 index 00000000..293a930b --- /dev/null +++ b/gedit/gedit-print-preview.c @@ -0,0 +1,1327 @@ +/* + * gedit-print-preview.c + * + * Copyright (C) 2008 Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2006. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id: gedit-commands-search.c 5931 2007-09-25 20:05:40Z pborelli $ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <math.h> +#include <stdlib.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include <cairo-pdf.h> + +#include "gedit-print-preview.h" + +#define PRINTER_DPI (72.) + +struct _GeditPrintPreviewPrivate +{ +	GtkPrintOperation *operation; +	GtkPrintContext *context; +	GtkPrintOperationPreview *gtk_preview; + +	GtkWidget *layout; +	GtkWidget *scrolled_window; + +	GtkToolItem *next; +	GtkToolItem *prev; +	GtkWidget   *page_entry; +	GtkWidget   *last; +	GtkToolItem *multi; +	GtkToolItem *zoom_one; +	GtkToolItem *zoom_fit; +	GtkToolItem *zoom_in; +	GtkToolItem *zoom_out; + +	/* real size of the page in inches */ +	double paper_w; +	double paper_h; +	double dpi; + +	double scale; + +	/* size of the tile of a page (including padding +	 * and drop shadow) in pixels */ +	gint tile_w; +	gint tile_h; + +	GtkPageOrientation orientation; + +	/* multipage support */ +	gint rows; +	gint cols; + +	guint n_pages; +	guint cur_page; +}; + +G_DEFINE_TYPE (GeditPrintPreview, gedit_print_preview, GTK_TYPE_VBOX) + +static void  +gedit_print_preview_get_property (GObject    *object, +				  guint       prop_id, +				  GValue     *value, +				  GParamSpec *pspec) +{ +	//GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (object); +	 +	switch (prop_id) +	{ +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void  +gedit_print_preview_set_property (GObject      *object, +				  guint	        prop_id, +				  const GValue *value, +				  GParamSpec   *pspec) +{ +	//GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (object); +	 +	switch (prop_id) +	{ +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_print_preview_finalize (GObject *object) +{ +	//GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (object); + +	G_OBJECT_CLASS (gedit_print_preview_parent_class)->finalize (object); +} + +static void +gedit_print_preview_grab_focus (GtkWidget *widget) +{ +	GeditPrintPreview *preview; + +	preview = GEDIT_PRINT_PREVIEW (widget); + +	gtk_widget_grab_focus (GTK_WIDGET (preview->priv->layout)); +} + +static void						  +gedit_print_preview_class_init (GeditPrintPreviewClass *klass) +{ +	GObjectClass *object_class; +	GtkWidgetClass *widget_class; + +	object_class = G_OBJECT_CLASS (klass); +	widget_class = GTK_WIDGET_CLASS (klass); + +	object_class->get_property = gedit_print_preview_get_property; +	object_class->set_property = gedit_print_preview_set_property; +	object_class->finalize = gedit_print_preview_finalize; + +	widget_class->grab_focus = gedit_print_preview_grab_focus; + +	g_type_class_add_private (object_class, sizeof(GeditPrintPreviewPrivate));	 +} + +static void +update_layout_size (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; + +	priv = preview->priv; + +	/* force size of the drawing area to make the scrolled window work */ +	gtk_layout_set_size (GTK_LAYOUT (priv->layout), +			     priv->tile_w * priv->cols, +			     priv->tile_h * priv->rows); + +	gtk_widget_queue_draw (preview->priv->layout); +} + +static void +set_rows_and_cols (GeditPrintPreview *preview, +		   gint	              rows, +		   gint	              cols) +{ +	/* TODO: set the zoom appropriately */ + +	preview->priv->rows = rows; +	preview->priv->cols = cols; +	update_layout_size (preview); +} + +/* get the paper size in points: these must be used only + * after the widget has been mapped and the dpi is known */ + +static double +get_paper_width (GeditPrintPreview *preview) +{ +	return preview->priv->paper_w * preview->priv->dpi; +} + +static double +get_paper_height (GeditPrintPreview *preview) +{ +	return preview->priv->paper_h * preview->priv->dpi; +} + +#define PAGE_PAD 12 +#define PAGE_SHADOW_OFFSET 5  + +/* The tile size is the size of the area where a page + * will be drawn including the padding and idependent + * of the orientation */ + +/* updates the tile size to the current zoom and page size */ +static void +update_tile_size (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv;	 +	gint w, h; + +	priv = preview->priv; + +	w = 2 * PAGE_PAD + floor (priv->scale * get_paper_width (preview) + 0.5); +	h = 2 * PAGE_PAD + floor (priv->scale * get_paper_height (preview) + 0.5); + +	if ((priv->orientation == GTK_PAGE_ORIENTATION_LANDSCAPE) || +	    (priv->orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)) +	{ +		priv->tile_w = h; +		priv->tile_h = w; +	} +	else +	{ +		priv->tile_w = w; +		priv->tile_h = h;	 +	} +} + +/* Zoom should always be set with one of these two function + * so that the tile size is properly updated */ + +static void +set_zoom_factor (GeditPrintPreview *preview, +		 double	            zoom) +{ +	GeditPrintPreviewPrivate *priv;	 + +	priv = preview->priv; + +	priv->scale = zoom; + +	update_tile_size (preview); +	update_layout_size (preview); +} + +static void +set_zoom_fit_to_size (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv;	 +	double width, height; +	double p_width, p_height; +	double zoomx, zoomy; + +	priv = preview->priv; + +	g_object_get (gtk_layout_get_hadjustment (GTK_LAYOUT (priv->layout)), +		      "page-size", &width, +		      NULL); +	g_object_get (gtk_layout_get_vadjustment (GTK_LAYOUT (priv->layout)), +		      "page-size", &height, +		      NULL); + +	width /= priv->cols; +	height /= priv->rows; + +	if ((priv->orientation == GTK_PAGE_ORIENTATION_LANDSCAPE) || +	    (priv->orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)) +	{ +		p_width = get_paper_height (preview); +		p_height = get_paper_width (preview); +	} +	else +	{ +		p_width = get_paper_width (preview); +		p_height = get_paper_height (preview); +	} + +	zoomx = MAX (1, width - 2 * PAGE_PAD) / p_width; +	zoomy = MAX (1, height - 2 * PAGE_PAD) / p_height; + +	if (zoomx <= zoomy) +	{ +		priv->tile_w = width; +		priv->tile_h = floor (0.5 + width * (p_height / p_width)); +		priv->scale = zoomx; +	} +	else +	{ +		priv->tile_w = floor (0.5 + height * (p_width / p_height)); +		priv->tile_h = height; +		priv->scale = zoomy; +	} + +	update_layout_size (preview); +} + +#define ZOOM_IN_FACTOR (1.2) +#define ZOOM_OUT_FACTOR (1.0 / ZOOM_IN_FACTOR) + +static void +zoom_in (GeditPrintPreview *preview) +{ +	set_zoom_factor (preview, +			 preview->priv->scale * ZOOM_IN_FACTOR); +} + +static void +zoom_out (GeditPrintPreview *preview) +{ +	set_zoom_factor (preview, +			 preview->priv->scale * ZOOM_OUT_FACTOR); +} + +static void +goto_page (GeditPrintPreview *preview, gint page) +{ +	gchar c[32]; + +	g_snprintf (c, 32, "%d", page + 1); +	gtk_entry_set_text (GTK_ENTRY (preview->priv->page_entry), c); + +	gtk_widget_set_sensitive (GTK_WIDGET (preview->priv->prev), +				  (page > 0) && (preview->priv->n_pages > 1)); +	gtk_widget_set_sensitive (GTK_WIDGET (preview->priv->next), +				  (page != (preview->priv->n_pages - 1)) && +				  (preview->priv->n_pages > 1)); + +	if (page != preview->priv->cur_page) +	{ +		preview->priv->cur_page = page; +		if (preview->priv->n_pages > 0) +			gtk_widget_queue_draw (preview->priv->layout); +	} +} + +static void +prev_button_clicked (GtkWidget         *button, +		     GeditPrintPreview *preview) +{ +	GdkEvent *event; +	gint page; + +	event = gtk_get_current_event (); + +	if (event->button.state & GDK_SHIFT_MASK) +		page = 0; +	else +		page = preview->priv->cur_page - preview->priv->rows * preview->priv->cols; + + 	goto_page (preview, MAX (page, 0)); + +	gdk_event_free (event); +} + +static void +next_button_clicked (GtkWidget         *button, +		     GeditPrintPreview *preview) +{ +	GdkEvent *event; +	gint page; + +	event = gtk_get_current_event (); + +	if (event->button.state & GDK_SHIFT_MASK) +		page = preview->priv->n_pages - 1; +	else +		page = preview->priv->cur_page + preview->priv->rows * preview->priv->cols; + + 	goto_page (preview, MIN (page, preview->priv->n_pages - 1)); + +	gdk_event_free (event); +} + +static void +page_entry_activated (GtkEntry          *entry, +		      GeditPrintPreview *preview) +{ +	const gchar *text; +	gint page; + +	text = gtk_entry_get_text (entry); + +	page = CLAMP (atoi (text), 1, preview->priv->n_pages) - 1; +	goto_page (preview, page); + +	gtk_widget_grab_focus (GTK_WIDGET (preview->priv->layout)); +} + +static void +page_entry_insert_text (GtkEditable *editable, +			const gchar *text, +			gint         length, +			gint        *position) +{ +	gunichar c; +	const gchar *p; + 	const gchar *end; + +	p = text; +	end = text + length; + +	while (p != end) +	{ +		const gchar *next; +		next = g_utf8_next_char (p); + +		c = g_utf8_get_char (p); + +		if (!g_unichar_isdigit (c)) +		{ +			g_signal_stop_emission_by_name (editable, "insert-text"); +			break; +		} + +		p = next; +	} +} + +static gboolean  +page_entry_focus_out (GtkWidget         *widget, +		      GdkEventFocus     *event, +		      GeditPrintPreview *preview) +{ +	const gchar *text; +	gint page; + +	text = gtk_entry_get_text (GTK_ENTRY (widget)); +	page = atoi (text) - 1; + +	/* Reset the page number only if really needed */ +	if (page != preview->priv->cur_page) +	{ +		gchar *str; + +		str = g_strdup_printf ("%d", preview->priv->cur_page + 1); +		gtk_entry_set_text (GTK_ENTRY (widget), str); +		g_free (str); +	} + +	return FALSE; +} + +static void +on_1x1_clicked (GtkMenuItem *i, GeditPrintPreview *preview) +{ +	set_rows_and_cols (preview, 1, 1); +} + +static void +on_1x2_clicked (GtkMenuItem *i, GeditPrintPreview *preview) +{ +	set_rows_and_cols (preview, 1, 2); +} + +static void +on_2x1_clicked (GtkMenuItem *i, GeditPrintPreview *preview) +{ +	set_rows_and_cols (preview, 2, 1); +} + +static void +on_2x2_clicked (GtkMenuItem *i, GeditPrintPreview *preview) +{ +	set_rows_and_cols (preview, 2, 2); +} + +static void +multi_button_clicked (GtkWidget	 *button, +		      GeditPrintPreview *preview) +{ +	GtkWidget *m, *i; + +	m = gtk_menu_new (); +	gtk_widget_show (m); +	g_signal_connect (m, +			 "selection_done", +			  G_CALLBACK (gtk_widget_destroy), +			  m); + +	i = gtk_menu_item_new_with_label ("1x1"); +	gtk_widget_show (i); +	gtk_menu_attach (GTK_MENU (m), i, 0, 1, 0, 1); +	g_signal_connect (i, "activate", G_CALLBACK (on_1x1_clicked), preview); + +	i = gtk_menu_item_new_with_label ("2x1"); +	gtk_widget_show (i); +	gtk_menu_attach (GTK_MENU (m), i, 0, 1, 1, 2); +	g_signal_connect (i, "activate", G_CALLBACK (on_2x1_clicked), preview); + +	i = gtk_menu_item_new_with_label ("1x2"); +	gtk_widget_show (i); +	gtk_menu_attach (GTK_MENU (m), i, 1, 2, 0, 1); +	g_signal_connect (i, "activate", G_CALLBACK (on_1x2_clicked), preview); + +	i = gtk_menu_item_new_with_label ("2x2"); +	gtk_widget_show (i); +	gtk_menu_attach (GTK_MENU (m), i, 1, 2, 1, 2); +	g_signal_connect (i, "activate", G_CALLBACK (on_2x2_clicked), preview); + +	gtk_menu_popup (GTK_MENU (m), +			NULL, NULL, NULL, preview, 0, +			GDK_CURRENT_TIME); +} + +static void +zoom_one_button_clicked (GtkWidget         *button, +			 GeditPrintPreview *preview) +{ +	set_zoom_factor (preview, 1); +} + +static void +zoom_fit_button_clicked (GtkWidget         *button, +			 GeditPrintPreview *preview) +{ +	set_zoom_fit_to_size (preview); +} + +static void +zoom_in_button_clicked (GtkWidget         *button, +			GeditPrintPreview *preview) +{ +	zoom_in (preview); +} + +static void +zoom_out_button_clicked (GtkWidget         *button, +			 GeditPrintPreview *preview) +{ +	zoom_out (preview); +} + +static void +close_button_clicked (GtkWidget         *button, +		      GeditPrintPreview *preview) +{ +	gtk_widget_destroy (GTK_WIDGET (preview)); +} + +static void +create_bar (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; +	GtkWidget *toolbar; +	GtkToolItem *i; +	AtkObject *atko; +	GtkWidget *status; + +	priv = preview->priv; + +	toolbar = gtk_toolbar_new (); +	gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), +			       GTK_TOOLBAR_BOTH_HORIZ); +	gtk_widget_show (toolbar); +	gtk_box_pack_start (GTK_BOX (preview), +			    toolbar, +			    FALSE, FALSE, 0); + +	priv->prev = gtk_tool_button_new_from_stock (GTK_STOCK_GO_BACK); +	gtk_tool_button_set_label (GTK_TOOL_BUTTON (priv->prev), +				   "P_revious Page"); +	gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->prev), TRUE); +	gtk_tool_item_set_tooltip_text (priv->prev, _("Show the previous page")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->prev, -1); +	g_signal_connect (priv->prev, +			  "clicked", +			  G_CALLBACK (prev_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->prev)); + +	priv->next = gtk_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD); +	gtk_tool_button_set_label (GTK_TOOL_BUTTON (priv->next), +				   "_Next Page"); +	gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->next), TRUE); +	gtk_tool_item_set_tooltip_text (priv->next, _("Show the next page")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->next, -1); +	g_signal_connect (priv->next, +			  "clicked", +			  G_CALLBACK (next_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->next)); + +	i = gtk_separator_tool_item_new (); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); + +	status = gtk_hbox_new (FALSE, 4); +	priv->page_entry = gtk_entry_new (); +	gtk_entry_set_width_chars (GTK_ENTRY (priv->page_entry), 3); +	gtk_entry_set_max_length (GTK_ENTRY (priv->page_entry), 6); +	gtk_widget_set_tooltip_text (priv->page_entry, _("Current page (Alt+P)")); + +	g_signal_connect (priv->page_entry, +			  "activate",  +			  G_CALLBACK (page_entry_activated), +			  preview); +	g_signal_connect (priv->page_entry, +			  "insert-text",  +			  G_CALLBACK (page_entry_insert_text), +			  NULL); +	g_signal_connect (priv->page_entry, +			  "focus-out-event",  +			  G_CALLBACK (page_entry_focus_out), +			  preview); + +	gtk_box_pack_start (GTK_BOX (status), +			    priv->page_entry, +			    FALSE, FALSE, 0); +	/* gtk_label_set_mnemonic_widget ((GtkLabel *) l, mp->priv->page_entry); */ + +	/* We are displaying 'XXX of XXX'. */ +	gtk_box_pack_start (GTK_BOX (status), +	                    /* Translators: the "of" from "1 of 19" in print preview. */ +			    gtk_label_new (_("of")), +			    FALSE, FALSE, 0); + +	priv->last = gtk_label_new (""); +	gtk_box_pack_start (GTK_BOX (status), +			    priv->last, +			    FALSE, FALSE, 0); +	atko = gtk_widget_get_accessible (priv->last); +	atk_object_set_name (atko, _("Page total")); +	atk_object_set_description (atko, _("The total number of pages in the document")); + +	gtk_widget_show_all (status); + +	i = gtk_tool_item_new (); +	gtk_container_add (GTK_CONTAINER (i), status); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); + +	i = gtk_separator_tool_item_new (); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); +	 +	priv->multi = gtk_tool_button_new_from_stock (GTK_STOCK_DND_MULTIPLE); +	gtk_tool_button_set_label (GTK_TOOL_BUTTON (priv->multi), +				   "_Show Multiple Pages"); +	gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->multi), TRUE); +	gtk_tool_item_set_tooltip_text (priv->multi, _("Show multiple pages")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->multi, -1); +	g_signal_connect (priv->multi, +			  "clicked", +			  G_CALLBACK (multi_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->multi)); + +	i = gtk_separator_tool_item_new (); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); + +	priv->zoom_one = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_100); +	gtk_tool_item_set_tooltip_text (priv->zoom_one, _("Zoom 1:1")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->zoom_one, -1); +	g_signal_connect (priv->zoom_one, +			  "clicked", +			  G_CALLBACK (zoom_one_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->zoom_one)); + +	priv->zoom_fit = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_FIT); +	gtk_tool_item_set_tooltip_text (priv->zoom_fit,	_("Zoom to fit the whole page")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->zoom_fit, -1); +	g_signal_connect (priv->zoom_fit, +			  "clicked", +			  G_CALLBACK (zoom_fit_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->zoom_fit)); + +	priv->zoom_in = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_IN); +	gtk_tool_item_set_tooltip_text (priv->zoom_in, _("Zoom the page in")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->zoom_in, -1); +	g_signal_connect (priv->zoom_in, +			  "clicked", +			  G_CALLBACK (zoom_in_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->zoom_in)); + +	priv->zoom_out = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_OUT); +	gtk_tool_item_set_tooltip_text (priv->zoom_out, _("Zoom the page out")); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), priv->zoom_out, -1); +	g_signal_connect (priv->zoom_out, +			  "clicked", +			  G_CALLBACK (zoom_out_button_clicked), +			  preview); +	gtk_widget_show (GTK_WIDGET (priv->zoom_out)); + +	i = gtk_separator_tool_item_new (); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); + +	i = gtk_tool_button_new (NULL, _("_Close Preview")); +	gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (i), TRUE); +	gtk_tool_item_set_is_important (i, TRUE); +	gtk_tool_item_set_tooltip_text (i, _("Close print preview")); +	g_signal_connect (i, "clicked", +			  G_CALLBACK (close_button_clicked), preview); +	gtk_widget_show (GTK_WIDGET (i)); +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), i, -1); +} + +static gint +get_first_page_displayed (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; + +	priv = preview->priv; + +	return priv->cur_page - priv->cur_page % (priv->cols * priv->rows); +} + +/* returns the page number (starting from 0) or -1 if no page */ +static gint +get_page_at_coords (GeditPrintPreview *preview, +		    gint               x, +		    gint               y) +{ +	GeditPrintPreviewPrivate *priv; +	GtkAdjustment *hadj, *vadj; +	gint r, c, pg; + +	priv = preview->priv; + +	if (priv->tile_h <= 0 || priv->tile_h <= 0) +		return -1; + +	hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (priv->layout)); +	vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (priv->layout)); + +	x += gtk_adjustment_get_value (hadj); +	y += gtk_adjustment_get_value (vadj); + +	r = 1 + y / (priv->tile_h); +	c = 1 + x / (priv->tile_w); + +	if (c > priv->cols) +		return -1; + +	pg = get_first_page_displayed (preview) - 1; +	pg += (r - 1) * priv->cols + c; + +	if (pg >= priv->n_pages) +		return -1; + +	/* FIXME: we could try to be picky and check +	 * if we actually are inside the page */ +	return pg;  +} + +static gboolean +preview_layout_query_tooltip (GtkWidget         *widget, +			      gint               x, +			      gint               y, +			      gboolean           keyboard_tip, +			      GtkTooltip        *tooltip, +			      GeditPrintPreview *preview) +{ +	gint pg; +	gchar *tip; + +	pg = get_page_at_coords (preview, x, y); +	if (pg < 0) +		return FALSE; + +	tip = g_strdup_printf (_("Page %d of %d"), pg + 1, preview->priv->n_pages); +	gtk_tooltip_set_text (tooltip, tip); +	g_free (tip); + +	return TRUE; +} + +static gint +preview_layout_key_press (GtkWidget         *widget, +			  GdkEventKey       *event, +			  GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; +	GtkAdjustment *hadj, *vadj; +	double x, y; +	guint h, w; +	double hlower, hupper, vlower, vupper; +	double hpage, vpage; +	double hstep, vstep; +	gboolean domove = FALSE; +	gboolean ret = TRUE; + +	priv = preview->priv; + +	hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (priv->layout)); +	vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (priv->layout)); + +	x = gtk_adjustment_get_value (hadj); +	y = gtk_adjustment_get_value (vadj); + +	g_object_get (hadj, +		      "lower", &hlower, +		      "upper", &hupper, +		      "page-size", &hpage, +		      NULL); +	g_object_get (vadj, +		      "lower", &vlower, +		      "upper", &vupper, +		      "page-size", &vpage, +		      NULL); + +	gtk_layout_get_size (GTK_LAYOUT (priv->layout), &w, &h); + +	hstep = 10; +	vstep = 10; + +	switch (event->keyval) { +	case '1': +		set_zoom_fit_to_size (preview); +		break; +	case '+': +	case '=': +	case GDK_KP_Add: +		zoom_in (preview); +		break; +	case '-': +	case '_': +	case GDK_KP_Subtract: +		zoom_out (preview); +		break; +	case GDK_KP_Right: +	case GDK_Right: +		if (event->state & GDK_SHIFT_MASK) +			x = hupper - hpage; +		else +			x = MIN (hupper - hpage, x + hstep); +		domove = TRUE; +		break; +	case GDK_KP_Left: +	case GDK_Left: +		if (event->state & GDK_SHIFT_MASK) +			x = hlower; +		else +			x = MAX (hlower, x - hstep); +		domove = TRUE; +		break; +	case GDK_KP_Up: +	case GDK_Up: +		if (event->state & GDK_SHIFT_MASK) +			goto page_up; +		y = MAX (vlower, y - vstep); +		domove = TRUE; +		break; +	case GDK_KP_Down: +	case GDK_Down: +		if (event->state & GDK_SHIFT_MASK) +			goto page_down; +		y = MIN (vupper - vpage, y + vstep); +		domove = TRUE; +		break; +	case GDK_KP_Page_Up: +	case GDK_Page_Up: +	case GDK_Delete: +	case GDK_KP_Delete: +	case GDK_BackSpace: +	page_up: +		if (y <= vlower) +		{ +			if (preview->priv->cur_page > 0) +			{ +				goto_page (preview, preview->priv->cur_page - 1); +				y = (vupper - vpage); +			} +		} +		else +		{ +			y = vlower; +		} +		domove = TRUE; +		break; +	case GDK_KP_Page_Down: +	case GDK_Page_Down: +	case ' ': +	page_down: +		if (y >= (vupper - vpage)) +		{ +			if (preview->priv->cur_page < preview->priv->n_pages - 1) +			{ +				goto_page (preview, preview->priv->cur_page + 1); +				y = vlower; +			} +		} +		else +		{ +			y = (vupper - vpage); +		} +		domove = TRUE; +		break; +	case GDK_KP_Home: +	case GDK_Home: +		goto_page (preview, 0); +		y = 0; +		domove = TRUE; +		break; +	case GDK_KP_End: +	case GDK_End: +		goto_page (preview, preview->priv->n_pages - 1); +		y = 0; +		domove = TRUE; +		break; +	case GDK_Escape: +		gtk_widget_destroy (GTK_WIDGET (preview)); +		break; +	case 'c': +		if (event->state & GDK_MOD1_MASK) +		{ +			gtk_widget_destroy (GTK_WIDGET (preview)); +		} +		break; +	case 'p': +		if (event->state & GDK_MOD1_MASK) +		{ +			gtk_widget_grab_focus (preview->priv->page_entry); +		} +		break; +	default: +		/* by default do not stop the default handler */ +		ret = FALSE; +	} + +	if (domove) +	{ +		gtk_adjustment_set_value (hadj, x); +		gtk_adjustment_set_value (vadj, y); + +		gtk_adjustment_value_changed (hadj); +		gtk_adjustment_value_changed (vadj); +	} + +	return ret; +} + +static void +create_preview_layout (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; +	AtkObject *atko; + +	priv = preview->priv; + +	priv->layout = gtk_layout_new (NULL, NULL); +//	gtk_widget_set_double_buffered (priv->layout, FALSE); + +	atko = gtk_widget_get_accessible (GTK_WIDGET (priv->layout)); +	atk_object_set_name (atko, _("Page Preview")); +	atk_object_set_description (atko, _("The preview of a page in the document to be printed")); + +	gtk_widget_add_events (priv->layout, +			       GDK_POINTER_MOTION_MASK | +			       GDK_BUTTON_PRESS_MASK | +			       GDK_KEY_PRESS_MASK); + +	GTK_WIDGET_SET_FLAGS (priv->layout, GTK_CAN_FOCUS); + +  	g_signal_connect (priv->layout, +			  "key-press-event", +			  G_CALLBACK (preview_layout_key_press), +			  preview); + +	g_object_set (priv->layout, "has-tooltip", TRUE, NULL); +  	g_signal_connect (priv->layout, +			  "query-tooltip", +			  G_CALLBACK (preview_layout_query_tooltip), +			  preview); + +	priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL); +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window), +					GTK_POLICY_AUTOMATIC, +					GTK_POLICY_AUTOMATIC); + +	gtk_container_add (GTK_CONTAINER (priv->scrolled_window), priv->layout); +	gtk_box_pack_end (GTK_BOX (preview), +			  priv->scrolled_window, +			  TRUE, TRUE, 0); + +	gtk_widget_show_all (GTK_WIDGET (priv->scrolled_window)); +	gtk_widget_grab_focus (GTK_WIDGET (priv->layout)); +} + +static void +gedit_print_preview_init (GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; +	 +	priv = G_TYPE_INSTANCE_GET_PRIVATE (preview,  +					    GEDIT_TYPE_PRINT_PREVIEW, +					    GeditPrintPreviewPrivate); + +	preview->priv = priv; + +	priv->operation = NULL; +	priv->context = NULL; +	priv->gtk_preview = NULL; + +	create_bar (preview); +	create_preview_layout (preview); + +	// FIXME +	priv->cur_page = 0; +	priv->paper_w = 0; +	priv->paper_h = 0; +	priv->dpi = PRINTER_DPI; +	priv->scale = 1.0; +	priv->rows = 1; +	priv->cols = 1; +} + +static void +draw_page_content (cairo_t            *cr, +		   gint	               page_number, +		   GeditPrintPreview  *preview) +{ +	/* scale to the desired size */ +	cairo_scale (cr, preview->priv->scale, preview->priv->scale); + +	/* rotate acording to page orientation if needed */ +	if ((preview->priv->orientation == GTK_PAGE_ORIENTATION_LANDSCAPE) || +	    (preview->priv->orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)) +	{ +		cairo_matrix_t matrix; + +		cairo_matrix_init (&matrix, +				   0, -1, +				   1,  0, +				   0,  get_paper_width (preview)); +		cairo_transform (cr, &matrix); +	} + +	gtk_print_context_set_cairo_context (preview->priv->context, +					     cr, +					     preview->priv->dpi, +					     preview->priv->dpi); + +	gtk_print_operation_preview_render_page (preview->priv->gtk_preview, +						 page_number); +} + +/* For the frame, we scale and rotate manually, since + * the line width should not depend on the zoom and + * the drop shadow should be on the bottom right no matter + * the orientation */ +static void +draw_page_frame (cairo_t            *cr, +		 GeditPrintPreview  *preview) +{ +	double w, h; + +	w = get_paper_width (preview); +	h = get_paper_height (preview); + +	if ((preview->priv->orientation == GTK_PAGE_ORIENTATION_LANDSCAPE) || +	    (preview->priv->orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)) +	{ +		double tmp; + +		tmp = w; +		w = h; +		h = tmp; +	} + +	w *= preview->priv->scale; +	h *= preview->priv->scale; + +	/* drop shadow */ +	cairo_set_source_rgb (cr, 0, 0, 0); +	cairo_rectangle (cr, +			 PAGE_SHADOW_OFFSET, PAGE_SHADOW_OFFSET, +			 w, h); +	cairo_fill (cr); + +	/* page frame */ +	cairo_set_source_rgb (cr, 1, 1, 1); +	cairo_rectangle (cr, +			 0, 0, +			 w, h); +	cairo_fill_preserve (cr); +	cairo_set_source_rgb (cr, 0, 0, 0); +	cairo_set_line_width (cr, 1); +	cairo_stroke (cr); +} + +static void +draw_page (cairo_t           *cr, +	   double             x, +	   double             y, +	   gint	              page_number, +	   GeditPrintPreview *preview) +{ +	cairo_save (cr); + +	/* move to the page top left corner */ +	cairo_translate (cr, x + PAGE_PAD, y + PAGE_PAD); + +	draw_page_frame (cr, preview); +	draw_page_content (cr, page_number, preview); + +	cairo_restore (cr); +} + +static gboolean +preview_expose (GtkWidget         *widget, +		GdkEventExpose    *event, +		GeditPrintPreview *preview) +{ +	GeditPrintPreviewPrivate *priv; +	GdkWindow *bin_window; +	cairo_t *cr; +	gint pg; +	gint i, j; + +	priv = preview->priv; + +	bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (priv->layout)); +	if (event->window != bin_window) +		return FALSE; + +	cr = gdk_cairo_create (bin_window); + +	gdk_cairo_rectangle (cr, &event->area); +	cairo_clip (cr); + +	/* get the first page to display */ +	pg = get_first_page_displayed (preview); + +	for (i = 0; i < priv->cols; ++i) +	{ +		for (j = 0; j < priv->rows; ++j) +		{ +			if (!gtk_print_operation_preview_is_selected (priv->gtk_preview, +								      pg)) +			{ +				continue; +			} + +			if (pg == priv->n_pages) +				break; + +			draw_page (cr, +				   j * priv->tile_w, +				   i * priv->tile_h, +				   pg, +				   preview); + +			++pg; +		} +	} +	cairo_destroy (cr); + +	return TRUE; +} + +static double +get_screen_dpi (GeditPrintPreview *preview) +{ +	GdkScreen *screen; +	double dpi; + +	screen = gtk_widget_get_screen (GTK_WIDGET (preview));	 + +	dpi = gdk_screen_get_resolution (screen); +	if (dpi < 30. || 600. < dpi) +	{ +		g_warning ("Invalid the x-resolution for the screen, assuming 96dpi"); +		dpi = 96.; +	} + +	return dpi; +} + +static void +set_n_pages (GeditPrintPreview *preview, +	     gint               n_pages) +{ +	gchar *str; + +	preview->priv->n_pages = n_pages; + +	// FIXME: count the visible pages + +	str =  g_strdup_printf ("%d", n_pages); +	gtk_label_set_markup (GTK_LABEL (preview->priv->last), str); +	g_free (str); +} + +static void +preview_ready (GtkPrintOperationPreview *gtk_preview, +	       GtkPrintContext          *context, +	       GeditPrintPreview        *preview) +{ +	gint n_pages; + +	g_object_get (preview->priv->operation, "n-pages", &n_pages, NULL); +	set_n_pages (preview, n_pages); +	goto_page (preview, 0); + +	/* figure out the dpi */ +	preview->priv->dpi = get_screen_dpi (preview); + +	set_zoom_factor (preview, 1.0); + +	/* let the default gtklayout handler clear the background */ +	g_signal_connect_after (preview->priv->layout, +				"expose-event", +				G_CALLBACK (preview_expose), +				preview); + +	gtk_widget_queue_draw (preview->priv->layout); +} + +static void +update_paper_size (GeditPrintPreview *preview, +		   GtkPageSetup      *page_setup) +{ +	GtkPaperSize *paper_size; + +	paper_size = gtk_page_setup_get_paper_size (page_setup); + +	preview->priv->paper_w = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH); +	preview->priv->paper_h = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH); + +	preview->priv->orientation = gtk_page_setup_get_orientation (page_setup); +} + +static void +preview_got_page_size (GtkPrintOperationPreview *gtk_preview,  +		       GtkPrintContext          *context, +		       GtkPageSetup             *page_setup, +		       GeditPrintPreview        *preview) +{ +	update_paper_size (preview, page_setup); +} + +/* HACK: we need a dummy surface to paginate... can we use something simpler? */ + +static cairo_status_t +dummy_write_func (G_GNUC_UNUSED gpointer      closure, +		  G_GNUC_UNUSED const guchar *data, +		  G_GNUC_UNUSED guint         length) +{ +    return CAIRO_STATUS_SUCCESS; +} + +#define PRINTER_DPI (72.) + +static cairo_surface_t * +create_preview_surface_platform (GtkPaperSize *paper_size, +				 double       *dpi_x, +				 double       *dpi_y) +{ +    double width, height; +    cairo_surface_t *sf; + +    width = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS); +    height = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS); + +    *dpi_x = *dpi_y = PRINTER_DPI; + +    sf = cairo_pdf_surface_create_for_stream (dummy_write_func, NULL, +					      width, height); +    return sf; +} + +static cairo_surface_t * +create_preview_surface (GeditPrintPreview *preview, +			double	  *dpi_x, +			double	  *dpi_y) +{ +    GtkPageSetup *page_setup; +    GtkPaperSize *paper_size; + +    page_setup = gtk_print_context_get_page_setup (preview->priv->context); +    /* gtk_page_setup_get_paper_size swaps width and height for landscape */ +    paper_size = gtk_page_setup_get_paper_size (page_setup); + +    return create_preview_surface_platform (paper_size, dpi_x, dpi_y); +} + +GtkWidget * +gedit_print_preview_new (GtkPrintOperation        *op, +			 GtkPrintOperationPreview *gtk_preview, +			 GtkPrintContext          *context) +{ +	GeditPrintPreview *preview; +	GtkPageSetup *page_setup; +	cairo_surface_t *surface; +	cairo_t *cr; +	double dpi_x, dpi_y; + +	g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), NULL); +	g_return_val_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (gtk_preview), NULL); + +	preview = g_object_new (GEDIT_TYPE_PRINT_PREVIEW, NULL); + +	preview->priv->operation = g_object_ref (op); +	preview->priv->gtk_preview = g_object_ref (gtk_preview); +	preview->priv->context = g_object_ref (context); + +	/* FIXME: is this legal?? */ +	gtk_print_operation_set_unit (op, GTK_UNIT_POINTS); + +	g_signal_connect (gtk_preview, "ready", +			  G_CALLBACK (preview_ready), preview); +	g_signal_connect (gtk_preview, "got-page-size", +			  G_CALLBACK (preview_got_page_size), preview); + +	page_setup = gtk_print_context_get_page_setup (preview->priv->context); +	update_paper_size (preview, page_setup); + +	/* FIXME: we need a cr to paginate... but we can't get the drawing +	 * area surface because it's not there yet... for now I create  +	 * a dummy pdf surface */ + +	surface = create_preview_surface (preview, &dpi_x, &dpi_y); +	cr = cairo_create (surface); +	gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y); +	cairo_destroy (cr); +	cairo_surface_destroy (surface); + +	return GTK_WIDGET (preview); +} + diff --git a/gedit/gedit-print-preview.h b/gedit/gedit-print-preview.h new file mode 100755 index 00000000..f86ef87a --- /dev/null +++ b/gedit/gedit-print-preview.h @@ -0,0 +1,71 @@ +/* + * gedit-print-preview.h + * + * Copyright (C) 2008 Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 1998-2006. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id: gedit-commands-search.c 5931 2007-09-25 20:05:40Z pborelli $ + */ + + +#ifndef __GEDIT_PRINT_PREVIEW_H__ +#define __GEDIT_PRINT_PREVIEW_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_PRINT_PREVIEW            (gedit_print_preview_get_type ()) +#define GEDIT_PRINT_PREVIEW(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GEDIT_TYPE_PRINT_PREVIEW, GeditPrintPreview)) +#define GEDIT_PRINT_PREVIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_PRINT_PREVIEW, GeditPrintPreviewClass)) +#define GEDIT_IS_PRINT_PREVIEW(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GEDIT_TYPE_PRINT_PREVIEW)) +#define GEDIT_IS_PRINT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PRINT_PREVIEW)) +#define GEDIT_PRINT_PREVIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_PRINT_PREVIEW, GeditPrintPreviewClass)) + +typedef struct _GeditPrintPreview        GeditPrintPreview; +typedef struct _GeditPrintPreviewPrivate GeditPrintPreviewPrivate; +typedef struct _GeditPrintPreviewClass   GeditPrintPreviewClass; + +struct _GeditPrintPreview +{ +	GtkVBox parent; +	GeditPrintPreviewPrivate *priv; +}; + +struct _GeditPrintPreviewClass +{ +	GtkVBoxClass parent_class; + +	void (* close)		(GeditPrintPreview          *preview); +}; + + +GType		 gedit_print_preview_get_type	(void) G_GNUC_CONST; + +GtkWidget	*gedit_print_preview_new	(GtkPrintOperation		*op, +						 GtkPrintOperationPreview	*gtk_preview, +						 GtkPrintContext		*context); + +G_END_DECLS + +#endif /* __GEDIT_PRINT_PREVIEW_H__ */ diff --git a/gedit/gedit-progress-message-area.c b/gedit/gedit-progress-message-area.c new file mode 100755 index 00000000..d408baee --- /dev/null +++ b/gedit/gedit-progress-message-area.c @@ -0,0 +1,261 @@ +/* + * gedit-progress-message-area.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ +  + /* TODO: add properties */ +  +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#include "gedit-progress-message-area.h" + +enum { +	PROP_0, +	PROP_HAS_CANCEL_BUTTON +}; + + +#define GEDIT_PROGRESS_MESSAGE_AREA_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_PROGRESS_MESSAGE_AREA, GeditProgressMessageAreaPrivate)) + +struct _GeditProgressMessageAreaPrivate +{ +	GtkWidget *image; +	GtkWidget *label; +	GtkWidget *progress; +}; + +#if !GTK_CHECK_VERSION (2, 17, 1) +G_DEFINE_TYPE(GeditProgressMessageArea, gedit_progress_message_area, GEDIT_TYPE_MESSAGE_AREA) +#else +G_DEFINE_TYPE(GeditProgressMessageArea, gedit_progress_message_area, GTK_TYPE_INFO_BAR) +#endif + +static void +gedit_progress_message_area_set_has_cancel_button (GeditProgressMessageArea *area, +						   gboolean                  has_button) +{ +	if (has_button) +#if !GTK_CHECK_VERSION (2, 17, 1) +		gedit_message_area_add_button (GEDIT_MESSAGE_AREA (area), +					       GTK_STOCK_CANCEL, +					       GTK_RESPONSE_CANCEL); +#else +		gtk_info_bar_add_button (GTK_INFO_BAR (area), +					 GTK_STOCK_CANCEL, +					 GTK_RESPONSE_CANCEL); +#endif + +	g_object_notify (G_OBJECT (area), "has-cancel-button"); +} + +static void +gedit_progress_message_area_set_property (GObject      *object, +					  guint         prop_id, +					  const GValue *value, +					  GParamSpec   *pspec) +{ +	GeditProgressMessageArea *area; + +	area = GEDIT_PROGRESS_MESSAGE_AREA (object); + +	switch (prop_id) +	{ +	case PROP_HAS_CANCEL_BUTTON: +		gedit_progress_message_area_set_has_cancel_button (area, +								   g_value_get_boolean (value)); +		break; +	default: +		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +		break; +	} +} + +static void +gedit_progress_message_area_get_property (GObject      *object, +					  guint         prop_id, +					  GValue       *value, +					  GParamSpec   *pspec) +{ +	GeditProgressMessageArea *area; + +	area = GEDIT_PROGRESS_MESSAGE_AREA (object); + +	switch (prop_id) +	{ +	default: +		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +		break; +	} +} + +static void  +gedit_progress_message_area_class_init (GeditProgressMessageAreaClass *klass) +{ +	GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + +	gobject_class->set_property = gedit_progress_message_area_set_property; +	gobject_class->get_property = gedit_progress_message_area_get_property; + +	g_object_class_install_property (gobject_class, +					 PROP_HAS_CANCEL_BUTTON, +					 g_param_spec_boolean ("has-cancel-button", +							       "Has Cancel Button", +							       "If the message area has a cancel button", +							       TRUE, +							       G_PARAM_WRITABLE | +							       G_PARAM_CONSTRUCT_ONLY | +							       G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (gobject_class, sizeof(GeditProgressMessageAreaPrivate)); +} + +static void +gedit_progress_message_area_init (GeditProgressMessageArea *area) +{ +	GtkWidget *vbox; +	GtkWidget *hbox; +	 +	area->priv = GEDIT_PROGRESS_MESSAGE_AREA_GET_PRIVATE (area); +	 +	vbox = gtk_vbox_new (FALSE, 6); +	gtk_widget_show (vbox); + +	hbox = gtk_hbox_new (FALSE, 4); +	gtk_widget_show (hbox); +	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + +	area->priv->image = gtk_image_new_from_icon_name (GTK_STOCK_MISSING_IMAGE,  +							  GTK_ICON_SIZE_SMALL_TOOLBAR); +	gtk_widget_show (area->priv->image); +	gtk_misc_set_alignment (GTK_MISC (area->priv->image), 0.5, 0.5); +	gtk_box_pack_start (GTK_BOX (hbox), area->priv->image, FALSE, FALSE, 4); +	 +	area->priv->label = gtk_label_new (""); +	gtk_widget_show (area->priv->label); +	gtk_box_pack_start (GTK_BOX (hbox), area->priv->label, TRUE, TRUE, 0); +	gtk_label_set_use_markup (GTK_LABEL (area->priv->label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (area->priv->label), 0, 0.5); +	gtk_label_set_ellipsize (GTK_LABEL (area->priv->label),  +				 PANGO_ELLIPSIZE_END); + +	area->priv->progress = gtk_progress_bar_new (); +	gtk_widget_show (area->priv->progress); +	gtk_box_pack_start (GTK_BOX (vbox), area->priv->progress, TRUE, FALSE, 0); +	gtk_widget_set_size_request (area->priv->progress, -1, 15); + +#if !GTK_CHECK_VERSION (2, 17, 1) +	gedit_message_area_set_contents (GEDIT_MESSAGE_AREA (area), +					 vbox); +#else +	GtkWidget *content; +	 +	content = gtk_info_bar_get_content_area (GTK_INFO_BAR (area)); +	gtk_container_add (GTK_CONTAINER (content), vbox); +#endif +} + +GtkWidget * +gedit_progress_message_area_new (const gchar *stock_id, +				 const gchar *markup, +				 gboolean     has_cancel) +{ +	GeditProgressMessageArea *area; + +	g_return_val_if_fail (stock_id != NULL, NULL); +	g_return_val_if_fail (markup != NULL, NULL); + +	area = GEDIT_PROGRESS_MESSAGE_AREA (g_object_new (GEDIT_TYPE_PROGRESS_MESSAGE_AREA, +							  "has-cancel-button", has_cancel, +							  NULL)); + +	gedit_progress_message_area_set_stock_image (area, +						     stock_id); + +	gedit_progress_message_area_set_markup (area, +						markup); + +	return GTK_WIDGET (area);	 +} + +void +gedit_progress_message_area_set_stock_image (GeditProgressMessageArea *area, +					     const gchar              *stock_id) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (area)); +	g_return_if_fail (stock_id != NULL); +	 +	gtk_image_set_from_stock (GTK_IMAGE (area->priv->image), +				  stock_id, +				  GTK_ICON_SIZE_SMALL_TOOLBAR); +} + +void +gedit_progress_message_area_set_markup (GeditProgressMessageArea *area, +					const gchar              *markup) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (area)); +	g_return_if_fail (markup != NULL); + +	gtk_label_set_markup (GTK_LABEL (area->priv->label), +			      markup); +} + +void +gedit_progress_message_area_set_text (GeditProgressMessageArea *area, +				      const gchar              *text) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (area)); +	g_return_if_fail (text != NULL); + +	gtk_label_set_text (GTK_LABEL (area->priv->label), +			    text); +} + +void +gedit_progress_message_area_set_fraction (GeditProgressMessageArea *area, +					  gdouble                   fraction) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (area)); + +	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (area->priv->progress), +				       fraction); +} + +void +gedit_progress_message_area_pulse (GeditProgressMessageArea *area) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (area)); + +	gtk_progress_bar_pulse (GTK_PROGRESS_BAR (area->priv->progress)); +} diff --git a/gedit/gedit-progress-message-area.h b/gedit/gedit-progress-message-area.h new file mode 100755 index 00000000..ed5d8e32 --- /dev/null +++ b/gedit/gedit-progress-message-area.h @@ -0,0 +1,112 @@ +/* + * gedit-progress-message-area.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_PROGRESS_MESSAGE_AREA_H__ +#define __GEDIT_PROGRESS_MESSAGE_AREA_H__ + +#if !GTK_CHECK_VERSION (2, 17, 1) +#include <gedit/gedit-message-area.h> +#else +#include <gtk/gtk.h> +#endif + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_PROGRESS_MESSAGE_AREA              (gedit_progress_message_area_get_type()) +#define GEDIT_PROGRESS_MESSAGE_AREA(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_PROGRESS_MESSAGE_AREA, GeditProgressMessageArea)) +#define GEDIT_PROGRESS_MESSAGE_AREA_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_PROGRESS_MESSAGE_AREA, GeditProgressMessageAreaClass)) +#define GEDIT_IS_PROGRESS_MESSAGE_AREA(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_PROGRESS_MESSAGE_AREA)) +#define GEDIT_IS_PROGRESS_MESSAGE_AREA_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_PROGRESS_MESSAGE_AREA)) +#define GEDIT_PROGRESS_MESSAGE_AREA_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_PROGRESS_MESSAGE_AREA, GeditProgressMessageAreaClass)) + +/* Private structure type */ +typedef struct _GeditProgressMessageAreaPrivate GeditProgressMessageAreaPrivate; + +/* + * Main object structure + */ +typedef struct _GeditProgressMessageArea GeditProgressMessageArea; + +struct _GeditProgressMessageArea  +{ +#if !GTK_CHECK_VERSION (2, 17, 1) +	GeditMessageArea parent; +#else +	GtkInfoBar parent; +#endif + +	/*< private > */ +	GeditProgressMessageAreaPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditProgressMessageAreaClass GeditProgressMessageAreaClass; + +struct _GeditProgressMessageAreaClass  +{ +#if !GTK_CHECK_VERSION (2, 17, 1) +	GeditMessageAreaClass parent_class; +#else +	GtkInfoBarClass parent_class; +#endif +}; + +/* + * Public methods + */ +GType 		 gedit_progress_message_area_get_type 		(void) G_GNUC_CONST; + +GtkWidget	*gedit_progress_message_area_new      		(const gchar              *stock_id, +								 const gchar              *markup, +								 gboolean                  has_cancel); + +void		 gedit_progress_message_area_set_stock_image	(GeditProgressMessageArea *area, +								 const gchar              *stock_id); + +void		 gedit_progress_message_area_set_markup		(GeditProgressMessageArea *area, +								 const gchar              *markup); + +void		 gedit_progress_message_area_set_text		(GeditProgressMessageArea *area, +								 const gchar              *text); + +void		 gedit_progress_message_area_set_fraction	(GeditProgressMessageArea *area, +								 gdouble                   fraction); + +void		 gedit_progress_message_area_pulse		(GeditProgressMessageArea *area); +								  + +G_END_DECLS + +#endif  /* __GEDIT_PROGRESS_MESSAGE_AREA_H__  */ diff --git a/gedit/gedit-session.c b/gedit/gedit-session.c new file mode 100755 index 00000000..3dc9f126 --- /dev/null +++ b/gedit/gedit-session.c @@ -0,0 +1,601 @@ +/* + * gedit-session.c - Basic session management for gedit + * This file is part of gedit + * + * Copyright (C) 2002 Ximian, Inc. + * Copyright (C) 2005 - Paolo Maggi  + * + * Author: Federico Mena-Quintero <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <string.h> + +#include <libxml/tree.h> +#include <libxml/xmlwriter.h> + +#include "gedit-session.h" + +#include "gedit-debug.h" +#include "gedit-plugins-engine.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-metadata-manager.h" +#include "gedit-window.h" +#include "gedit-app.h" +#include "gedit-commands.h" +#include "dialogs/gedit-close-confirmation-dialog.h" +#include "smclient/eggsmclient.h" + +/* The master client we use for SM */ +static EggSMClient *master_client = NULL; + +/* global var used during quit_requested */ +static GSList *window_dirty_list; + +static void	ask_next_confirmation	(void); + +#define GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE "gedit-session-list-of-docs-to-save-key" + +static void +save_window_session (GKeyFile    *state_file, +		     const gchar *group_name, +		     GeditWindow      *window) +{ +	const gchar *role; +	int width, height; +	GeditPanel *panel; +	GList *docs, *l; +	GPtrArray *doc_array; +	GeditDocument *active_document; +	gchar *uri; + +	gedit_debug (DEBUG_SESSION); + +	role = gtk_window_get_role (GTK_WINDOW (window)); +	g_key_file_set_string (state_file, group_name, "role", role); +	gtk_window_get_size (GTK_WINDOW (window), &width, &height); +	g_key_file_set_integer (state_file, group_name, "width", width); +	g_key_file_set_integer (state_file, group_name, "height", height); + +	panel = gedit_window_get_side_panel (window); +	g_key_file_set_boolean (state_file, group_name, "side-panel-visible", +				GTK_WIDGET_VISIBLE (panel)); + +	panel = gedit_window_get_bottom_panel (window); +	g_key_file_set_boolean (state_file, group_name, "bottom-panel-visible", +				GTK_WIDGET_VISIBLE (panel)); + +	active_document = gedit_window_get_active_document (window); +	if (active_document) +	{ +	        uri = gedit_document_get_uri (active_document); +	        g_key_file_set_string (state_file, group_name, +				       "active-document", uri); +	} + +	docs = gedit_window_get_documents (window); + +	doc_array = g_ptr_array_new (); +	for (l = docs; l != NULL; l = g_list_next (l)) +	{ +		uri = gedit_document_get_uri (GEDIT_DOCUMENT (l->data)); + +		if (uri != NULL) +		        g_ptr_array_add (doc_array, uri); +			   +	} +	g_list_free (docs);	 + +	if (doc_array->len) +	{ +	        guint i; +  +		g_key_file_set_string_list (state_file, group_name, +					    "documents", +					    (const char **)doc_array->pdata, +					    doc_array->len); +		for (i = 0; i < doc_array->len; i++) +		        g_free (doc_array->pdata[i]); +	} +	g_ptr_array_free (doc_array, TRUE); +} + +static void +client_save_state_cb (EggSMClient *client, +		      GKeyFile    *state_file, +		      gpointer     user_data) +{ +        const GList *windows; +	gchar *group_name; +	int n; + +	windows = gedit_app_get_windows (gedit_app_get_default ()); +	n = 1; + +	while (windows != NULL) +	{ +	        group_name = g_strdup_printf ("gedit window %d", n); +		save_window_session (state_file, +				     group_name, +				     GEDIT_WINDOW (windows->data)); +		g_free (group_name); +		 +		windows = g_list_next (windows); +		n++; +	} +} + +static void +window_handled (GeditWindow *window) +{ +	window_dirty_list = g_slist_remove (window_dirty_list, window); + +	/* whee... we made it! */ +	if (window_dirty_list == NULL) +	        egg_sm_client_will_quit (master_client, TRUE); +	else +		ask_next_confirmation (); +} + +static void +window_state_change (GeditWindow *window, +		     GParamSpec  *pspec, +		     gpointer     data) +{ +	GeditWindowState state; +	GList *unsaved_docs; +	GList *docs_to_save; +	GList *l; +	gboolean done = TRUE; + +	state = gedit_window_get_state (window); + +	/* we are still saving */ +	if (state & GEDIT_WINDOW_STATE_SAVING) +		return; + +	unsaved_docs = gedit_window_get_unsaved_documents (window); + +	docs_to_save =	g_object_get_data (G_OBJECT (window), +					   GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE); + + +	for (l = docs_to_save; l != NULL; l = l->next) +	{ +		if (g_list_find (unsaved_docs, l->data)) +		{ +			done = FALSE; +			break; +		} +	} + +	if (done) +	{ +		g_signal_handlers_disconnect_by_func (window, window_state_change, data); +		g_list_free (docs_to_save); +		g_object_set_data (G_OBJECT (window), +				   GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE, +				   NULL); + +		window_handled (window); +	} + +	g_list_free (unsaved_docs); +} + +static void +close_confirmation_dialog_response_handler (GeditCloseConfirmationDialog *dlg, +					    gint                          response_id, +					    GeditWindow                  *window) +{ +	GList *selected_documents; +	GSList *l; + +	gedit_debug (DEBUG_COMMANDS); + +	switch (response_id) +	{ +		case GTK_RESPONSE_YES: +			/* save selected docs */ + +			g_signal_connect (window, +					  "notify::state", +					  G_CALLBACK (window_state_change), +					  NULL); + +			selected_documents = gedit_close_confirmation_dialog_get_selected_documents (dlg); + +			g_return_if_fail (g_object_get_data (G_OBJECT (window), +							     GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE) == NULL); + +			g_object_set_data (G_OBJECT (window), +					   GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE, +					   selected_documents); + +			_gedit_cmd_file_save_documents_list (window, selected_documents); + +			/* FIXME: also need to lock the window to prevent further changes... */ + +			break; + +		case GTK_RESPONSE_NO: +			/* dont save */ +			window_handled (window); +			break; + +		default: +			/* disconnect window_state_changed where needed */ +			for (l = window_dirty_list; l != NULL; l = l->next) +				g_signal_handlers_disconnect_by_func (window, +						window_state_change, NULL); +			g_slist_free (window_dirty_list); +			window_dirty_list = NULL; + +			/* cancel shutdown */ +			egg_sm_client_will_quit (master_client, FALSE); + +			break; +	} + +	gtk_widget_destroy (GTK_WIDGET (dlg)); +} + +static void +show_confirmation_dialog (GeditWindow *window) +{ +	GList *unsaved_docs; +	GtkWidget *dlg; + +	gedit_debug (DEBUG_SESSION); + +	unsaved_docs = gedit_window_get_unsaved_documents (window); + +	g_return_if_fail (unsaved_docs != NULL); + +	if (unsaved_docs->next == NULL) +	{ +		/* There is only one unsaved document */ +		GeditTab *tab; +		GeditDocument *doc; + +		doc = GEDIT_DOCUMENT (unsaved_docs->data); + +		tab = gedit_tab_get_from_document (doc); +		g_return_if_fail (tab != NULL); + +		gedit_window_set_active_tab (window, tab); + +		dlg = gedit_close_confirmation_dialog_new_single ( +						GTK_WINDOW (window), +						doc, +						TRUE); +	} +	else +	{ +		dlg = gedit_close_confirmation_dialog_new (GTK_WINDOW (window), +							   unsaved_docs, +							   TRUE); +	} + +	g_list_free (unsaved_docs); + +	g_signal_connect (dlg, +			  "response", +			  G_CALLBACK (close_confirmation_dialog_response_handler), +			  window); + +	gtk_widget_show (dlg); +} + +static void +ask_next_confirmation (void) +{ +	g_return_if_fail (window_dirty_list != NULL); + +	/* pop up the confirmation dialog for the first window +	 * in the dirty list. The next confirmation is asked once +	 * this one has been handled. +	 */ +	show_confirmation_dialog (GEDIT_WINDOW (window_dirty_list->data)); +} + +/* quit_requested handler for the master client */ +static void +client_quit_requested_cb (EggSMClient *client, gpointer data) +{ +	GeditApp *app; +	const GList *l; + +	gedit_debug (DEBUG_SESSION); + +	app = gedit_app_get_default (); + +	if (window_dirty_list != NULL) +	{ +		g_critical ("global variable window_dirty_list not NULL"); +		window_dirty_list = NULL; +	} + +	for (l = gedit_app_get_windows (app); l != NULL; l = l->next) +	{ +		if (gedit_window_get_unsaved_documents (GEDIT_WINDOW (l->data)) != NULL) +		{ +			window_dirty_list = g_slist_prepend (window_dirty_list, l->data); +		} +	} + +	/* no modified docs */ +	if (window_dirty_list == NULL) +	{ +		egg_sm_client_will_quit (client, TRUE); + +		return; +	} + +	ask_next_confirmation (); + +	gedit_debug_message (DEBUG_SESSION, "END"); +} + +/* quit handler for the master client */ +static void +client_quit_cb (EggSMClient *client, gpointer data) +{ +#if 0 +	gedit_debug (DEBUG_SESSION); + +	if (!client->save_yourself_emitted) +		gedit_file_close_all (); + +	gedit_debug_message (DEBUG_FILE, "All files closed."); +	 +	matecomponent_mdi_destroy (MATECOMPONENT_MDI (gedit_mdi)); +	 +	gedit_debug_message (DEBUG_FILE, "Unref gedit_mdi."); + +	g_object_unref (G_OBJECT (gedit_mdi)); + +	gedit_debug_message (DEBUG_FILE, "Unref gedit_mdi: DONE"); + +	gedit_debug_message (DEBUG_FILE, "Unref gedit_app_server."); + +	matecomponent_object_unref (gedit_app_server); + +	gedit_debug_message (DEBUG_FILE, "Unref gedit_app_server: DONE"); +#endif + +	gtk_main_quit (); +} + +/** + * gedit_session_init: + *  + * Initializes session management support.  This function should be called near + * the beginning of the program. + **/ +void +gedit_session_init (void) +{ +	gedit_debug (DEBUG_SESSION); +	 +	if (master_client) +	  return; + +	master_client = egg_sm_client_get (); +	g_signal_connect (master_client, +			  "save_state", +			  G_CALLBACK (client_save_state_cb), +			  NULL); +	g_signal_connect (master_client, +			  "quit_requested", +			  G_CALLBACK (client_quit_requested_cb), +			  NULL); +	g_signal_connect (master_client, +			  "quit", +			  G_CALLBACK (client_quit_cb), +			  NULL);		   +} + +/** + * gedit_session_is_restored: + *  + * Returns whether this gedit is running from a restarted session. + *  + * Return value: TRUE if the session manager restarted us, FALSE otherwise. + * This should be used to determine whether to pay attention to command line + * arguments in case the session was not restored. + **/ +gboolean +gedit_session_is_restored (void) +{ +	gboolean restored; + +	gedit_debug (DEBUG_SESSION); + +	if (!master_client) +		return FALSE; + +	restored = egg_sm_client_is_resumed (master_client); + +	gedit_debug_message (DEBUG_SESSION, restored ? "RESTORED" : "NOT RESTORED"); + +	return restored; +} + +static void +parse_window (GKeyFile *state_file, const char *group_name) +{ +	GeditWindow *window; +	gchar *role, *active_document, **documents; +	int width, height; +	gboolean visible; +	GeditPanel *panel; +	GError *error = NULL; +   +	role = g_key_file_get_string (state_file, group_name, "role", NULL); + +	gedit_debug_message (DEBUG_SESSION, "Window role: %s", role); + +	window = _gedit_app_restore_window (gedit_app_get_default (), (gchar *) role); +	g_free (role); + +	if (window == NULL) +	{ +		g_warning ("Couldn't restore window"); +		return; +	} + +	width = g_key_file_get_integer (state_file, group_name, +					"width", &error); +	if (error) +	{ +	        g_clear_error (&error); +		width = -1; +	} +	height = g_key_file_get_integer (state_file, group_name, +					 "height", &error); +	if (error) +	{ +	        g_clear_error (&error); +		height = -1; +	} +	gtk_window_set_default_size (GTK_WINDOW (window), width, height); +   +  +	visible = g_key_file_get_boolean (state_file, group_name, +					  "side-panel-visible", &error); +	if (error) +	{ +	        g_clear_error (&error); +		visible = FALSE; +	} +   +	panel = gedit_window_get_side_panel (window); +   +	if (visible) +	{ +	        gedit_debug_message (DEBUG_SESSION, "Side panel visible"); +		gtk_widget_show (GTK_WIDGET (panel)); +	} +	else +	{ +	      gedit_debug_message (DEBUG_SESSION, "Side panel _NOT_ visible"); +	      gtk_widget_hide (GTK_WIDGET (panel)); +	} +   +	visible = g_key_file_get_boolean (state_file, group_name, +					  "bottom-panel-visible", &error); +	if (error) +	{ +	        g_clear_error (&error); +		visible = FALSE; +	} +   +	panel = gedit_window_get_bottom_panel (window); +	if (visible) +	{ +	        gedit_debug_message (DEBUG_SESSION, "Bottom panel visible"); +		gtk_widget_show (GTK_WIDGET (panel)); +	} +	else +	{ +	        gedit_debug_message (DEBUG_SESSION, "Bottom panel _NOT_ visible"); +		gtk_widget_hide (GTK_WIDGET (panel)); +	} + +	active_document = g_key_file_get_string (state_file, group_name, +						 "active-document", NULL); +	documents = g_key_file_get_string_list (state_file, group_name, +						"documents", NULL, NULL); +	if (documents) +	{ +	        int i; +		gboolean jump_to = FALSE; +   +		for (i = 0; documents[i]; i++) +		{ +		        if (active_document != NULL) +			        jump_to = strcmp (active_document, +						  documents[i]) == 0; +   +			gedit_debug_message (DEBUG_SESSION, +					     "URI: %s (%s)", +					     documents[i], +					     jump_to ? "active" : "not active"); +			gedit_window_create_tab_from_uri (window, +							  documents[i], +							  NULL, +							  0, +							  FALSE, +							  jump_to); +		} +		g_strfreev (documents); +	} +  +	g_free (active_document); +	 +	gtk_widget_show (GTK_WIDGET (window)); +} + +/** + * gedit_session_load: + *  + * Loads the session by fetching the necessary information from the session + * manager and opening files. + *  + * Return value: TRUE if the session was loaded successfully, FALSE otherwise. + **/ +gboolean +gedit_session_load (void) +{ +	GKeyFile *state_file; +	gchar **groups; +	int i; + +	gedit_debug (DEBUG_SESSION); + +	state_file = egg_sm_client_get_state_file (master_client); +	if (state_file == NULL) +	       return FALSE; + +	groups = g_key_file_get_groups (state_file, NULL); + +	for (i = 0; groups[i] != NULL; i++) +	{ +		if (g_str_has_prefix (groups[i], "gedit window ")) +		        parse_window (state_file, groups[i]); +	} + +	g_strfreev (groups); +	g_key_file_free (state_file); + +	return TRUE; +} diff --git a/gedit/gedit-session.h b/gedit/gedit-session.h new file mode 100755 index 00000000..ba59c131 --- /dev/null +++ b/gedit/gedit-session.h @@ -0,0 +1,47 @@ +/* + * gedit-session.h - Basic session management for gedit + * This file is part of gedit + * + * Copyright (C) 2002 Ximian, Inc. + * Copyright (C) 2005 - Paolo Maggi  + * + * Author: Federico Mena-Quintero <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ + +/* + * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id  + */ + +#ifndef __GEDIT_SESSION_H__ +#define __GEDIT_SESSION_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +void		gedit_session_init 		(void); +gboolean	gedit_session_is_restored 	(void); +gboolean 	gedit_session_load 		(void); + +G_END_DECLS + +#endif /* __GEDIT_SESSION_H__ */ diff --git a/gedit/gedit-smart-charset-converter.c b/gedit/gedit-smart-charset-converter.c new file mode 100755 index 00000000..e32b0b17 --- /dev/null +++ b/gedit/gedit-smart-charset-converter.c @@ -0,0 +1,422 @@ +/* + * gedit-smart-charset-converter.c + * This file is part of gedit + * + * Copyright (C) 2009 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + +#include "gedit-smart-charset-converter.h" +#include "gedit-debug.h" +#include "gedit-document.h" + +#include <gio/gio.h> +#include <glib/gi18n.h> + +#define GEDIT_SMART_CHARSET_CONVERTER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_SMART_CHARSET_CONVERTER, GeditSmartCharsetConverterPrivate)) + +struct _GeditSmartCharsetConverterPrivate +{ +	GCharsetConverter *charset_conv; + +	GSList *encodings; +	GSList *current_encoding; + +	guint is_utf8 : 1; +	guint use_first : 1; +}; + +static void gedit_smart_charset_converter_iface_init    (GConverterIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GeditSmartCharsetConverter, gedit_smart_charset_converter, +			 G_TYPE_OBJECT, +			 G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, +						gedit_smart_charset_converter_iface_init)) + +static void +gedit_smart_charset_converter_finalize (GObject *object) +{ +	GeditSmartCharsetConverter *smart = GEDIT_SMART_CHARSET_CONVERTER (object); + +	g_slist_free (smart->priv->encodings); + +	gedit_debug_message (DEBUG_UTILS, "finalizing smart charset converter"); + +	G_OBJECT_CLASS (gedit_smart_charset_converter_parent_class)->finalize (object); +} + +static void +gedit_smart_charset_converter_dispose (GObject *object) +{ +	GeditSmartCharsetConverter *smart = GEDIT_SMART_CHARSET_CONVERTER (object); + +	if (smart->priv->charset_conv != NULL) +	{ +		g_object_unref (smart->priv->charset_conv); +		smart->priv->charset_conv = NULL; +	} + +	gedit_debug_message (DEBUG_UTILS, "disposing smart charset converter"); + +	G_OBJECT_CLASS (gedit_smart_charset_converter_parent_class)->dispose (object); +} + +static void +gedit_smart_charset_converter_class_init (GeditSmartCharsetConverterClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_smart_charset_converter_finalize; +	object_class->dispose = gedit_smart_charset_converter_dispose; + +	g_type_class_add_private (object_class, sizeof (GeditSmartCharsetConverterPrivate)); +} + +static void +gedit_smart_charset_converter_init (GeditSmartCharsetConverter *smart) +{ +	smart->priv = GEDIT_SMART_CHARSET_CONVERTER_GET_PRIVATE (smart); + +	smart->priv->charset_conv = NULL; +	smart->priv->encodings = NULL; +	smart->priv->current_encoding = NULL; +	smart->priv->is_utf8 = FALSE; +	smart->priv->use_first = FALSE; + +	gedit_debug_message (DEBUG_UTILS, "initializing smart charset converter"); +} + +static const GeditEncoding * +get_encoding (GeditSmartCharsetConverter *smart) +{ +	if (smart->priv->current_encoding == NULL) +	{ +		smart->priv->current_encoding = smart->priv->encodings; +	} +	else +	{ +		smart->priv->current_encoding = g_slist_next (smart->priv->current_encoding); +	} + +	if (smart->priv->current_encoding != NULL) +		return (const GeditEncoding *)smart->priv->current_encoding->data; + +#if 0 +	FIXME: uncomment this when using fallback +	/* If we tried all encodings, we return the first encoding */ +	smart->priv->use_first = TRUE; +	smart->priv->current_encoding = smart->priv->encodings; + +	return (const GeditEncoding *)smart->priv->current_encoding->data; +#endif +	return NULL; +} + +static gboolean +try_convert (GCharsetConverter *converter, +             const void        *inbuf, +             gsize              inbuf_size) +{ +	GError *err; +	gsize bytes_read, nread; +	gsize bytes_written, nwritten; +	GConverterResult res; +	gchar *out; +	gboolean ret; +	gsize out_size; + +	if (inbuf == NULL || inbuf_size == 0) +	{ +		return FALSE; +	} + +	err = NULL; +	nread = 0; +	nwritten = 0; +	out_size = inbuf_size * 4; +	out = g_malloc (out_size); + +	do +	{ +		res = g_converter_convert (G_CONVERTER (converter), +		                           inbuf + nread, +		                           inbuf_size - nread, +		                           out + nwritten, +		                           out_size - nwritten, +		                           G_CONVERTER_INPUT_AT_END, +		                           &bytes_read, +		                           &bytes_written, +		                           &err); + +		nread += bytes_read; +		nwritten += bytes_written; +	} while (res != G_CONVERTER_FINISHED && res != G_CONVERTER_ERROR && err == NULL); + +	if (err != NULL) +	{ +		if (err->code == G_CONVERT_ERROR_PARTIAL_INPUT) +		{ +			/* FIXME We can get partial input while guessing the +			   encoding because we just take some amount of text +			   to guess from. */ +			ret = TRUE; +		} +		else +		{ +			ret = FALSE; +		} + +		g_error_free (err); +	} +	else +	{ +		ret = TRUE; +	} + +	/* FIXME: Check the remainder? */ +	if (ret == TRUE && !g_utf8_validate (out, nwritten, NULL)) +	{ +		ret = FALSE; +	} + +	g_free (out); + +	return ret; +} + +static GCharsetConverter * +guess_encoding (GeditSmartCharsetConverter *smart, +		const void                 *inbuf, +		gsize                       inbuf_size) +{ +	GCharsetConverter *conv = NULL; + +	if (inbuf == NULL || inbuf_size == 0) +	{ +		smart->priv->is_utf8 = TRUE; +		return NULL; +	} + +	if (smart->priv->encodings != NULL && +	    smart->priv->encodings->next == NULL) +		smart->priv->use_first = TRUE; + +	/* We just check the first block */ +	while (TRUE) +	{ +		const GeditEncoding *enc; + +		if (conv != NULL) +		{ +			g_object_unref (conv); +			conv = NULL; +		} + +		/* We get an encoding from the list */ +		enc = get_encoding (smart); + +		/* if it is NULL we didn't guess anything */ +		if (enc == NULL) +		{ +			break; +		} + +		gedit_debug_message (DEBUG_UTILS, "trying charset: %s", +				     gedit_encoding_get_charset (smart->priv->current_encoding->data)); + +		if (enc == gedit_encoding_get_utf8 ()) +		{ +			gsize remainder; +			const gchar *end; +			 +			if (g_utf8_validate (inbuf, inbuf_size, &end) || +			    smart->priv->use_first) +			{ +				smart->priv->is_utf8 = TRUE; +				break; +			} + +			/* Check if the end is less than one char */ +			remainder = inbuf_size - (end - (gchar *)inbuf); +			if (remainder < 6) +			{ +				smart->priv->is_utf8 = TRUE; +				break; +			} + +			continue; +		} + +		conv = g_charset_converter_new ("UTF-8", +						gedit_encoding_get_charset (enc), +						NULL); + +		/* If we tried all encodings we use the first one */ +		if (smart->priv->use_first) +		{ +			break; +		} + +		/* Try to convert */ +		if (try_convert (conv, inbuf, inbuf_size)) +		{ +			break; +		} +	} + +	if (conv != NULL) +	{ +		g_converter_reset (G_CONVERTER (conv)); + +		/* FIXME: uncomment this when we want to use the fallback +		g_charset_converter_set_use_fallback (conv, TRUE);*/ +	} + +	return conv; +} + +static GConverterResult +gedit_smart_charset_converter_convert (GConverter *converter, +				       const void *inbuf, +				       gsize       inbuf_size, +				       void       *outbuf, +				       gsize       outbuf_size, +				       GConverterFlags flags, +				       gsize      *bytes_read, +				       gsize      *bytes_written, +				       GError    **error) +{ +	GeditSmartCharsetConverter *smart = GEDIT_SMART_CHARSET_CONVERTER (converter); + +	/* Guess the encoding if we didn't make it yet */ +	if (smart->priv->charset_conv == NULL && +	    !smart->priv->is_utf8) +	{ +		smart->priv->charset_conv = guess_encoding (smart, inbuf, inbuf_size); + +		/* If we still have the previous case is that we didn't guess +		   anything */ +		if (smart->priv->charset_conv == NULL && +		    !smart->priv->is_utf8) +		{ +			/* FIXME: Add a different domain when we kill gedit_convert */ +			g_set_error_literal (error, GEDIT_DOCUMENT_ERROR, +					     GEDIT_DOCUMENT_ERROR_ENCODING_AUTO_DETECTION_FAILED, +					     _("It is not possible to detect the encoding automatically")); +			return G_CONVERTER_ERROR; +		} +	} + +	/* Now if the encoding is utf8 just redirect the input to the output */ +	if (smart->priv->is_utf8) +	{ +		gsize size; +		GConverterResult ret; + +		size = MIN (inbuf_size, outbuf_size); + +		memcpy (outbuf, inbuf, size); +		*bytes_read = size; +		*bytes_written = size; + +		ret = G_CONVERTER_CONVERTED; + +		if (flags & G_CONVERTER_INPUT_AT_END) +			ret = G_CONVERTER_FINISHED; +		else if (flags & G_CONVERTER_FLUSH) +			ret = G_CONVERTER_FLUSHED; + +		return ret; +	} + +	/* If we reached here is because we need to convert the text so, we +	   convert it with the charset converter */ +	return g_converter_convert (G_CONVERTER (smart->priv->charset_conv), +				    inbuf, +				    inbuf_size, +				    outbuf, +				    outbuf_size, +				    flags, +				    bytes_read, +				    bytes_written, +				    error); +} + +static void +gedit_smart_charset_converter_reset (GConverter *converter) +{ +	GeditSmartCharsetConverter *smart = GEDIT_SMART_CHARSET_CONVERTER (converter); + +	smart->priv->current_encoding = NULL; +	smart->priv->is_utf8 = FALSE; + +	if (smart->priv->charset_conv != NULL) +	{ +		g_object_unref (smart->priv->charset_conv); +		smart->priv->charset_conv = NULL; +	} +} + +static void +gedit_smart_charset_converter_iface_init (GConverterIface *iface) +{ +	iface->convert = gedit_smart_charset_converter_convert; +	iface->reset = gedit_smart_charset_converter_reset; +} + +GeditSmartCharsetConverter * +gedit_smart_charset_converter_new (GSList *candidate_encodings) +{ +	GeditSmartCharsetConverter *smart; + +	g_return_val_if_fail (candidate_encodings != NULL, NULL); + +	smart = g_object_new (GEDIT_TYPE_SMART_CHARSET_CONVERTER, NULL); + +	smart->priv->encodings = g_slist_copy (candidate_encodings); + +	return smart; +} + +const GeditEncoding * +gedit_smart_charset_converter_get_guessed (GeditSmartCharsetConverter *smart) +{ +	g_return_val_if_fail (GEDIT_IS_SMART_CHARSET_CONVERTER (smart), NULL); + +	if (smart->priv->current_encoding != NULL) +	{ +		return (const GeditEncoding *)smart->priv->current_encoding->data; +	} +	else if (smart->priv->is_utf8) +	{ +		return gedit_encoding_get_utf8 (); +	} + +	return NULL; +} + +guint +gedit_smart_charset_converter_get_num_fallbacks (GeditSmartCharsetConverter *smart) +{ +	g_return_val_if_fail (GEDIT_IS_SMART_CHARSET_CONVERTER (smart), FALSE); + +	if (smart->priv->charset_conv == NULL) +		return FALSE; + +	return g_charset_converter_get_num_fallbacks (smart->priv->charset_conv) != 0; +} + diff --git a/gedit/gedit-smart-charset-converter.h b/gedit/gedit-smart-charset-converter.h new file mode 100755 index 00000000..803e07a5 --- /dev/null +++ b/gedit/gedit-smart-charset-converter.h @@ -0,0 +1,66 @@ +/* + * gedit-smart-charset-converter.h + * This file is part of gedit + * + * Copyright (C) 2009 - Ignacio Casal Quinteiro + * + * gedit 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. + * + * gedit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gedit; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor,  + * Boston, MA  02110-1301  USA + */ + +#ifndef __GEDIT_SMART_CHARSET_CONVERTER_H__ +#define __GEDIT_SMART_CHARSET_CONVERTER_H__ + +#include <glib-object.h> + +#include "gedit-encodings.h" + +G_BEGIN_DECLS + +#define GEDIT_TYPE_SMART_CHARSET_CONVERTER		(gedit_smart_charset_converter_get_type ()) +#define GEDIT_SMART_CHARSET_CONVERTER(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_SMART_CHARSET_CONVERTER, GeditSmartCharsetConverter)) +#define GEDIT_SMART_CHARSET_CONVERTER_CONST(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_SMART_CHARSET_CONVERTER, GeditSmartCharsetConverter const)) +#define GEDIT_SMART_CHARSET_CONVERTER_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_SMART_CHARSET_CONVERTER, GeditSmartCharsetConverterClass)) +#define GEDIT_IS_SMART_CHARSET_CONVERTER(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_SMART_CHARSET_CONVERTER)) +#define GEDIT_IS_SMART_CHARSET_CONVERTER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_SMART_CHARSET_CONVERTER)) +#define GEDIT_SMART_CHARSET_CONVERTER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_SMART_CHARSET_CONVERTER, GeditSmartCharsetConverterClass)) + +typedef struct _GeditSmartCharsetConverter		GeditSmartCharsetConverter; +typedef struct _GeditSmartCharsetConverterClass		GeditSmartCharsetConverterClass; +typedef struct _GeditSmartCharsetConverterPrivate	GeditSmartCharsetConverterPrivate; + +struct _GeditSmartCharsetConverter +{ +	GObject parent; +	 +	GeditSmartCharsetConverterPrivate *priv; +}; + +struct _GeditSmartCharsetConverterClass +{ +	GObjectClass parent_class; +}; + +GType gedit_smart_charset_converter_get_type (void) G_GNUC_CONST; + +GeditSmartCharsetConverter	*gedit_smart_charset_converter_new		(GSList *candidate_encodings); + +const GeditEncoding		*gedit_smart_charset_converter_get_guessed	(GeditSmartCharsetConverter *smart); + +guint				 gedit_smart_charset_converter_get_num_fallbacks(GeditSmartCharsetConverter *smart); + +G_END_DECLS + +#endif /* __GEDIT_SMART_CHARSET_CONVERTER_H__ */ diff --git a/gedit/gedit-spinner.c b/gedit/gedit-spinner.c new file mode 100755 index 00000000..5cb74587 --- /dev/null +++ b/gedit/gedit-spinner.c @@ -0,0 +1,989 @@ +/* + * gedit-spinner.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * Copyright (C) 2002-2004 Marco Pesenti Gritti + * Copyright (C) 2004 Christian Persch + * Copyright (C) 2000 - Eazel, Inc.  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * This widget was originally written by Andy Hertzfeld <[email protected]> for + * Caja. It was then modified by Marco Pesenti Gritti and Christian Persch + * for Epiphany. + * + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gedit-spinner.h" + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtk.h> + +/* Spinner cache implementation */ + +#define GEDIT_TYPE_SPINNER_CACHE		(gedit_spinner_cache_get_type()) +#define GEDIT_SPINNER_CACHE(object)		(G_TYPE_CHECK_INSTANCE_CAST((object), GEDIT_TYPE_SPINNER_CACHE, GeditSpinnerCache)) +#define GEDIT_SPINNER_CACHE_CLASS(klass) 	(G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_SPINNER_CACHE, GeditSpinnerCacheClass)) +#define GEDIT_IS_SPINNER_CACHE(object)		(G_TYPE_CHECK_INSTANCE_TYPE((object), GEDIT_TYPE_SPINNER_CACHE)) +#define GEDIT_IS_SPINNER_CACHE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GEDIT_TYPE_SPINNER_CACHE)) +#define GEDIT_SPINNER_CACHE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_SPINNER_CACHE, GeditSpinnerCacheClass)) + +typedef struct _GeditSpinnerCache		GeditSpinnerCache; +typedef struct _GeditSpinnerCacheClass		GeditSpinnerCacheClass; +typedef struct _GeditSpinnerCachePrivate	GeditSpinnerCachePrivate; + +struct _GeditSpinnerCacheClass +{ +	GObjectClass parent_class; +}; + +struct _GeditSpinnerCache +{ +	GObject parent_object; + +	/*< private >*/ +	GeditSpinnerCachePrivate *priv; +}; + +#define GEDIT_SPINNER_CACHE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_SPINNER_CACHE, GeditSpinnerCachePrivate)) + +struct _GeditSpinnerCachePrivate +{ +	/* Hash table of GdkScreen -> GeditSpinnerCacheData */ +	GHashTable *hash; +}; + +typedef struct +{ +	guint        ref_count; +	GtkIconSize  size; +	gint         width; +	gint         height; +	GdkPixbuf  **animation_pixbufs; +	guint        n_animation_pixbufs; +} GeditSpinnerImages; + +#define LAST_ICON_SIZE			GTK_ICON_SIZE_DIALOG + 1 +#define SPINNER_ICON_NAME		"process-working" +#define SPINNER_FALLBACK_ICON_NAME	"mate-spinner" +#define GEDIT_SPINNER_IMAGES_INVALID	((GeditSpinnerImages *) 0x1) + +typedef struct +{ +	GdkScreen          *screen; +	GtkIconTheme       *icon_theme; +	GeditSpinnerImages *images[LAST_ICON_SIZE]; +} GeditSpinnerCacheData; + +static void gedit_spinner_cache_class_init	(GeditSpinnerCacheClass *klass); +static void gedit_spinner_cache_init		(GeditSpinnerCache      *cache); + +static GObjectClass *gedit_spinner_cache_parent_class; + +static GType +gedit_spinner_cache_get_type (void) +{ +	static GType type = 0; + +	if (G_UNLIKELY (type == 0)) +	{ +		const GTypeInfo our_info = +		{ +			sizeof (GeditSpinnerCacheClass), +			NULL, +			NULL, +			(GClassInitFunc) gedit_spinner_cache_class_init, +			NULL, +			NULL, +			sizeof (GeditSpinnerCache), +			0, +			(GInstanceInitFunc) gedit_spinner_cache_init +		}; + +		type = g_type_register_static (G_TYPE_OBJECT, +					       "GeditSpinnerCache", +					       &our_info, 0); +	} + +	return type; +} + +static GeditSpinnerImages * +gedit_spinner_images_ref (GeditSpinnerImages *images) +{ +	g_return_val_if_fail (images != NULL, NULL); + +	images->ref_count++; + +	return images; +} + +static void +gedit_spinner_images_unref (GeditSpinnerImages *images) +{ +	g_return_if_fail (images != NULL); + +	images->ref_count--; +	if (images->ref_count == 0) +	{ +		guint i; + +		/* LOG ("Freeing spinner images %p for size %d", images, images->size); */ + +		for (i = 0; i < images->n_animation_pixbufs; ++i) +		{ +			g_object_unref (images->animation_pixbufs[i]); +		} +		g_free (images->animation_pixbufs); + +		g_free (images); +	} +} + +static void +gedit_spinner_cache_data_unload (GeditSpinnerCacheData *data) +{ +	GtkIconSize size; +	GeditSpinnerImages *images; + +	g_return_if_fail (data != NULL); + +	/* LOG ("GeditSpinnerDataCache unload for screen %p", data->screen); */ + +	for (size = GTK_ICON_SIZE_INVALID; size < LAST_ICON_SIZE; ++size) +	{ +		images = data->images[size]; +		data->images[size] = NULL; + +		if (images != NULL && images != GEDIT_SPINNER_IMAGES_INVALID) +		{ +			gedit_spinner_images_unref (images); +		} +	} +} + +static GdkPixbuf * +extract_frame (GdkPixbuf *grid_pixbuf, +	       int x, +	       int y, +	       int size) +{ +	GdkPixbuf *pixbuf; + +	if (x + size > gdk_pixbuf_get_width (grid_pixbuf) || +	    y + size > gdk_pixbuf_get_height (grid_pixbuf)) +	{ +		return NULL; +	} + +	pixbuf = gdk_pixbuf_new_subpixbuf (grid_pixbuf, +					   x, y, +					   size, size); +	g_return_val_if_fail (pixbuf != NULL, NULL); + +	return pixbuf; +} + +static GdkPixbuf * +scale_to_size (GdkPixbuf *pixbuf, +	       int dw, +	       int dh) +{ +	GdkPixbuf *result; +	int pw, ph; + +	g_return_val_if_fail (pixbuf != NULL, NULL); + +	pw = gdk_pixbuf_get_width (pixbuf); +	ph = gdk_pixbuf_get_height (pixbuf); + +	if (pw != dw || ph != dh) +	{ +		result = gdk_pixbuf_scale_simple (pixbuf, dw, dh, +						  GDK_INTERP_BILINEAR); +		g_object_unref (pixbuf); +		return result; +	} + +	return pixbuf; +} + +static GeditSpinnerImages * +gedit_spinner_images_load (GdkScreen *screen, +			   GtkIconTheme *icon_theme, +			   GtkIconSize icon_size) +{ +	GeditSpinnerImages *images; +	GdkPixbuf *icon_pixbuf, *pixbuf; +	GtkIconInfo *icon_info = NULL; +	int grid_width, grid_height, x, y, requested_size, size, isw, ish, n; +	const char *icon; +	GSList *list = NULL, *l; + +	/* LOG ("GeditSpinnerCacheData loading for screen %p at size %d", screen, icon_size); */ + +	/* START_PROFILER ("loading spinner animation") */ + +	if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_for_screen (screen), +						icon_size, &isw, &ish)) +		goto loser; +  +	requested_size = MAX (ish, isw); + +	/* Load the animation. The 'rest icon' is the 0th frame */ +	icon_info = gtk_icon_theme_lookup_icon (icon_theme, +						SPINNER_ICON_NAME, +						requested_size, 0); +	if (icon_info == NULL) +	{ +		g_warning ("Throbber animation not found"); + +		/* If the icon naming spec compliant name wasn't found, try the old name */ +		icon_info = gtk_icon_theme_lookup_icon (icon_theme, +							SPINNER_FALLBACK_ICON_NAME, +						        requested_size, 0); +		if (icon_info == NULL) +		{ +			g_warning ("Throbber fallback animation not found either"); +			goto loser; +	 	} +	} + +	g_assert (icon_info != NULL); + +	size = gtk_icon_info_get_base_size (icon_info); +	icon = gtk_icon_info_get_filename (icon_info); + +	if (icon == NULL) +		goto loser; + +	icon_pixbuf = gdk_pixbuf_new_from_file (icon, NULL); +	gtk_icon_info_free (icon_info); +	icon_info = NULL; + +	if (icon_pixbuf == NULL) +	{ +		g_warning ("Could not load the spinner file"); +		goto loser; +	} + +	grid_width = gdk_pixbuf_get_width (icon_pixbuf); +	grid_height = gdk_pixbuf_get_height (icon_pixbuf); + +	n = 0; +	for (y = 0; y < grid_height; y += size) +	{ +		for (x = 0; x < grid_width ; x += size) +		{ +			pixbuf = extract_frame (icon_pixbuf, x, y, size); + +			if (pixbuf) +			{ +				list = g_slist_prepend (list, pixbuf); +				++n; +			} +			else +			{ +				g_warning ("Cannot extract frame (%d, %d) from the grid\n", x, y); +			} +		} +	} + +	g_object_unref (icon_pixbuf); + +	if (list == NULL) +		goto loser; + +	/* g_assert (n > 0); */ + +	if (size > requested_size) +	{ +		for (l = list; l != NULL; l = l->next) +		{ +			l->data = scale_to_size (l->data, isw, ish); +		} + 	} + +	/* Now we've successfully got all the data */ +	images = g_new (GeditSpinnerImages, 1); +	images->ref_count = 1; +  +	images->size = icon_size; +	images->width = images->height = requested_size; + +	images->n_animation_pixbufs = n; +	images->animation_pixbufs = g_new (GdkPixbuf *, n); + +	for (l = list; l != NULL; l = l->next) +	{ +		g_assert (l->data != NULL); +		images->animation_pixbufs[--n] = l->data; +	} +	g_assert (n == 0); + +	g_slist_free (list); + +	/* STOP_PROFILER ("loading spinner animation") */ +	return images; +  +loser: +	if (icon_info) +	{ +		gtk_icon_info_free (icon_info); + 	} + +	g_slist_foreach (list, (GFunc) g_object_unref, NULL); + +	/* STOP_PROFILER ("loading spinner animation") */ + +	return NULL; +} + +static GeditSpinnerCacheData * +gedit_spinner_cache_data_new (GdkScreen *screen) +{ +	GeditSpinnerCacheData *data; + +	data = g_new0 (GeditSpinnerCacheData, 1); + +	data->screen = screen; +	data->icon_theme = gtk_icon_theme_get_for_screen (screen); +	g_signal_connect_swapped (data->icon_theme, +				  "changed", +				  G_CALLBACK (gedit_spinner_cache_data_unload), +				  data); + +	return data; +} + +static void +gedit_spinner_cache_data_free (GeditSpinnerCacheData *data) +{ +	g_return_if_fail (data != NULL); +	g_return_if_fail (data->icon_theme != NULL); + +	g_signal_handlers_disconnect_by_func (data->icon_theme, +					      G_CALLBACK (gedit_spinner_cache_data_unload), +					      data); + +	gedit_spinner_cache_data_unload (data); + +	g_free (data); +} + +static GeditSpinnerImages * +gedit_spinner_cache_get_images (GeditSpinnerCache *cache, +				GdkScreen         *screen, +				GtkIconSize        icon_size) +{ +	GeditSpinnerCachePrivate *priv = cache->priv; +	GeditSpinnerCacheData *data; +	GeditSpinnerImages *images; + +	/* LOG ("Getting animation images for screen %p at size %d", screen, icon_size); */ + +	g_return_val_if_fail (icon_size >= 0 && icon_size < LAST_ICON_SIZE, NULL); + +	/* Backward compat: "invalid" meant "native" size which doesn't exist anymore */ +	if (icon_size == GTK_ICON_SIZE_INVALID) +	{ +		icon_size = GTK_ICON_SIZE_DIALOG; +	} + +	data = g_hash_table_lookup (priv->hash, screen); +	if (data == NULL) +	{ +		data = gedit_spinner_cache_data_new (screen); +		/* FIXME: think about what happens when the screen's display is closed later on */ +		g_hash_table_insert (priv->hash, screen, data); +	} + +	images = data->images[icon_size]; +	if (images == GEDIT_SPINNER_IMAGES_INVALID) +	{ +		/* Load failed, but don't try endlessly again! */ +		return NULL; +	} + +	if (images != NULL) +	{ +		/* Return cached data */ +		return gedit_spinner_images_ref (images); +	} + +	images = gedit_spinner_images_load (screen, data->icon_theme, icon_size); + +	if (images == NULL) + 	{ +		/* Mark as failed-to-load */ +		data->images[icon_size] = GEDIT_SPINNER_IMAGES_INVALID; +  +		return NULL; + 	} + +	data->images[icon_size] = images; + +	return gedit_spinner_images_ref (images); +} + +static void +gedit_spinner_cache_init (GeditSpinnerCache *cache) +{ +	GeditSpinnerCachePrivate *priv; + +	priv = cache->priv = GEDIT_SPINNER_CACHE_GET_PRIVATE (cache); + +	/* LOG ("GeditSpinnerCache initialising"); */ + +	priv->hash = g_hash_table_new_full (g_direct_hash, +					    g_direct_equal, +					    NULL, +					    (GDestroyNotify) gedit_spinner_cache_data_free); +} + +static void +gedit_spinner_cache_finalize (GObject *object) +{ +	GeditSpinnerCache *cache = GEDIT_SPINNER_CACHE (object);  +	GeditSpinnerCachePrivate *priv = cache->priv; + +	g_hash_table_destroy (priv->hash); + +	/* LOG ("GeditSpinnerCache finalised"); */ + +	G_OBJECT_CLASS (gedit_spinner_cache_parent_class)->finalize (object); +} + +static void +gedit_spinner_cache_class_init (GeditSpinnerCacheClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	gedit_spinner_cache_parent_class = g_type_class_peek_parent (klass); + +	object_class->finalize = gedit_spinner_cache_finalize; + +	g_type_class_add_private (object_class, sizeof (GeditSpinnerCachePrivate)); +} + +static GeditSpinnerCache *spinner_cache = NULL; + +static GeditSpinnerCache * +gedit_spinner_cache_ref (void) +{ +	if (spinner_cache == NULL) +	{ +		GeditSpinnerCache **cache_ptr; + +		spinner_cache = g_object_new (GEDIT_TYPE_SPINNER_CACHE, NULL); +		cache_ptr = &spinner_cache; +		g_object_add_weak_pointer (G_OBJECT (spinner_cache), +					   (gpointer *) cache_ptr); + +		return spinner_cache; +	} + +	return g_object_ref (spinner_cache); +} + +/* Spinner implementation */ + +#define SPINNER_TIMEOUT 125 /* ms */ + +#define GEDIT_SPINNER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_SPINNER, GeditSpinnerPrivate)) + +struct _GeditSpinnerPrivate +{ +	GtkIconTheme       *icon_theme; +	GeditSpinnerCache  *cache; +	GtkIconSize         size; +	GeditSpinnerImages *images; +	guint               current_image; +	guint               timeout; +	guint               timer_task; +	guint               spinning : 1; +	guint               need_load : 1; +}; + +static void gedit_spinner_class_init	(GeditSpinnerClass *class); +static void gedit_spinner_init		(GeditSpinner      *spinner); + +static GObjectClass *parent_class; + +GType +gedit_spinner_get_type (void) +{ +	static GType type = 0; + +	if (G_UNLIKELY (type == 0)) +	{ +		const GTypeInfo our_info = +		{ +			sizeof (GeditSpinnerClass), +			NULL, /* base_init */ +			NULL, /* base_finalize */ +			(GClassInitFunc) gedit_spinner_class_init, +			NULL, +			NULL, /* class_data */ +			sizeof (GeditSpinner), +			0, /* n_preallocs */ +			(GInstanceInitFunc) gedit_spinner_init +		}; + +		type = g_type_register_static (GTK_TYPE_WIDGET, +					       "GeditSpinner", +					       &our_info, 0); +	} + +	return type; +} + +static gboolean +gedit_spinner_load_images (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	if (priv->need_load) +	{ +		/* START_PROFILER ("gedit_spinner_load_images") */ + +		priv->images = +			gedit_spinner_cache_get_images (priv->cache, +							gtk_widget_get_screen (GTK_WIDGET (spinner)), +							priv->size); + +		/* STOP_PROFILER ("gedit_spinner_load_images") */ + +		priv->current_image = 0; /* 'rest' icon */ +		priv->need_load = FALSE; +	} + +	return priv->images != NULL; +} + +static void +gedit_spinner_unload_images (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	if (priv->images != NULL) +	{ +		gedit_spinner_images_unref (priv->images); +		priv->images = NULL; +	} + +	priv->current_image = 0; +	priv->need_load = TRUE; +} + +static void +icon_theme_changed_cb (GtkIconTheme *icon_theme, +		       GeditSpinner *spinner) +{ +	gedit_spinner_unload_images (spinner); +	gtk_widget_queue_resize (GTK_WIDGET (spinner)); +} + +static void +gedit_spinner_init (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv; + +	priv = spinner->priv = GEDIT_SPINNER_GET_PRIVATE (spinner); + +	GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW); + +	priv->cache = gedit_spinner_cache_ref (); +	priv->size = GTK_ICON_SIZE_DIALOG; +	priv->spinning = FALSE; +	priv->timeout = SPINNER_TIMEOUT; +	priv->need_load = TRUE; +} + +static int +gedit_spinner_expose (GtkWidget      *widget, +		      GdkEventExpose *event) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (widget); +	GeditSpinnerPrivate *priv = spinner->priv; +	GeditSpinnerImages *images; +	GdkPixbuf *pixbuf; +	GdkGC *gc; +	int x_offset, y_offset, width, height; +	GdkRectangle pix_area, dest; + +	if (!GTK_WIDGET_DRAWABLE (spinner)) +	{ +		return FALSE; +	} + +	if (priv->need_load && +	    !gedit_spinner_load_images (spinner)) +	{ +		return FALSE; +	} + +	images = priv->images; +	if (images == NULL) +	{ +		return FALSE; +	} + +	/* Otherwise |images| will be NULL anyway */ +	g_assert (images->n_animation_pixbufs > 0); + +	g_assert (priv->current_image >= 0 && +		  priv->current_image < images->n_animation_pixbufs); + +	pixbuf = images->animation_pixbufs[priv->current_image]; + +	g_assert (pixbuf != NULL); + +	width = gdk_pixbuf_get_width (pixbuf); +	height = gdk_pixbuf_get_height (pixbuf); + +	/* Compute the offsets for the image centered on our allocation */ +	x_offset = (widget->allocation.width - width) / 2; +	y_offset = (widget->allocation.height - height) / 2; + +	pix_area.x = x_offset + widget->allocation.x; +	pix_area.y = y_offset + widget->allocation.y; +	pix_area.width = width; +	pix_area.height = height; + +	if (!gdk_rectangle_intersect (&event->area, &pix_area, &dest)) +	{ +		return FALSE; +	} + +	gc = gdk_gc_new (widget->window); +	gdk_draw_pixbuf (widget->window, gc, pixbuf, +			 dest.x - x_offset - widget->allocation.x, +			 dest.y - y_offset - widget->allocation.y, +			 dest.x, dest.y, +			 dest.width, dest.height, +			 GDK_RGB_DITHER_MAX, 0, 0); +	g_object_unref (gc); + +	return FALSE; +} + +static gboolean +bump_spinner_frame_cb (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	/* This can happen when we've unloaded the images on a theme +	 * change, but haven't been in the queued size request yet. +	 * Just skip this update. +	 */ +	if (priv->images == NULL) +		return TRUE; + +	priv->current_image++; +	if (priv->current_image >= priv->images->n_animation_pixbufs) +	{ +		/* the 0th frame is the 'rest' icon */ +		priv->current_image = MIN (1, priv->images->n_animation_pixbufs); +	} + +	gtk_widget_queue_draw (GTK_WIDGET (spinner)); + +	/* run again */ +	return TRUE; +} + +/** + * gedit_spinner_start: + * @spinner: a #GeditSpinner + * + * Start the spinner animation. + **/ +void +gedit_spinner_start (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	priv->spinning = TRUE; + +	if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)) && +			       priv->timer_task == 0 && +			       gedit_spinner_load_images (spinner)) +	{ +		/* the 0th frame is the 'rest' icon */ +		priv->current_image = MIN (1, priv->images->n_animation_pixbufs); + +		priv->timer_task = g_timeout_add_full (G_PRIORITY_LOW, +						       priv->timeout, +						       (GSourceFunc) bump_spinner_frame_cb, +						       spinner, +						       NULL); +	} +} + +static void +gedit_spinner_remove_update_callback (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	if (priv->timer_task != 0) +	{ +		g_source_remove (priv->timer_task); +		priv->timer_task = 0; +	} +} + +/** + * gedit_spinner_stop: + * @spinner: a #GeditSpinner + * + * Stop the spinner animation. + **/ +void +gedit_spinner_stop (GeditSpinner *spinner) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	priv->spinning = FALSE; +	priv->current_image = 0; + +	if (priv->timer_task != 0) +	{ +		gedit_spinner_remove_update_callback (spinner); + +		if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner))) +			gtk_widget_queue_draw (GTK_WIDGET (spinner)); +	} +} + +/* + * gedit_spinner_set_size: + * @spinner: a #GeditSpinner + * @size: the size of type %GtkIconSize + * + * Set the size of the spinner. + **/ +void +gedit_spinner_set_size (GeditSpinner *spinner, +			GtkIconSize size) +{ +	if (size == GTK_ICON_SIZE_INVALID) +	{ +		size = GTK_ICON_SIZE_DIALOG; +	} + +	if (size != spinner->priv->size) +	{ +		gedit_spinner_unload_images (spinner); + +		spinner->priv->size = size; + +		gtk_widget_queue_resize (GTK_WIDGET (spinner)); +	} +} + +#if 0 +/* +* gedit_spinner_set_timeout: +* @spinner: a #GeditSpinner +* @timeout: time delay between updates to the spinner. +* +* Sets the timeout delay for spinner updates. +**/ +void +gedit_spinner_set_timeout (GeditSpinner *spinner, +			   guint         timeout) +{ +	GeditSpinnerPrivate *priv = spinner->priv; + +	if (timeout != priv->timeout) +	{ +		gedit_spinner_stop (spinner); + +		priv->timeout = timeout; + +		if (priv->spinning) +		{ +			gedit_spinner_start (spinner); +		} +	} +} +#endif + +static void +gedit_spinner_size_request (GtkWidget *widget, +			   GtkRequisition *requisition) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (widget); +	GeditSpinnerPrivate *priv = spinner->priv; + +	if ((priv->need_load && +	     !gedit_spinner_load_images (spinner)) || +            priv->images == NULL) +	{ +		requisition->width = requisition->height = 0; +		gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget), +						   priv->size, +						   &requisition->width, +					           &requisition->height); +		return; +	} + +	requisition->width = priv->images->width; +	requisition->height = priv->images->height; + +	/* FIXME fix this hack */ +	/* allocate some extra margin so we don't butt up against toolbar edges */ +	if (priv->size != GTK_ICON_SIZE_MENU) +	{ +		requisition->width += 2; +		requisition->height += 2; +	} +} + +static void +gedit_spinner_map (GtkWidget *widget) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (widget); +	GeditSpinnerPrivate *priv = spinner->priv; + +	GTK_WIDGET_CLASS (parent_class)->map (widget); + +	if (priv->spinning) +	{ +		gedit_spinner_start (spinner); +	} +} + +static void +gedit_spinner_unmap (GtkWidget *widget) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (widget); + +	gedit_spinner_remove_update_callback (spinner); + +	GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static void +gedit_spinner_dispose (GObject *object) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (object); + +	g_signal_handlers_disconnect_by_func +			(spinner->priv->icon_theme, +			 G_CALLBACK (icon_theme_changed_cb), spinner); + +	G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gedit_spinner_finalize (GObject *object) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (object); + +	gedit_spinner_remove_update_callback (spinner); +	gedit_spinner_unload_images (spinner); + +	g_object_unref (spinner->priv->cache); + +	G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gedit_spinner_screen_changed (GtkWidget *widget, +			      GdkScreen *old_screen) +{ +	GeditSpinner *spinner = GEDIT_SPINNER (widget); +	GeditSpinnerPrivate *priv = spinner->priv; +	GdkScreen *screen; + +	if (GTK_WIDGET_CLASS (parent_class)->screen_changed) +	{ +		GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, old_screen); +	} + +	screen = gtk_widget_get_screen (widget); + +	/* FIXME: this seems to be happening when then spinner is destroyed!? */ +	if (old_screen == screen) +		return; + +	/* We'll get mapped again on the new screen, but not unmapped from +	 * the old screen, so remove timeout here. +	 */ +	gedit_spinner_remove_update_callback (spinner); + +	gedit_spinner_unload_images (spinner); + +	if (old_screen != NULL) +	{ +		g_signal_handlers_disconnect_by_func +			(gtk_icon_theme_get_for_screen (old_screen), +			 G_CALLBACK (icon_theme_changed_cb), spinner); +	} + +	priv->icon_theme = gtk_icon_theme_get_for_screen (screen); +	g_signal_connect (priv->icon_theme, "changed", +			  G_CALLBACK (icon_theme_changed_cb), spinner); +} + +static void +gedit_spinner_class_init (GeditSpinnerClass *class) +{ +	GObjectClass *object_class =  G_OBJECT_CLASS (class); +	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + +	parent_class = g_type_class_peek_parent (class); + +	object_class->dispose = gedit_spinner_dispose; +	object_class->finalize = gedit_spinner_finalize; + +	widget_class->expose_event = gedit_spinner_expose; +	widget_class->size_request = gedit_spinner_size_request; +	widget_class->map = gedit_spinner_map; +	widget_class->unmap = gedit_spinner_unmap; +	widget_class->screen_changed = gedit_spinner_screen_changed; + +	g_type_class_add_private (object_class, sizeof (GeditSpinnerPrivate)); +} + +/* + * gedit_spinner_new: + * + * Create a new #GeditSpinner. The spinner is a widget + * that gives the user feedback about network status with + * an animated image. + * + * Return Value: the spinner #GtkWidget + **/ +GtkWidget * +gedit_spinner_new (void) +{ +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_SPINNER, NULL)); +} diff --git a/gedit/gedit-spinner.h b/gedit/gedit-spinner.h new file mode 100755 index 00000000..807ba7c3 --- /dev/null +++ b/gedit/gedit-spinner.h @@ -0,0 +1,95 @@ +/* + * gedit-spinner.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * Copyright (C) 2000 - Eazel, Inc.  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * This widget was originally written by Andy Hertzfeld <[email protected]> for + * Caja. + * + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_SPINNER_H__ +#define __GEDIT_SPINNER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_SPINNER		(gedit_spinner_get_type ()) +#define GEDIT_SPINNER(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_SPINNER, GeditSpinner)) +#define GEDIT_SPINNER_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_SPINNER, GeditSpinnerClass)) +#define GEDIT_IS_SPINNER(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_SPINNER)) +#define GEDIT_IS_SPINNER_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_SPINNER)) +#define GEDIT_SPINNER_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_SPINNER, GeditSpinnerClass)) + + +/* Private structure type */ +typedef struct _GeditSpinnerPrivate	GeditSpinnerPrivate; + +/* + * Main object structure + */ +typedef struct _GeditSpinner		GeditSpinner; + +struct _GeditSpinner +{ +	GtkWidget parent; + +	/*< private >*/ +	GeditSpinnerPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditSpinnerClass	GeditSpinnerClass; + +struct _GeditSpinnerClass +{ +	GtkWidgetClass parent_class; +}; + +/* + * Public methods + */ +GType		gedit_spinner_get_type	(void) G_GNUC_CONST; + +GtkWidget      *gedit_spinner_new	(void); + +void		gedit_spinner_start	(GeditSpinner *throbber); + +void		gedit_spinner_stop	(GeditSpinner *throbber); + +void		gedit_spinner_set_size	(GeditSpinner *spinner, +					 GtkIconSize   size); + +G_END_DECLS + +#endif /* __GEDIT_SPINNER_H__ */ diff --git a/gedit/gedit-status-combo-box.c b/gedit/gedit-status-combo-box.c new file mode 100755 index 00000000..71ea8c9f --- /dev/null +++ b/gedit/gedit-status-combo-box.c @@ -0,0 +1,418 @@ +/* + * gedit-status-combo-box.c + * This file is part of gedit + * + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gedit-status-combo-box.h" + +#define COMBO_BOX_TEXT_DATA "GeditStatusComboBoxTextData" + +#define GEDIT_STATUS_COMBO_BOX_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_STATUS_COMBO_BOX, GeditStatusComboBoxPrivate)) + +struct _GeditStatusComboBoxPrivate +{ +	GtkWidget *frame; +	GtkWidget *button; +	GtkWidget *hbox; +	GtkWidget *label; +	GtkWidget *item; +	GtkWidget *arrow; +	 +	GtkWidget *menu; +	GtkWidget *current_item; +}; + +/* Signals */ +enum +{ +	CHANGED, +	NUM_SIGNALS +}; + +/* Properties */ +enum  +{ +	PROP_0, +	 +	PROP_LABEL +}; + +static guint signals[NUM_SIGNALS] = { 0 }; + +G_DEFINE_TYPE(GeditStatusComboBox, gedit_status_combo_box, GTK_TYPE_EVENT_BOX) + +static void +gedit_status_combo_box_finalize (GObject *object) +{ +	G_OBJECT_CLASS (gedit_status_combo_box_parent_class)->finalize (object); +} + +static void +gedit_status_combo_box_get_property (GObject    *object, +			             guint       prop_id, +			             GValue     *value, +			             GParamSpec *pspec) +{ +	GeditStatusComboBox *obj = GEDIT_STATUS_COMBO_BOX (object); + +	switch (prop_id) +	{ +		case PROP_LABEL: +			g_value_set_string (value, gedit_status_combo_box_get_label (obj)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_status_combo_box_set_property (GObject      *object, +			             guint         prop_id, +			             const GValue *value, +			             GParamSpec   *pspec) +{ +	GeditStatusComboBox *obj = GEDIT_STATUS_COMBO_BOX (object); + +	switch (prop_id) +	{ +		case PROP_LABEL: +			gedit_status_combo_box_set_label (obj, g_value_get_string (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_status_combo_box_changed (GeditStatusComboBox *combo, +				GtkMenuItem         *item) +{ +	const gchar *text; +	 +	text = g_object_get_data (G_OBJECT (item), COMBO_BOX_TEXT_DATA); + +	if (text != NULL) +	{ +		gtk_label_set_markup (GTK_LABEL (combo->priv->item), text); +		combo->priv->current_item = GTK_WIDGET (item); +	} +} + +static void +gedit_status_combo_box_class_init (GeditStatusComboBoxClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	 +	object_class->finalize = gedit_status_combo_box_finalize; +	object_class->get_property = gedit_status_combo_box_get_property; +	object_class->set_property = gedit_status_combo_box_set_property; +	 +	klass->changed = gedit_status_combo_box_changed; + +	signals[CHANGED] = +	    g_signal_new ("changed", +			  G_OBJECT_CLASS_TYPE (object_class), +			  G_SIGNAL_RUN_LAST, +			  G_STRUCT_OFFSET (GeditStatusComboBoxClass, +					   changed), NULL, NULL, +			  g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, +			  GTK_TYPE_MENU_ITEM); +			   +	g_object_class_install_property (object_class, PROP_LABEL, +					 g_param_spec_string ("label", +					 		      "LABEL", +					 		      "The label", +					 		      NULL, +					 		      G_PARAM_READWRITE)); + +	/* Set up a style for the button to decrease spacing. */ +	gtk_rc_parse_string ( +		"style \"gedit-status-combo-button-style\"\n" +		"{\n" +		"  GtkWidget::focus-padding = 0\n" +		"  GtkWidget::focus-line-width = 0\n" +		"  xthickness = 0\n" +		"  ythickness = 0\n" +		"}\n" +		"widget \"*.gedit-status-combo-button\" style \"gedit-status-combo-button-style\""); + +	g_type_class_add_private (object_class, sizeof(GeditStatusComboBoxPrivate)); +} + +static void +menu_deactivate (GtkMenu             *menu, +		 GeditStatusComboBox *combo) +{ +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo->priv->button), FALSE); +} + +static void +menu_position_func (GtkMenu		*menu, +	            gint		*x, +		    gint		*y, +		    gboolean		*push_in, +		    GeditStatusComboBox *combo) +{ +	GtkRequisition request; +	 +	*push_in = FALSE; +	 +	gtk_widget_size_request (gtk_widget_get_toplevel (GTK_WIDGET (menu)), &request); +	 +	/* get the origin... */ +	gdk_window_get_origin (GTK_WIDGET (combo)->window, x, y); +	 +	/* make the menu as wide as the widget */ +	if (request.width < GTK_WIDGET (combo)->allocation.width) +	{ +		gtk_widget_set_size_request (GTK_WIDGET (menu), GTK_WIDGET (combo)->allocation.width, -1); +	} +	 +	/* position it above the widget */ +	*y -= request.height; +} + +static void +button_press_event (GtkWidget           *widget, +		    GdkEventButton      *event, +		    GeditStatusComboBox *combo) +{ +	GtkRequisition request; +	gint max_height; +	 +	gtk_widget_size_request (combo->priv->menu, &request); + +	/* do something relative to our own height here, maybe we can do better */ +	max_height = GTK_WIDGET (combo)->allocation.height * 20; +	 +	if (request.height > max_height) +	{ +		gtk_widget_set_size_request (combo->priv->menu, -1, max_height); +		gtk_widget_set_size_request (gtk_widget_get_toplevel (combo->priv->menu), -1, max_height); +	} +	 +	gtk_menu_popup (GTK_MENU (combo->priv->menu),  +			NULL,  +			NULL,  +			(GtkMenuPositionFunc)menu_position_func,  +			combo,  +			event->button,  +			event->time); + +	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo->priv->button), TRUE); + +	if (combo->priv->current_item) +	{ +		gtk_menu_shell_select_item (GTK_MENU_SHELL (combo->priv->menu),  +					    combo->priv->current_item); +	} +} + +static void +set_shadow_type (GeditStatusComboBox *combo) +{ +	GtkShadowType shadow_type; +	GtkWidget *statusbar; + +	/* This is a hack needed to use the shadow type of a statusbar */ +	statusbar = gtk_statusbar_new (); +	gtk_widget_ensure_style (statusbar); + +	gtk_widget_style_get (statusbar, "shadow-type", &shadow_type, NULL); +	gtk_frame_set_shadow_type (GTK_FRAME (combo->priv->frame), shadow_type); + +	gtk_widget_destroy (statusbar); +} + +static void +gedit_status_combo_box_init (GeditStatusComboBox *self) +{ +	self->priv = GEDIT_STATUS_COMBO_BOX_GET_PRIVATE (self); +	 +	gtk_event_box_set_visible_window (GTK_EVENT_BOX (self), TRUE); + +	self->priv->frame = gtk_frame_new (NULL); +	gtk_widget_show (self->priv->frame); +	 +	self->priv->button = gtk_toggle_button_new (); +	gtk_widget_set_name (self->priv->button, "gedit-status-combo-button"); +	gtk_button_set_relief (GTK_BUTTON (self->priv->button), GTK_RELIEF_NONE); +	gtk_widget_show (self->priv->button); + +	set_shadow_type (self); + +	self->priv->hbox = gtk_hbox_new (FALSE, 3); +	gtk_widget_show (self->priv->hbox); +	 +	gtk_container_add (GTK_CONTAINER (self), self->priv->frame); +	gtk_container_add (GTK_CONTAINER (self->priv->frame), self->priv->button); +	gtk_container_add (GTK_CONTAINER (self->priv->button), self->priv->hbox); +	 +	self->priv->label = gtk_label_new (""); +	gtk_widget_show (self->priv->label); +	 +	gtk_label_set_single_line_mode (GTK_LABEL (self->priv->label), TRUE); +	gtk_misc_set_alignment (GTK_MISC (self->priv->label), 0.0, 0.5); +	 +	gtk_box_pack_start (GTK_BOX (self->priv->hbox), self->priv->label, FALSE, TRUE, 0); +	 +	self->priv->item = gtk_label_new (""); +	gtk_widget_show (self->priv->item); +	 +	gtk_label_set_single_line_mode (GTK_LABEL (self->priv->item), TRUE); +	gtk_misc_set_alignment (GTK_MISC (self->priv->item), 0, 0.5); +	 +	gtk_box_pack_start (GTK_BOX (self->priv->hbox), self->priv->item, TRUE, TRUE, 0); +	 +	self->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); +	gtk_widget_show (self->priv->arrow); +	gtk_misc_set_alignment (GTK_MISC (self->priv->arrow), 0.5, 0.5); +	 +	gtk_box_pack_start (GTK_BOX (self->priv->hbox), self->priv->arrow, FALSE, TRUE, 0); +	 +	self->priv->menu = gtk_menu_new (); +	g_object_ref_sink (self->priv->menu); + +	g_signal_connect (self->priv->button,  +			  "button-press-event",  +			  G_CALLBACK (button_press_event),  +			  self); +	g_signal_connect (self->priv->menu, +			  "deactivate", +			  G_CALLBACK (menu_deactivate), +			  self); +} + +/* public functions */ +GtkWidget * +gedit_status_combo_box_new (const gchar *label) +{ +	return g_object_new (GEDIT_TYPE_STATUS_COMBO_BOX, "label", label, NULL); +} + +void +gedit_status_combo_box_set_label (GeditStatusComboBox *combo,  +				  const gchar         *label) +{ +	gchar *text; + +	g_return_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo)); +	 +	text = g_strconcat ("  ", label, ": ", NULL); +	gtk_label_set_markup (GTK_LABEL (combo->priv->label), text); +	g_free (text); +} + +const gchar * +gedit_status_combo_box_get_label (GeditStatusComboBox *combo) +{ +	g_return_val_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo), NULL); + +	return gtk_label_get_label (GTK_LABEL (combo->priv->label)); +} + +static void +item_activated (GtkMenuItem         *item, +		GeditStatusComboBox *combo) +{ +	gedit_status_combo_box_set_item (combo, item); +} + +void +gedit_status_combo_box_add_item (GeditStatusComboBox *combo, +				 GtkMenuItem         *item, +				 const gchar         *text) +{ +	g_return_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo)); +	g_return_if_fail (GTK_IS_MENU_ITEM (item)); + +	gtk_menu_shell_append (GTK_MENU_SHELL (combo->priv->menu), GTK_WIDGET (item)); +	 +	gedit_status_combo_box_set_item_text (combo, item, text); +	g_signal_connect (item, "activate", G_CALLBACK (item_activated), combo); +} + +void +gedit_status_combo_box_remove_item (GeditStatusComboBox *combo, +				    GtkMenuItem         *item) +{ +	g_return_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo)); +	g_return_if_fail (GTK_IS_MENU_ITEM (item)); + +	gtk_container_remove (GTK_CONTAINER (combo->priv->menu), +			      GTK_WIDGET (item)); +} + +GList * +gedit_status_combo_box_get_items (GeditStatusComboBox *combo) +{ +	g_return_val_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo), NULL); + +	return gtk_container_get_children (GTK_CONTAINER (combo->priv->menu)); +} + +const gchar * +gedit_status_combo_box_get_item_text (GeditStatusComboBox *combo, +				      GtkMenuItem	  *item) +{ +	const gchar *ret = NULL; +	 +	g_return_val_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo), NULL); +	g_return_val_if_fail (GTK_IS_MENU_ITEM (item), NULL); +	 +	ret = g_object_get_data (G_OBJECT (item), COMBO_BOX_TEXT_DATA); +	 +	return ret; +} + +void  +gedit_status_combo_box_set_item_text (GeditStatusComboBox *combo, +				      GtkMenuItem	  *item, +				      const gchar         *text) +{ +	g_return_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo)); +	g_return_if_fail (GTK_IS_MENU_ITEM (item)); + +	g_object_set_data_full (G_OBJECT (item),  +				COMBO_BOX_TEXT_DATA, +				g_strdup (text), +				(GDestroyNotify)g_free); +} + +void +gedit_status_combo_box_set_item (GeditStatusComboBox *combo, +				 GtkMenuItem         *item) +{ +	g_return_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo)); +	g_return_if_fail (GTK_IS_MENU_ITEM (item)); + +	g_signal_emit (combo, signals[CHANGED], 0, item, NULL); +} + +GtkLabel * +gedit_status_combo_box_get_item_label (GeditStatusComboBox *combo) +{ +	g_return_val_if_fail (GEDIT_IS_STATUS_COMBO_BOX (combo), NULL); +	 +	return GTK_LABEL (combo->priv->item); +} + diff --git a/gedit/gedit-status-combo-box.h b/gedit/gedit-status-combo-box.h new file mode 100755 index 00000000..e3593a8a --- /dev/null +++ b/gedit/gedit-status-combo-box.h @@ -0,0 +1,82 @@ +/* + * gedit-status-combo-box.h + * This file is part of gedit + * + * Copyright (C) 2008 - Jesse van den Kieboom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GEDIT_STATUS_COMBO_BOX_H__ +#define __GEDIT_STATUS_COMBO_BOX_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_STATUS_COMBO_BOX		(gedit_status_combo_box_get_type ()) +#define GEDIT_STATUS_COMBO_BOX(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_STATUS_COMBO_BOX, GeditStatusComboBox)) +#define GEDIT_STATUS_COMBO_BOX_CONST(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_STATUS_COMBO_BOX, GeditStatusComboBox const)) +#define GEDIT_STATUS_COMBO_BOX_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_STATUS_COMBO_BOX, GeditStatusComboBoxClass)) +#define GEDIT_IS_STATUS_COMBO_BOX(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_STATUS_COMBO_BOX)) +#define GEDIT_IS_STATUS_COMBO_BOX_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_STATUS_COMBO_BOX)) +#define GEDIT_STATUS_COMBO_BOX_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_STATUS_COMBO_BOX, GeditStatusComboBoxClass)) + +typedef struct _GeditStatusComboBox		GeditStatusComboBox; +typedef struct _GeditStatusComboBoxClass	GeditStatusComboBoxClass; +typedef struct _GeditStatusComboBoxPrivate	GeditStatusComboBoxPrivate; + +struct _GeditStatusComboBox { +	GtkEventBox parent; +	 +	GeditStatusComboBoxPrivate *priv; +}; + +struct _GeditStatusComboBoxClass { +	GtkEventBoxClass parent_class; +	 +	void (*changed) (GeditStatusComboBox *combo, +			 GtkMenuItem         *item); +}; + +GType gedit_status_combo_box_get_type 			(void) G_GNUC_CONST; +GtkWidget *gedit_status_combo_box_new			(const gchar 		*label); + +const gchar *gedit_status_combo_box_get_label 		(GeditStatusComboBox 	*combo); +void gedit_status_combo_box_set_label 			(GeditStatusComboBox 	*combo, +							 const gchar         	*label); + +void gedit_status_combo_box_add_item 			(GeditStatusComboBox 	*combo, +							 GtkMenuItem         	*item, +							 const gchar         	*text); +void gedit_status_combo_box_remove_item			(GeditStatusComboBox    *combo, +							 GtkMenuItem            *item); + +GList *gedit_status_combo_box_get_items			(GeditStatusComboBox    *combo); +const gchar *gedit_status_combo_box_get_item_text 	(GeditStatusComboBox	*combo, +							 GtkMenuItem		*item); +void gedit_status_combo_box_set_item_text 		(GeditStatusComboBox	*combo, +							 GtkMenuItem		*item, +							 const gchar            *text); + +void gedit_status_combo_box_set_item			(GeditStatusComboBox	*combo, +							 GtkMenuItem		*item); + +GtkLabel *gedit_status_combo_box_get_item_label		(GeditStatusComboBox	*combo); + +G_END_DECLS + +#endif /* __GEDIT_STATUS_COMBO_BOX_H__ */ diff --git a/gedit/gedit-statusbar.c b/gedit/gedit-statusbar.c new file mode 100755 index 00000000..178147c8 --- /dev/null +++ b/gedit/gedit-statusbar.c @@ -0,0 +1,448 @@ +/* + * gedit-statusbar.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "gedit-statusbar.h" + +#define GEDIT_STATUSBAR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\ +					    GEDIT_TYPE_STATUSBAR,\ +					    GeditStatusbarPrivate)) + +struct _GeditStatusbarPrivate +{ +	GtkWidget     *overwrite_mode_statusbar; +	GtkWidget     *cursor_position_statusbar; + +	GtkWidget     *state_frame; +	GtkWidget     *load_image; +	GtkWidget     *save_image; +	GtkWidget     *print_image; + +	GtkWidget     *error_frame; +	GtkWidget     *error_event_box; + +	/* tmp flash timeout data */ +	guint          flash_timeout; +	guint          flash_context_id; +	guint          flash_message_id; +}; + +G_DEFINE_TYPE(GeditStatusbar, gedit_statusbar, GTK_TYPE_STATUSBAR) + + +static gchar * +get_overwrite_mode_string (gboolean overwrite) +{ +	return g_strconcat ("  ", overwrite ? _("OVR") :  _("INS"), NULL); +} + +static gint +get_overwrite_mode_length (void) +{ +	return 2 + MAX (g_utf8_strlen (_("OVR"), -1), g_utf8_strlen (_("INS"), -1)); +} + +static void +gedit_statusbar_notify (GObject    *object, +			GParamSpec *pspec) +{ +	/* don't allow gtk_statusbar_set_has_resize_grip to mess with us. +	 * See _gedit_statusbar_set_has_resize_grip for an explanation. +	 */ +	if (strcmp (g_param_spec_get_name (pspec), "has-resize-grip") == 0) +	{ +		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (object), FALSE); +		return; +	} + +	if (G_OBJECT_CLASS (gedit_statusbar_parent_class)->notify) +		G_OBJECT_CLASS (gedit_statusbar_parent_class)->notify (object, pspec); +} + +static void +gedit_statusbar_finalize (GObject *object) +{ +	GeditStatusbar *statusbar = GEDIT_STATUSBAR (object); + +	if (statusbar->priv->flash_timeout > 0) +		g_source_remove (statusbar->priv->flash_timeout); + +	G_OBJECT_CLASS (gedit_statusbar_parent_class)->finalize (object); +} + +static void +gedit_statusbar_class_init (GeditStatusbarClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->notify = gedit_statusbar_notify; +	object_class->finalize = gedit_statusbar_finalize; + +	g_type_class_add_private (object_class, sizeof (GeditStatusbarPrivate)); +} + +#define RESIZE_GRIP_EXTRA_WIDTH 30 + +static void +set_statusbar_width_chars (GtkWidget *statusbar, +			   gint       n_chars, +			   gboolean   has_resize_grip) +{ +	PangoContext *context; +	PangoFontMetrics *metrics; +	gint char_width, digit_width, width; +	GtkStyle *style; + +	context = gtk_widget_get_pango_context (statusbar); +	style = gtk_widget_get_style (GTK_WIDGET (statusbar)); +	metrics = pango_context_get_metrics (context, +					     style->font_desc, +					     pango_context_get_language (context)); + +	char_width = pango_font_metrics_get_approximate_digit_width (metrics); +	digit_width = pango_font_metrics_get_approximate_char_width (metrics); + +	width = PANGO_PIXELS (MAX (char_width, digit_width) * n_chars); + +	pango_font_metrics_unref (metrics); + +	/* If there is a resize grip, allocate some extra width. +	 * It would be nice to calculate the exact size programmatically +	 * but I could not find out how to do it */ +	if (has_resize_grip) +		width += RESIZE_GRIP_EXTRA_WIDTH; + +	gtk_widget_set_size_request (statusbar, width, -1); +} + +static void +gedit_statusbar_init (GeditStatusbar *statusbar) +{ +	GtkWidget *hbox; +	GtkWidget *error_image; + +	statusbar->priv = GEDIT_STATUSBAR_GET_PRIVATE (statusbar); + +	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), FALSE); + +	statusbar->priv->overwrite_mode_statusbar = gtk_statusbar_new (); +	gtk_widget_show (statusbar->priv->overwrite_mode_statusbar); +	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar->priv->overwrite_mode_statusbar), +					   TRUE); +	set_statusbar_width_chars (statusbar->priv->overwrite_mode_statusbar, +				   get_overwrite_mode_length (), +				   TRUE); +	gtk_box_pack_end (GTK_BOX (statusbar), +			  statusbar->priv->overwrite_mode_statusbar, +			  FALSE, TRUE, 0); + +	statusbar->priv->cursor_position_statusbar = gtk_statusbar_new (); +	gtk_widget_show (statusbar->priv->cursor_position_statusbar); +	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar->priv->cursor_position_statusbar), +					   FALSE); +	set_statusbar_width_chars (statusbar->priv->cursor_position_statusbar, 18, FALSE); +	gtk_box_pack_end (GTK_BOX (statusbar), +			  statusbar->priv->cursor_position_statusbar, +			  FALSE, TRUE, 0); + +	statusbar->priv->state_frame = gtk_frame_new (NULL); +	gtk_frame_set_shadow_type (GTK_FRAME (statusbar->priv->state_frame), GTK_SHADOW_IN); + +	hbox = gtk_hbox_new (FALSE, 0); +	gtk_container_add (GTK_CONTAINER (statusbar->priv->state_frame), hbox); + +	statusbar->priv->load_image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); +	statusbar->priv->save_image = gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU); +	statusbar->priv->print_image = gtk_image_new_from_stock (GTK_STOCK_PRINT, GTK_ICON_SIZE_MENU); + +	gtk_widget_show (hbox); + +	gtk_box_pack_start (GTK_BOX (hbox), +			    statusbar->priv->load_image, +			    FALSE, TRUE, 4); +	gtk_box_pack_start (GTK_BOX (hbox), +			    statusbar->priv->save_image, +			    FALSE, TRUE, 4); +	gtk_box_pack_start (GTK_BOX (hbox), +			    statusbar->priv->print_image, +			    FALSE, TRUE, 4); + +	gtk_box_pack_start (GTK_BOX (statusbar), +			    statusbar->priv->state_frame, +			    FALSE, TRUE, 0); + +	statusbar->priv->error_frame = gtk_frame_new (NULL); +	gtk_frame_set_shadow_type (GTK_FRAME (statusbar->priv->error_frame), GTK_SHADOW_IN); + +	error_image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_MENU); +	gtk_misc_set_padding (GTK_MISC (error_image), 4, 0); +	gtk_widget_show (error_image); + +	statusbar->priv->error_event_box = gtk_event_box_new (); +	gtk_event_box_set_visible_window  (GTK_EVENT_BOX (statusbar->priv->error_event_box), +					   FALSE); +	gtk_widget_show (statusbar->priv->error_event_box); + +	gtk_container_add (GTK_CONTAINER (statusbar->priv->error_frame), +			   statusbar->priv->error_event_box); +	gtk_container_add (GTK_CONTAINER (statusbar->priv->error_event_box), +			   error_image); + +	gtk_box_pack_start (GTK_BOX (statusbar), +			    statusbar->priv->error_frame, +			    FALSE, TRUE, 0); + +	gtk_box_reorder_child (GTK_BOX (statusbar), +			       statusbar->priv->error_frame, +			       0); +} + +/** + * gedit_statusbar_new: + * + * Creates a new #GeditStatusbar. + * + * Return value: the new #GeditStatusbar object + **/ +GtkWidget * +gedit_statusbar_new (void) +{ +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_STATUSBAR, NULL)); +} + +/** + * gedit_set_has_resize_grip: + * @statusbar: a #GeditStatusbar + * @show: if the resize grip is shown + * + * Sets if a resize grip showld be shown. + * + **/ + /* +  * I don't like this much, in a perfect world it would have been +  * possible to override the parent property and use +  * gtk_statusbar_set_has_resize_grip. Unfortunately this is not +  * possible and it's not even possible to intercept the notify signal +  * since the parent property should always be set to false thus when +  * using set_resize_grip (FALSE) the property doesn't change and the +  * notification is not emitted. +  * For now just add this private method; if needed we can turn it into +  * a property. +  */ +void +_gedit_statusbar_set_has_resize_grip (GeditStatusbar *bar, +				      gboolean        show) +{ +	g_return_if_fail (GEDIT_IS_STATUSBAR (bar)); + +	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (bar->priv->overwrite_mode_statusbar), +					   show); +} + +/** + * gedit_statusbar_set_overwrite: + * @statusbar: a #GeditStatusbar + * @overwrite: if the overwrite mode is set + * + * Sets the overwrite mode on the statusbar. + **/ +void +gedit_statusbar_set_overwrite (GeditStatusbar *statusbar, +                               gboolean        overwrite) +{ +	gchar *msg; + +	g_return_if_fail (GEDIT_IS_STATUSBAR (statusbar)); + +	gtk_statusbar_pop (GTK_STATUSBAR (statusbar->priv->overwrite_mode_statusbar), 0); + +	msg = get_overwrite_mode_string (overwrite); + +	gtk_statusbar_push (GTK_STATUSBAR (statusbar->priv->overwrite_mode_statusbar), 0, msg); + +      	g_free (msg); +} + +void +gedit_statusbar_clear_overwrite (GeditStatusbar *statusbar) +{ +	g_return_if_fail (GEDIT_IS_STATUSBAR (statusbar)); + +	gtk_statusbar_pop (GTK_STATUSBAR (statusbar->priv->overwrite_mode_statusbar), 0); +} + +/** + * gedit_statusbar_cursor_position: + * @statusbar: an #GeditStatusbar + * @line: line position + * @col: column position + * + * Sets the cursor position on the statusbar. + **/ +void +gedit_statusbar_set_cursor_position (GeditStatusbar *statusbar, +				     gint            line, +				     gint            col) +{ +	gchar *msg; + +	g_return_if_fail (GEDIT_IS_STATUSBAR (statusbar)); + +	gtk_statusbar_pop (GTK_STATUSBAR (statusbar->priv->cursor_position_statusbar), 0); + +	if ((line == -1) && (col == -1)) +		return; + +	/* Translators: "Ln" is an abbreviation for "Line", Col is an abbreviation for "Column". Please, +	use abbreviations if possible to avoid space problems. */ +	msg = g_strdup_printf (_("  Ln %d, Col %d"), line, col); + +	gtk_statusbar_push (GTK_STATUSBAR (statusbar->priv->cursor_position_statusbar), 0, msg); + +      	g_free (msg); +} + +static gboolean +remove_message_timeout (GeditStatusbar *statusbar) +{ +	gtk_statusbar_remove (GTK_STATUSBAR (statusbar), +			      statusbar->priv->flash_context_id, +			      statusbar->priv->flash_message_id); + +	/* remove the timeout */ +	statusbar->priv->flash_timeout = 0; +  	return FALSE; +} + +/** + * gedit_statusbar_flash_message: + * @statusbar: a #GeditStatusbar + * @context_id: message context_id + * @format: message to flash on the statusbar + * + * Flash a temporary message on the statusbar. + */ +void +gedit_statusbar_flash_message (GeditStatusbar *statusbar, +			       guint           context_id, +			       const gchar    *format, ...) +{ +	const guint32 flash_length = 3000; /* three seconds */ +	va_list args; +	gchar *msg; + +	g_return_if_fail (GEDIT_IS_STATUSBAR (statusbar)); +	g_return_if_fail (format != NULL); + +	va_start (args, format); +	msg = g_strdup_vprintf (format, args); +	va_end (args); + +	/* remove a currently ongoing flash message */ +	if (statusbar->priv->flash_timeout > 0) +	{ +		g_source_remove (statusbar->priv->flash_timeout); +		statusbar->priv->flash_timeout = 0; + +		gtk_statusbar_remove (GTK_STATUSBAR (statusbar), +				      statusbar->priv->flash_context_id, +				      statusbar->priv->flash_message_id); +	} + +	statusbar->priv->flash_context_id = context_id; +	statusbar->priv->flash_message_id = gtk_statusbar_push (GTK_STATUSBAR (statusbar), +								context_id, +								msg); + +	statusbar->priv->flash_timeout = g_timeout_add (flash_length, +							(GtkFunction) remove_message_timeout, +							statusbar); + +	g_free (msg); +} + +void +gedit_statusbar_set_window_state (GeditStatusbar   *statusbar, +				  GeditWindowState  state, +				  gint              num_of_errors) +{ +	g_return_if_fail (GEDIT_IS_STATUSBAR (statusbar)); + +	gtk_widget_hide (statusbar->priv->state_frame); +	gtk_widget_hide (statusbar->priv->save_image); +	gtk_widget_hide (statusbar->priv->load_image); +	gtk_widget_hide (statusbar->priv->print_image); + +	if (state & GEDIT_WINDOW_STATE_SAVING) +	{ +		gtk_widget_show (statusbar->priv->state_frame); +		gtk_widget_show (statusbar->priv->save_image); +	} +	if (state & GEDIT_WINDOW_STATE_LOADING) +	{ +		gtk_widget_show (statusbar->priv->state_frame); +		gtk_widget_show (statusbar->priv->load_image); +	} + +	if (state & GEDIT_WINDOW_STATE_PRINTING) +	{ +		gtk_widget_show (statusbar->priv->state_frame); +		gtk_widget_show (statusbar->priv->print_image); +	} + +	if (state & GEDIT_WINDOW_STATE_ERROR) +	{ +	 	gchar *tip; + + 		tip = g_strdup_printf (ngettext("There is a tab with errors", +						"There are %d tabs with errors", +						num_of_errors), +			       		num_of_errors); + +		gtk_widget_set_tooltip_text (statusbar->priv->error_event_box, +					     tip); +		g_free (tip); + +		gtk_widget_show (statusbar->priv->error_frame); +	} +	else +	{ +		gtk_widget_hide (statusbar->priv->error_frame); +	} +} + + diff --git a/gedit/gedit-statusbar.h b/gedit/gedit-statusbar.h new file mode 100755 index 00000000..b98790b7 --- /dev/null +++ b/gedit/gedit-statusbar.h @@ -0,0 +1,99 @@ +/* + * gedit-statusbar.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef GEDIT_STATUSBAR_H +#define GEDIT_STATUSBAR_H + +#include <gtk/gtk.h> +#include <gedit/gedit-window.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_STATUSBAR		(gedit_statusbar_get_type ()) +#define GEDIT_STATUSBAR(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_STATUSBAR, GeditStatusbar)) +#define GEDIT_STATUSBAR_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_STATUSBAR, GeditStatusbarClass)) +#define GEDIT_IS_STATUSBAR(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_STATUSBAR)) +#define GEDIT_IS_STATUSBAR_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_STATUSBAR)) +#define GEDIT_STATUSBAR_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_STATUSBAR, GeditStatusbarClass)) + +typedef struct _GeditStatusbar		GeditStatusbar; +typedef struct _GeditStatusbarPrivate	GeditStatusbarPrivate; +typedef struct _GeditStatusbarClass	GeditStatusbarClass; + +struct _GeditStatusbar +{ +        GtkStatusbar parent; + +	/* <private/> */ +        GeditStatusbarPrivate *priv; +}; + +struct _GeditStatusbarClass +{ +        GtkStatusbarClass parent_class; +}; + +GType		 gedit_statusbar_get_type		(void) G_GNUC_CONST; + +GtkWidget	*gedit_statusbar_new			(void); + +/* FIXME: status is not defined in any .h */ +#define GeditStatus gint +void		 gedit_statusbar_set_window_state	(GeditStatusbar   *statusbar, +							 GeditWindowState  state, +							 gint              num_of_errors); + +void		 gedit_statusbar_set_overwrite		(GeditStatusbar   *statusbar, +							 gboolean          overwrite); + +void		 gedit_statusbar_set_cursor_position	(GeditStatusbar   *statusbar, +							 gint              line, +							 gint              col); + +void		 gedit_statusbar_clear_overwrite 	(GeditStatusbar   *statusbar); + +void		 gedit_statusbar_flash_message		(GeditStatusbar   *statusbar, +							 guint             context_id, +							 const gchar      *format, +							 ...) G_GNUC_PRINTF(3, 4); +/* FIXME: these would be nice for plugins... +void		 gedit_statusbar_add_widget		(GeditStatusbar   *statusbar, +							 GtkWidget        *widget); +void		 gedit_statusbar_remove_widget		(GeditStatusbar   *statusbar, +							 GtkWidget        *widget); +*/ + +/* + * Non exported functions + */ +void		_gedit_statusbar_set_has_resize_grip	(GeditStatusbar   *statusbar, +							 gboolean          show); + +G_END_DECLS + +#endif diff --git a/gedit/gedit-style-scheme-manager.c b/gedit/gedit-style-scheme-manager.c new file mode 100755 index 00000000..a65fedd7 --- /dev/null +++ b/gedit/gedit-style-scheme-manager.c @@ -0,0 +1,364 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-source-style-manager.c + * + * Copyright (C) 2007 - Paolo Borelli and Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2007. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#include <string.h> +#include <errno.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include "gedit-style-scheme-manager.h" +#include "gedit-prefs-manager.h" +#include "gedit-dirs.h" + +static GtkSourceStyleSchemeManager *style_scheme_manager = NULL; + +static gchar * +get_gedit_styles_path (void) +{ +	gchar *config_dir; +	gchar *dir = NULL; + +	config_dir = gedit_dirs_get_user_config_dir (); + +	if (config_dir != NULL) +	{ +		dir = g_build_filename (config_dir, +					"styles", +					NULL); +		g_free (config_dir); +	} +	 +	return dir; +} + +static void +add_gedit_styles_path (GtkSourceStyleSchemeManager *mgr) +{ +	gchar *dir; + +	dir = get_gedit_styles_path(); + +	if (dir != NULL) +	{ +		gtk_source_style_scheme_manager_append_search_path (mgr, dir); +		g_free (dir); +	}	 +} + +GtkSourceStyleSchemeManager * +gedit_get_style_scheme_manager (void) +{ +	if (style_scheme_manager == NULL) +	{ +		style_scheme_manager = gtk_source_style_scheme_manager_new (); +		add_gedit_styles_path (style_scheme_manager); +	} + +	return style_scheme_manager; +} + +static gint +schemes_compare (gconstpointer a, gconstpointer b) +{ +	GtkSourceStyleScheme *scheme_a = (GtkSourceStyleScheme *)a; +	GtkSourceStyleScheme *scheme_b = (GtkSourceStyleScheme *)b; + +	const gchar *name_a = gtk_source_style_scheme_get_name (scheme_a); +	const gchar *name_b = gtk_source_style_scheme_get_name (scheme_b); + +	return g_utf8_collate (name_a, name_b); +} + +GSList * +gedit_style_scheme_manager_list_schemes_sorted (GtkSourceStyleSchemeManager *manager) +{ +	const gchar * const * scheme_ids; +	GSList *schemes = NULL; + +	g_return_val_if_fail (GTK_IS_SOURCE_STYLE_SCHEME_MANAGER (manager), NULL); + +	scheme_ids = gtk_source_style_scheme_manager_get_scheme_ids (manager); +	 +	while (*scheme_ids != NULL) +	{ +		GtkSourceStyleScheme *scheme; + +		scheme = gtk_source_style_scheme_manager_get_scheme (manager,  +								     *scheme_ids); + +		schemes = g_slist_prepend (schemes, scheme); + +		++scheme_ids; +	} + +	if (schemes != NULL) +		schemes = g_slist_sort (schemes, (GCompareFunc)schemes_compare); + +	return schemes; +} + +gboolean +_gedit_style_scheme_manager_scheme_is_gedit_user_scheme (GtkSourceStyleSchemeManager *manager, +							 const gchar                 *scheme_id) +{ +	GtkSourceStyleScheme *scheme; +	const gchar *filename; +	gchar *dir; +	gboolean res = FALSE; + +	scheme = gtk_source_style_scheme_manager_get_scheme (manager, scheme_id); +	if (scheme == NULL) +		return FALSE; + +	filename = gtk_source_style_scheme_get_filename (scheme); +	if (filename == NULL) +		return FALSE; + +	dir = get_gedit_styles_path (); + +	res = g_str_has_prefix (filename, dir); + +	g_free (dir); + +	return res; +} + +/** + * file_copy: + * @name: a pointer to a %NULL-terminated string, that names + * the file to be copied, in the GLib file name encoding + * @dest_name: a pointer to a %NULL-terminated string, that is the + * name for the destination file, in the GLib file name encoding + * @error: return location for a #GError, or %NULL + * + * Copies file @name to @dest_name. + * + * If the call was successful, it returns %TRUE. If the call was not + * successful, it returns %FALSE and sets @error. The error domain + * is #G_FILE_ERROR. Possible error + * codes are those in the #GFileError enumeration. + * + * Return value: %TRUE on success, %FALSE otherwise. + */ +static gboolean +file_copy (const gchar  *name, +	   const gchar  *dest_name, +	   GError      **error) +{ +	gchar *contents; +	gsize length; +	gchar *dest_dir; + +	/* FIXME - Paolo (Aug. 13, 2007): +	 * Since the style scheme files are relatively small, we can implement +	 * file copy getting all the content of the source file in a buffer and +	 * then write the content to the destination file. In this way we +	 * can use the g_file_get_contents and g_file_set_contents and avoid to +	 * write custom code to copy the file (with sane error management). +	 * If needed we can improve this code later. */ + +	g_return_val_if_fail (name != NULL, FALSE); +	g_return_val_if_fail (dest_name != NULL, FALSE); +	g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + +	/* Note: we allow to copy a file to itself since this is not a problem +	 * in our use case */ + +	/* Ensure the destination directory exists */ +	dest_dir = g_path_get_dirname (dest_name); + +	errno = 0; +	if (g_mkdir_with_parents (dest_dir, 0755) != 0) +	{ +		gint save_errno = errno; +		gchar *display_filename = g_filename_display_name (dest_dir); + +		g_set_error (error, +			     G_FILE_ERROR, +			     g_file_error_from_errno (save_errno), +			     _("Directory '%s' could not be created: g_mkdir_with_parents() failed: %s"), +			     display_filename, +			     g_strerror (save_errno)); + +		g_free (dest_dir); +		g_free (display_filename); + +		return FALSE; +	} + +	g_free (dest_dir); + +	if (!g_file_get_contents (name, &contents, &length, error)) +		return FALSE; + +	if (!g_file_set_contents (dest_name, contents, length, error)) +		return FALSE; + +	g_free (contents); + +	return TRUE; +} + +/** + * _gedit_style_scheme_manager_install_scheme: + * @manager: a #GtkSourceStyleSchemeManager + * @fname: the file name of the style scheme to be installed + * + * Install a new user scheme. + * This function copies @fname in #GEDIT_STYLES_DIR and ask the style manager to + * recompute the list of available style schemes. It then checks if a style + * scheme with the right file name exists. + * + * If the call was succesful, it returns the id of the installed scheme + * otherwise %NULL. + * + * Return value: the id of the installed scheme, %NULL otherwise. + */ +const gchar * +_gedit_style_scheme_manager_install_scheme (GtkSourceStyleSchemeManager *manager, +					    const gchar                 *fname) +{ +	gchar *new_file_name = NULL; +	gchar *dirname; +	gchar *styles_dir; +	GError *error = NULL; +	gboolean copied = FALSE; + +	const gchar* const *ids; + +	g_return_val_if_fail (GTK_IS_SOURCE_STYLE_SCHEME_MANAGER (manager), NULL); +	g_return_val_if_fail (fname != NULL, NULL); + +	dirname = g_path_get_dirname (fname); +	styles_dir = get_gedit_styles_path(); + +	if (strcmp (dirname, styles_dir) != 0) +	{ +		gchar *basename; + +		basename = g_path_get_basename (fname); +		new_file_name = g_build_filename (styles_dir, basename, NULL); +		g_free (basename); + +		/* Copy the style scheme file into GEDIT_STYLES_DIR */ +		if (!file_copy (fname, new_file_name, &error)) +		{ +			g_free (new_file_name); + +			g_message ("Cannot install style scheme:\n%s", +				   error->message); + +			return NULL; +		} + +		copied = TRUE; +	} +	else +	{ +		new_file_name = g_strdup (fname); +	} + +	g_free (dirname); +	g_free (styles_dir); + +	/* Reload the available style schemes */ +	gtk_source_style_scheme_manager_force_rescan (manager); + +	/* Check the new style scheme has been actually installed */ +	ids = gtk_source_style_scheme_manager_get_scheme_ids (manager); + +	while (*ids != NULL) +	{ +		GtkSourceStyleScheme *scheme; +		const gchar *filename; + +		scheme = gtk_source_style_scheme_manager_get_scheme ( +				gedit_get_style_scheme_manager (), *ids); + +		filename = gtk_source_style_scheme_get_filename (scheme); + +		if (filename && (strcmp (filename, new_file_name) == 0)) +		{ +			/* The style scheme has been correctly installed */ +			g_free (new_file_name); + +			return gtk_source_style_scheme_get_id (scheme); +		} +		++ids; +	} + +	/* The style scheme has not been correctly installed */ +	if (copied) +		g_unlink (new_file_name); + +	g_free (new_file_name); + +	return NULL; +} + +/** + * _gedit_style_scheme_manager_uninstall_scheme: + * @manager: a #GtkSourceStyleSchemeManager + * @id: the id of the style scheme to be uninstalled + * + * Uninstall a user scheme. + * + * If the call was succesful, it returns %TRUE + * otherwise %FALSE. + * + * Return value: %TRUE on success, %FALSE otherwise. + */ +gboolean +_gedit_style_scheme_manager_uninstall_scheme (GtkSourceStyleSchemeManager *manager, +					      const gchar                 *id) +{ +	GtkSourceStyleScheme *scheme; +	const gchar *filename; + +	g_return_val_if_fail (GTK_IS_SOURCE_STYLE_SCHEME_MANAGER (manager), FALSE); +	g_return_val_if_fail (id != NULL, FALSE); + +	scheme = gtk_source_style_scheme_manager_get_scheme (manager, id); +	if (scheme == NULL) +		return FALSE; + +	filename = gtk_source_style_scheme_get_filename (scheme); +	if (filename == NULL) +		return FALSE; + +	if (g_unlink (filename) == -1) +		return FALSE; +		 +	/* Reload the available style schemes */ +	gtk_source_style_scheme_manager_force_rescan (manager); +	 +	return TRUE;	 +} diff --git a/gedit/gedit-style-scheme-manager.h b/gedit/gedit-style-scheme-manager.h new file mode 100755 index 00000000..406719ae --- /dev/null +++ b/gedit/gedit-style-scheme-manager.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gedit-style-scheme-manager.h + * + * Copyright (C) 2007 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + * + * $Id: gedit-source-style-manager.h 5598 2007-04-15 13:16:24Z pborelli $ + */ + +#ifndef __GEDIT_STYLE_SCHEME_MANAGER_H__ +#define __GEDIT_STYLE_SCHEME_MANAGER_H__ + +#include <gtksourceview/gtksourcestyleschememanager.h> + +G_BEGIN_DECLS + +GtkSourceStyleSchemeManager * +		 gedit_get_style_scheme_manager		(void); + +/* Returns a sorted list of style schemes */ +GSList		*gedit_style_scheme_manager_list_schemes_sorted +							(GtkSourceStyleSchemeManager *manager); + +/* + * Non exported functions + */ +gboolean	 _gedit_style_scheme_manager_scheme_is_gedit_user_scheme +							(GtkSourceStyleSchemeManager *manager, +							 const gchar                 *scheme_id); + +const gchar	*_gedit_style_scheme_manager_install_scheme +							(GtkSourceStyleSchemeManager *manager, +							 const gchar                 *fname); + +gboolean	 _gedit_style_scheme_manager_uninstall_scheme +							(GtkSourceStyleSchemeManager *manager, +							 const gchar                 *id); + +G_END_DECLS + +#endif /* __GEDIT_STYLE_SCHEME_MANAGER_H__ */ diff --git a/gedit/gedit-tab-label.c b/gedit/gedit-tab-label.c new file mode 100755 index 00000000..a358ae94 --- /dev/null +++ b/gedit/gedit-tab-label.c @@ -0,0 +1,371 @@ +/* + * gedit-tab-label.c + * This file is part of gedit + * + * Copyright (C) 2010 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include "gedit-tab-label.h" +#include "gedit-close-button.h" + +#ifdef BUILD_SPINNER +#include "gedit-spinner.h" +#endif + +#define GEDIT_TAB_LABEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_TAB_LABEL, GeditTabLabelPrivate)) + +/* Signals */ +enum +{ +	CLOSE_CLICKED, +	LAST_SIGNAL +}; + +enum +{ +	PROP_0, +	PROP_TAB +}; + +struct _GeditTabLabelPrivate +{ +	GeditTab *tab; + +	GtkWidget *ebox; +	GtkWidget *close_button; +	GtkWidget *spinner; +	GtkWidget *icon; +	GtkWidget *label; + +	gboolean close_button_sensitive; +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GeditTabLabel, gedit_tab_label, GTK_TYPE_HBOX) + +static void +gedit_tab_label_finalize (GObject *object) +{ +	G_OBJECT_CLASS (gedit_tab_label_parent_class)->finalize (object); +} + +static void +gedit_tab_label_set_property (GObject      *object, +			      guint         prop_id, +			      const GValue *value, +			      GParamSpec   *pspec) +{ +	GeditTabLabel *tab_label = GEDIT_TAB_LABEL (object); + +	switch (prop_id) +	{ +		case PROP_TAB: +			tab_label->priv->tab = GEDIT_TAB (g_value_get_object (value)); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +gedit_tab_label_get_property (GObject    *object, +			      guint       prop_id, +			      GValue     *value, +			      GParamSpec *pspec) +{ +	GeditTabLabel *tab_label = GEDIT_TAB_LABEL (object); + +	switch (prop_id) +	{ +		case PROP_TAB: +			g_value_set_object (value, tab_label->priv->tab); +			break; + +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break; +	} +} + +static void +close_button_clicked_cb (GtkWidget     *widget,  +			 GeditTabLabel *tab_label) +{ +	g_signal_emit (tab_label, signals[CLOSE_CLICKED], 0, NULL); +} + +static void +sync_tip (GeditTab *tab, GeditTabLabel *tab_label) +{ +	gchar *str; + +	str = _gedit_tab_get_tooltips (tab); +	g_return_if_fail (str != NULL); + +	gtk_widget_set_tooltip_markup (tab_label->priv->ebox, str); +	g_free (str); +} + +static void +sync_name (GeditTab *tab, GParamSpec *pspec, GeditTabLabel *tab_label) +{ +	gchar *str; + +	g_return_if_fail (tab == tab_label->priv->tab); + +	str = _gedit_tab_get_name (tab); +	g_return_if_fail (str != NULL); + +	gtk_label_set_text (GTK_LABEL (tab_label->priv->label), str); +	g_free (str); + +	sync_tip (tab, tab_label); +} + +static void +sync_state (GeditTab *tab, GParamSpec *pspec, GeditTabLabel *tab_label) +{ +	GeditTabState  state; + +	g_return_if_fail (tab == tab_label->priv->tab); + +	state = gedit_tab_get_state (tab); + +	gtk_widget_set_sensitive (tab_label->priv->close_button, +				  tab_label->priv->close_button_sensitive && +				  (state != GEDIT_TAB_STATE_CLOSING) && +				  (state != GEDIT_TAB_STATE_SAVING)  && +				  (state != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) && +				  (state != GEDIT_TAB_STATE_SAVING_ERROR)); + +	if ((state == GEDIT_TAB_STATE_LOADING)   || +	    (state == GEDIT_TAB_STATE_SAVING)    || +	    (state == GEDIT_TAB_STATE_REVERTING)) +	{ +		gtk_widget_hide (tab_label->priv->icon); + +		gtk_widget_show (tab_label->priv->spinner); +#ifdef BUILD_SPINNER +		gedit_spinner_start (GEDIT_SPINNER (tab_label->priv->spinner)); +#else +		gtk_spinner_start (GTK_SPINNER (tab_label->priv->spinner)); +#endif +	} +	else +	{ +		GdkPixbuf *pixbuf; + +		pixbuf = _gedit_tab_get_icon (tab); +		gtk_image_set_from_pixbuf (GTK_IMAGE (tab_label->priv->icon), pixbuf); + +		if (pixbuf != NULL) +			g_object_unref (pixbuf); + +		gtk_widget_show (tab_label->priv->icon); + +		gtk_widget_hide (tab_label->priv->spinner); +#ifdef BUILD_SPINNER +		gedit_spinner_stop (GEDIT_SPINNER (tab_label->priv->spinner)); +#else +		gtk_spinner_stop (GTK_SPINNER (tab_label->priv->spinner)); +#endif +	} + +	/* sync tip since encoding is known only after load/save end */ +	sync_tip (tab, tab_label); +} + +static void +gedit_tab_label_constructed (GObject *object) +{ +	GeditTabLabel *tab_label = GEDIT_TAB_LABEL (object); + +	if (!tab_label->priv->tab) +	{ +		g_critical ("The tab label was not properly constructed"); +		return; +	} + +	sync_name (tab_label->priv->tab, NULL, tab_label); +	sync_state (tab_label->priv->tab, NULL, tab_label); + +	g_signal_connect_object (tab_label->priv->tab, +				 "notify::name", +				 G_CALLBACK (sync_name), +				 tab_label, +				 0); + +	g_signal_connect_object (tab_label->priv->tab, +				 "notify::state", +				 G_CALLBACK (sync_state), +				 tab_label, +				 0); +} + +static void +gedit_tab_label_class_init (GeditTabLabelClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	 +	object_class->finalize = gedit_tab_label_finalize; +	object_class->set_property = gedit_tab_label_set_property; +	object_class->get_property = gedit_tab_label_get_property; +	object_class->constructed = gedit_tab_label_constructed; + +	signals[CLOSE_CLICKED] = +		g_signal_new ("close-clicked", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_LAST, +			      G_STRUCT_OFFSET (GeditTabLabelClass, close_clicked), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, +			      0); + +	g_object_class_install_property (object_class, +					 PROP_TAB, +					 g_param_spec_object ("tab", +							      "Tab", +							      "The GeditTab", +							      GEDIT_TYPE_TAB, +							      G_PARAM_READWRITE | +							      G_PARAM_CONSTRUCT_ONLY)); + +	g_type_class_add_private (object_class, sizeof(GeditTabLabelPrivate)); +} + +static void +gedit_tab_label_init (GeditTabLabel *tab_label) +{ +	GtkWidget *ebox; +	GtkWidget *hbox; +	GtkWidget *close_button; +	GtkWidget *spinner; +	GtkWidget *icon; +	GtkWidget *label; +	GtkWidget *dummy_label; + +	tab_label->priv = GEDIT_TAB_LABEL_GET_PRIVATE (tab_label); + +	tab_label->priv->close_button_sensitive = TRUE; + +	ebox = gtk_event_box_new (); +	gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE); +	gtk_box_pack_start (GTK_BOX (tab_label), ebox, TRUE, TRUE, 0); +	tab_label->priv->ebox = ebox; + +	hbox = gtk_hbox_new (FALSE, 4); +	gtk_container_add (GTK_CONTAINER (ebox), hbox); + +	close_button = gedit_close_button_new (); +	gtk_widget_set_tooltip_text (close_button, _("Close document")); +	gtk_box_pack_start (GTK_BOX (tab_label), close_button, FALSE, FALSE, 0); +	tab_label->priv->close_button = close_button; + +	g_signal_connect (close_button, +			  "clicked", +			  G_CALLBACK (close_button_clicked_cb), +			  tab_label); + +#ifdef BUILD_SPINNER +	spinner = gedit_spinner_new (); +	gedit_spinner_set_size (GEDIT_SPINNER (spinner), GTK_ICON_SIZE_MENU); +#else +	spinner = gtk_spinner_new (); +#endif +	gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); +	tab_label->priv->spinner = spinner; + +	/* setup icon, empty by default */ +	icon = gtk_image_new (); +	gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); +	tab_label->priv->icon = icon; + +	label = gtk_label_new (""); +	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); +	gtk_misc_set_padding (GTK_MISC (label), 0, 0); +	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); +	tab_label->priv->label = label; + +	dummy_label = gtk_label_new (""); +	gtk_box_pack_start (GTK_BOX (hbox), dummy_label, TRUE, TRUE, 0); + +	gtk_widget_show (ebox); +	gtk_widget_show (hbox); +	gtk_widget_show (close_button); +	gtk_widget_show (icon); +	gtk_widget_show (label); +	gtk_widget_show (dummy_label); +} + +void +gedit_tab_label_set_close_button_sensitive (GeditTabLabel *tab_label, +					    gboolean       sensitive) +{ +	GeditTabState state; + +	g_return_if_fail (GEDIT_IS_TAB_LABEL (tab_label)); + +	sensitive = (sensitive != FALSE); + +	if (sensitive == tab_label->priv->close_button_sensitive) +		return; + +	tab_label->priv->close_button_sensitive = sensitive; + +	state = gedit_tab_get_state (tab_label->priv->tab); + +	gtk_widget_set_sensitive (tab_label->priv->close_button,  +				  tab_label->priv->close_button_sensitive && +				  (state != GEDIT_TAB_STATE_CLOSING) && +				  (state != GEDIT_TAB_STATE_SAVING)  && +				  (state != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) && +				  (state != GEDIT_TAB_STATE_PRINTING) && +				  (state != GEDIT_TAB_STATE_PRINT_PREVIEWING) && +				  (state != GEDIT_TAB_STATE_SAVING_ERROR)); +} + +GeditTab * +gedit_tab_label_get_tab (GeditTabLabel *tab_label) +{ +	g_return_val_if_fail (GEDIT_IS_TAB_LABEL (tab_label), NULL); + +	return tab_label->priv->tab; +} + +GtkWidget * +gedit_tab_label_new (GeditTab *tab) +{ +	GeditTabLabel *tab_label; + +	tab_label = g_object_new (GEDIT_TYPE_TAB_LABEL, +				  "homogeneous", FALSE, +				  "tab", tab, +				  NULL); + +	return GTK_WIDGET (tab_label); +} diff --git a/gedit/gedit-tab-label.h b/gedit/gedit-tab-label.h new file mode 100755 index 00000000..4b0360ad --- /dev/null +++ b/gedit/gedit-tab-label.h @@ -0,0 +1,66 @@ +/* + * gedit-tab-label.h + * This file is part of gedit + * + * Copyright (C) 2010 - Paolo Borelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GEDIT_TAB_LABEL_H__ +#define __GEDIT_TAB_LABEL_H__ + +#include <gtk/gtk.h> +#include <gedit/gedit-tab.h> + +G_BEGIN_DECLS + +#define GEDIT_TYPE_TAB_LABEL		(gedit_tab_label_get_type ()) +#define GEDIT_TAB_LABEL(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_TAB_LABEL, GeditTabLabel)) +#define GEDIT_TAB_LABEL_CONST(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_TAB_LABEL, GeditTabLabel const)) +#define GEDIT_TAB_LABEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_TAB_LABEL, GeditTabLabelClass)) +#define GEDIT_IS_TAB_LABEL(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_TAB_LABEL)) +#define GEDIT_IS_TAB_LABEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_TAB_LABEL)) +#define GEDIT_TAB_LABEL_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_TAB_LABEL, GeditTabLabelClass)) + +typedef struct _GeditTabLabel		GeditTabLabel; +typedef struct _GeditTabLabelClass	GeditTabLabelClass; +typedef struct _GeditTabLabelPrivate	GeditTabLabelPrivate; + +struct _GeditTabLabel { +	GtkHBox parent; +	 +	GeditTabLabelPrivate *priv; +}; + +struct _GeditTabLabelClass { +	GtkHBoxClass parent_class; + +	void (* close_clicked)  (GeditTabLabel *tab_label); +}; + +GType		 gedit_tab_label_get_type (void) G_GNUC_CONST; + +GtkWidget 	*gedit_tab_label_new (GeditTab *tab); + +GeditTab	*gedit_tab_label_get_tab (GeditTabLabel *tab_label); + +void		gedit_tab_label_set_close_button_sensitive (GeditTabLabel *tab_label, +							    gboolean       sensitive); + +G_END_DECLS + +#endif /* __GEDIT_TAB_LABEL_H__ */ diff --git a/gedit/gedit-tab.c b/gedit/gedit-tab.c new file mode 100755 index 00000000..9f26c692 --- /dev/null +++ b/gedit/gedit-tab.c @@ -0,0 +1,2832 @@ +/* + * gedit-tab.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gedit-app.h" +#include "gedit-notebook.h" +#include "gedit-tab.h" +#include "gedit-utils.h" +#include "gedit-io-error-message-area.h" +#include "gedit-print-job.h" +#include "gedit-print-preview.h" +#include "gedit-progress-message-area.h" +#include "gedit-debug.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-enum-types.h" + +#if !GTK_CHECK_VERSION (2, 17, 1) +#include "gedit-message-area.h" +#endif + +#define GEDIT_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_TAB, GeditTabPrivate)) + +#define GEDIT_TAB_KEY "GEDIT_TAB_KEY" + +struct _GeditTabPrivate +{ +	GeditTabState	        state; +	 +	GtkWidget	       *view; +	GtkWidget	       *view_scrolled_window; + +	GtkWidget	       *message_area; +	GtkWidget	       *print_preview; + +	GeditPrintJob          *print_job; + +	/* tmp data for saving */ +	gchar		       *tmp_save_uri; + +	/* tmp data for loading */ +	gint                    tmp_line_pos; +	const GeditEncoding    *tmp_encoding; +	 +	GTimer 		       *timer; +	guint		        times_called; + +	GeditDocumentSaveFlags	save_flags; + +        gint                    auto_save_interval; +        guint                   auto_save_timeout; +         +	gint	                not_editable : 1; +	gint                    auto_save : 1; + +	gint                    ask_if_externally_modified : 1; +}; + +G_DEFINE_TYPE(GeditTab, gedit_tab, GTK_TYPE_VBOX) + +enum +{ +	PROP_0, +	PROP_NAME, +	PROP_STATE, +	PROP_AUTO_SAVE, +	PROP_AUTO_SAVE_INTERVAL +}; + +static gboolean gedit_tab_auto_save (GeditTab *tab); + +static void +install_auto_save_timeout (GeditTab *tab) +{ +	gint timeout; + +	gedit_debug (DEBUG_TAB); + +	g_return_if_fail (tab->priv->auto_save_timeout <= 0); +	g_return_if_fail (tab->priv->auto_save); +	g_return_if_fail (tab->priv->auto_save_interval > 0); +	 +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_LOADING); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_SAVING); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_REVERTING); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_LOADING_ERROR); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_SAVING_ERROR); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_SAVING_ERROR); +	g_return_if_fail (tab->priv->state != GEDIT_TAB_STATE_REVERTING_ERROR); + +	/* Add a new timeout */ +	timeout = g_timeout_add_seconds (tab->priv->auto_save_interval * 60, +					 (GSourceFunc) gedit_tab_auto_save, +					 tab); + +	tab->priv->auto_save_timeout = timeout; +} + +static gboolean +install_auto_save_timeout_if_needed (GeditTab *tab) +{ +	GeditDocument *doc; + +	gedit_debug (DEBUG_TAB); +	 +	g_return_val_if_fail (tab->priv->auto_save_timeout <= 0, FALSE); +	g_return_val_if_fail ((tab->priv->state == GEDIT_TAB_STATE_NORMAL) || +			      (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) || +			      (tab->priv->state == GEDIT_TAB_STATE_CLOSING), FALSE); + +	if (tab->priv->state == GEDIT_TAB_STATE_CLOSING) +		return FALSE; + +	doc = gedit_tab_get_document (tab); + + 	if (tab->priv->auto_save &&  + 	    !gedit_document_is_untitled (doc) && + 	    !gedit_document_get_readonly (doc)) + 	{  + 		install_auto_save_timeout (tab); + 		 + 		return TRUE; + 	} + 	 + 	return FALSE; +} + +static void +remove_auto_save_timeout (GeditTab *tab) +{ +	gedit_debug (DEBUG_TAB); + +	/* FIXME: check sugli stati */ +	 +	g_return_if_fail (tab->priv->auto_save_timeout > 0); +	 +	g_source_remove (tab->priv->auto_save_timeout); +	tab->priv->auto_save_timeout = 0; +} + +static void +gedit_tab_get_property (GObject    *object, +		        guint       prop_id, +		        GValue     *value, +		        GParamSpec *pspec) +{ +	GeditTab *tab = GEDIT_TAB (object); + +	switch (prop_id) +	{ +		case PROP_NAME: +			g_value_take_string (value, +					     _gedit_tab_get_name (tab)); +			break; +		case PROP_STATE: +			g_value_set_enum (value, +					  gedit_tab_get_state (tab)); +			break;			 +		case PROP_AUTO_SAVE: +			g_value_set_boolean (value, +					     gedit_tab_get_auto_save_enabled (tab)); +			break; +		case PROP_AUTO_SAVE_INTERVAL: +			g_value_set_int (value, +					 gedit_tab_get_auto_save_interval (tab)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break;			 +	} +} + +static void +gedit_tab_set_property (GObject      *object, +		        guint         prop_id, +		        const GValue *value, +		        GParamSpec   *pspec) +{ +	GeditTab *tab = GEDIT_TAB (object); + +	switch (prop_id) +	{ +		case PROP_AUTO_SAVE: +			gedit_tab_set_auto_save_enabled (tab, +							 g_value_get_boolean (value)); +			break; +		case PROP_AUTO_SAVE_INTERVAL: +			gedit_tab_set_auto_save_interval (tab, +							  g_value_get_int (value)); +			break; +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break;			 +	} +} + +static void +gedit_tab_finalize (GObject *object) +{ +	GeditTab *tab = GEDIT_TAB (object); + +	if (tab->priv->timer != NULL) +		g_timer_destroy (tab->priv->timer); + +	g_free (tab->priv->tmp_save_uri); + +	if (tab->priv->auto_save_timeout > 0) +		remove_auto_save_timeout (tab); + +	G_OBJECT_CLASS (gedit_tab_parent_class)->finalize (object); +} + +static void  +gedit_tab_class_init (GeditTabClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); + +	object_class->finalize = gedit_tab_finalize; +	object_class->get_property = gedit_tab_get_property; +	object_class->set_property = gedit_tab_set_property; +	 +	g_object_class_install_property (object_class, +					 PROP_NAME, +					 g_param_spec_string ("name", +							      "Name", +							      "The tab's name", +							      NULL, +							      G_PARAM_READABLE | +							      G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_STATE, +					 g_param_spec_enum ("state", +							    "State", +							    "The tab's state", +							    GEDIT_TYPE_TAB_STATE, +							    GEDIT_TAB_STATE_NORMAL, +							    G_PARAM_READABLE | +							    G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_AUTO_SAVE, +					 g_param_spec_boolean ("autosave", +							       "Autosave", +							       "Autosave feature", +							       TRUE, +							       G_PARAM_READWRITE | +							       G_PARAM_STATIC_STRINGS)); + +	g_object_class_install_property (object_class, +					 PROP_AUTO_SAVE_INTERVAL, +					 g_param_spec_int ("autosave-interval", +							   "AutosaveInterval", +							   "Time between two autosaves", +							   0, +							   G_MAXINT, +							   0, +							   G_PARAM_READWRITE | +							   G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof (GeditTabPrivate)); +} + +/** + * gedit_tab_get_state: + * @tab: a #GeditTab + * + * Gets the #GeditTabState of @tab. + * + * Returns: the #GeditTabState of @tab + */ +GeditTabState +gedit_tab_get_state (GeditTab *tab) +{ +	g_return_val_if_fail (GEDIT_IS_TAB (tab), GEDIT_TAB_STATE_NORMAL); +	 +	return tab->priv->state; +} + +static void +set_cursor_according_to_state (GtkTextView   *view, +			       GeditTabState  state) +{ +	GdkCursor *cursor; +	GdkWindow *text_window; +	GdkWindow *left_window; + +	text_window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_TEXT); +	left_window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_LEFT); + +	if ((state == GEDIT_TAB_STATE_LOADING)          || +	    (state == GEDIT_TAB_STATE_REVERTING)        || +	    (state == GEDIT_TAB_STATE_SAVING)           || +	    (state == GEDIT_TAB_STATE_PRINTING)         || +	    (state == GEDIT_TAB_STATE_PRINT_PREVIEWING) || +	    (state == GEDIT_TAB_STATE_CLOSING)) +	{ +		cursor = gdk_cursor_new_for_display ( +				gtk_widget_get_display (GTK_WIDGET (view)), +				GDK_WATCH); + +		if (text_window != NULL) +			gdk_window_set_cursor (text_window, cursor); +		if (left_window != NULL) +			gdk_window_set_cursor (left_window, cursor); + +		gdk_cursor_unref (cursor); +	} +	else +	{ +		cursor = gdk_cursor_new_for_display ( +				gtk_widget_get_display (GTK_WIDGET (view)), +				GDK_XTERM); + +		if (text_window != NULL) +			gdk_window_set_cursor (text_window, cursor); +		if (left_window != NULL) +			gdk_window_set_cursor (left_window, NULL); + +		gdk_cursor_unref (cursor); +	} +} + +static void +view_realized (GtkTextView *view, +	       GeditTab    *tab) +{ +	set_cursor_according_to_state (view, tab->priv->state); +} + +static void +set_view_properties_according_to_state (GeditTab      *tab, +					GeditTabState  state) +{ +	gboolean val; + +	val = ((state == GEDIT_TAB_STATE_NORMAL) && +	       (tab->priv->print_preview == NULL) && +	       !tab->priv->not_editable); +	gtk_text_view_set_editable (GTK_TEXT_VIEW (tab->priv->view), val); + +	val = ((state != GEDIT_TAB_STATE_LOADING) && +	       (state != GEDIT_TAB_STATE_CLOSING)); +	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (tab->priv->view), val); + +	val = ((state != GEDIT_TAB_STATE_LOADING) && +	       (state != GEDIT_TAB_STATE_CLOSING) && +	       (gedit_prefs_manager_get_highlight_current_line ())); +	gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (tab->priv->view), val); +} + +static void +gedit_tab_set_state (GeditTab      *tab, +		     GeditTabState  state) +{ +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail ((state >= 0) && (state < GEDIT_TAB_NUM_OF_STATES)); + +	if (tab->priv->state == state) +		return; + +	tab->priv->state = state; + +	set_view_properties_according_to_state (tab, state); + +	if ((state == GEDIT_TAB_STATE_LOADING_ERROR) || /* FIXME: add other states if needed */ +	    (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) +	{ +		gtk_widget_hide (tab->priv->view_scrolled_window); +	} +	else +	{ +		if (tab->priv->print_preview == NULL) +			gtk_widget_show (tab->priv->view_scrolled_window); +	} + +	set_cursor_according_to_state (GTK_TEXT_VIEW (tab->priv->view), +				       state); + +	g_object_notify (G_OBJECT (tab), "state");		 +} + +static void  +document_uri_notify_handler (GeditDocument *document, +			     GParamSpec    *pspec, +			     GeditTab      *tab) +{ +	gedit_debug (DEBUG_TAB); +	 +	/* Notify the change in the URI */ +	g_object_notify (G_OBJECT (tab), "name"); +} + +static void  +document_shortname_notify_handler (GeditDocument *document, +				   GParamSpec    *pspec, +				   GeditTab      *tab) +{ +	gedit_debug (DEBUG_TAB); +	 +	/* Notify the change in the shortname */ +	g_object_notify (G_OBJECT (tab), "name"); +} + +static void +document_modified_changed (GtkTextBuffer *document, +			   GeditTab      *tab) +{ +	g_object_notify (G_OBJECT (tab), "name"); +} + +static void +set_message_area (GeditTab  *tab, +		  GtkWidget *message_area) +{ +	if (tab->priv->message_area == message_area) +		return; + +	if (tab->priv->message_area != NULL) +		gtk_widget_destroy (tab->priv->message_area); + +	tab->priv->message_area = message_area; + +	if (message_area == NULL) +		return; + +	gtk_box_pack_start (GTK_BOX (tab), +			    tab->priv->message_area, +			    FALSE, +			    FALSE, +			    0);		 + +	g_object_add_weak_pointer (G_OBJECT (tab->priv->message_area),  +				   (gpointer *)&tab->priv->message_area); +} + +static void +remove_tab (GeditTab *tab) +{ +	GeditNotebook *notebook; + +	notebook = GEDIT_NOTEBOOK (gtk_widget_get_parent (GTK_WIDGET (tab))); + +	gedit_notebook_remove_tab (notebook, tab); +} + +static void  +io_loading_error_message_area_response (GtkWidget        *message_area, +					gint              response_id, +					GeditTab         *tab) +{ +	GeditDocument *doc; +	GeditView *view; +	gchar *uri; +	const GeditEncoding *encoding; + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	view = gedit_tab_get_view (tab); +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	uri = gedit_document_get_uri (doc); +	g_return_if_fail (uri != NULL); + +	switch (response_id) +	{ +		case GTK_RESPONSE_OK: +			encoding = gedit_conversion_error_message_area_get_encoding ( +					GTK_WIDGET (message_area)); + +			if (encoding != NULL) +			{ +				tab->priv->tmp_encoding = encoding; +			} + +			set_message_area (tab, NULL); +			gedit_tab_set_state (tab, GEDIT_TAB_STATE_LOADING); + +			g_return_if_fail (tab->priv->auto_save_timeout <= 0); + +			gedit_document_load (doc, +					     uri, +					     tab->priv->tmp_encoding, +					     tab->priv->tmp_line_pos, +					     FALSE); +			break; +		case GTK_RESPONSE_YES: +			/* This means that we want to edit the document anyway */ +			set_message_area (tab, NULL); +			_gedit_document_set_readonly (doc, FALSE); +			break; +		case GTK_RESPONSE_NO: +			/* We don't want to edit the document just show it */ +			set_message_area (tab, NULL); +			break; +		default: +			_gedit_recent_remove (GEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), uri); + +			remove_tab (tab); +			break; +	} + +	g_free (uri); +} + +static void  +file_already_open_warning_message_area_response (GtkWidget   *message_area, +						 gint         response_id, +						 GeditTab    *tab) +{ +	GeditView *view; +	 +	view = gedit_tab_get_view (tab); +	 +	if (response_id == GTK_RESPONSE_YES) +	{ +		tab->priv->not_editable = FALSE; +		 +		gtk_text_view_set_editable (GTK_TEXT_VIEW (view), +					    TRUE); +	} + +	gtk_widget_destroy (message_area); + +	gtk_widget_grab_focus (GTK_WIDGET (view));	 +} + +static void +load_cancelled (GtkWidget        *area, +                gint              response_id, +                GeditTab         *tab) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area)); +	 +	g_object_ref (tab); +	gedit_document_load_cancel (gedit_tab_get_document (tab)); +	g_object_unref (tab);	 +} + +static void  +unrecoverable_reverting_error_message_area_response (GtkWidget        *message_area, +						     gint              response_id, +						     GeditTab         *tab) +{ +	GeditView *view; +	 +	gedit_tab_set_state (tab, +			     GEDIT_TAB_STATE_NORMAL); + +	set_message_area (tab, NULL); + +	view = gedit_tab_get_view (tab); + +	gtk_widget_grab_focus (GTK_WIDGET (view)); +	 +	install_auto_save_timeout_if_needed (tab);	 +} + +#define MAX_MSG_LENGTH 100 + +static void +show_loading_message_area (GeditTab *tab) +{ +	GtkWidget *area; +	GeditDocument *doc = NULL; +	gchar *name; +	gchar *dirname = NULL; +	gchar *msg = NULL; +	gchar *name_markup; +	gchar *dirname_markup; +	gint len; + +	if (tab->priv->message_area != NULL) +		return; + +	gedit_debug (DEBUG_TAB); +		 +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (doc != NULL); + +	name = gedit_document_get_short_name_for_display (doc); +	len = g_utf8_strlen (name, -1); + +	/* if the name is awfully long, truncate it and be done with it, +	 * otherwise also show the directory (ellipsized if needed) +	 */ +	if (len > MAX_MSG_LENGTH) +	{ +		gchar *str; + +		str = gedit_utils_str_middle_truncate (name, MAX_MSG_LENGTH); +		g_free (name); +		name = str; +	} +	else +	{ +		GFile *file; + +		file = gedit_document_get_location (doc); +		if (file != NULL) +		{ +			gchar *str; + +			str = gedit_utils_location_get_dirname_for_display (file); +			g_object_unref (file); + +			/* use the remaining space for the dir, but use a min of 20 chars +			 * so that we do not end up with a dirname like "(a...b)". +			 * This means that in the worst case when the filename is long 99 +			 * we have a title long 99 + 20, but I think it's a rare enough +			 * case to be acceptable. It's justa darn title afterall :) +			 */ +			dirname = gedit_utils_str_middle_truncate (str,  +								   MAX (20, MAX_MSG_LENGTH - len)); +			g_free (str); +		} +	} + +	name_markup = g_markup_printf_escaped ("<b>%s</b>", name); + +	if (tab->priv->state == GEDIT_TAB_STATE_REVERTING) +	{ +		if (dirname != NULL) +		{ +			dirname_markup = g_markup_printf_escaped ("<b>%s</b>", dirname); + +			/* Translators: the first %s is a file name (e.g. test.txt) the second one +			   is a directory (e.g. ssh://master.mate.org/home/users/paolo) */ +			msg = g_strdup_printf (_("Reverting %s from %s"), +					       name_markup, +					       dirname_markup); +			g_free (dirname_markup); +		} +		else +		{ +			msg = g_strdup_printf (_("Reverting %s"),  +					       name_markup); +		} +		 +		area = gedit_progress_message_area_new (GTK_STOCK_REVERT_TO_SAVED, +							msg, +							TRUE); +	} +	else +	{ +		if (dirname != NULL) +		{ +			dirname_markup = g_markup_printf_escaped ("<b>%s</b>", dirname); + +			/* Translators: the first %s is a file name (e.g. test.txt) the second one +			   is a directory (e.g. ssh://master.mate.org/home/users/paolo) */ +			msg = g_strdup_printf (_("Loading %s from %s"), +					       name_markup, +					       dirname_markup); +			g_free (dirname_markup); +		} +		else +		{ +			msg = g_strdup_printf (_("Loading %s"),  +					       name_markup); +		} + +		area = gedit_progress_message_area_new (GTK_STOCK_OPEN, +							msg, +							TRUE); +	} + +	g_signal_connect (area, +			  "response", +			  G_CALLBACK (load_cancelled), +			  tab); +						  +	gtk_widget_show (area); + +	set_message_area (tab, area); + +	g_free (msg); +	g_free (name); +	g_free (name_markup); +	g_free (dirname); +} + +static void +show_saving_message_area (GeditTab *tab) +{ +	GtkWidget *area; +	GeditDocument *doc = NULL; +	gchar *short_name; +	gchar *from; +	gchar *to = NULL; +	gchar *from_markup; +	gchar *to_markup; +	gchar *msg = NULL; +	gint len; + +	g_return_if_fail (tab->priv->tmp_save_uri != NULL); +	 +	if (tab->priv->message_area != NULL) +		return; +	 +	gedit_debug (DEBUG_TAB); +		 +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (doc != NULL); + +	short_name = gedit_document_get_short_name_for_display (doc); + +	len = g_utf8_strlen (short_name, -1); + +	/* if the name is awfully long, truncate it and be done with it, +	 * otherwise also show the directory (ellipsized if needed) +	 */ +	if (len > MAX_MSG_LENGTH) +	{ +		from = gedit_utils_str_middle_truncate (short_name,  +							MAX_MSG_LENGTH); +		g_free (short_name); +	} +	else +	{ +		gchar *str; + +		from = short_name; + +		to = gedit_utils_uri_for_display (tab->priv->tmp_save_uri); + +		str = gedit_utils_str_middle_truncate (to,  +						       MAX (20, MAX_MSG_LENGTH - len)); +		g_free (to); +			 +		to = str; +	} + +	from_markup = g_markup_printf_escaped ("<b>%s</b>", from); + +	if (to != NULL) +	{ +		to_markup = g_markup_printf_escaped ("<b>%s</b>", to); + +		/* Translators: the first %s is a file name (e.g. test.txt) the second one +		   is a directory (e.g. ssh://master.mate.org/home/users/paolo) */ +		msg = g_strdup_printf (_("Saving %s to %s"), +				       from_markup, +				       to_markup); +		g_free (to_markup); +	} +	else +	{ +		msg = g_strdup_printf (_("Saving %s"), from_markup); +	} + +	area = gedit_progress_message_area_new (GTK_STOCK_SAVE, +						msg, +						FALSE); + +	gtk_widget_show (area); + +	set_message_area (tab, area); + +	g_free (msg); +	g_free (to); +	g_free (from); +	g_free (from_markup); +} + +static void +message_area_set_progress (GeditTab *tab, +			   goffset   size, +			   goffset   total_size) +{ +	if (tab->priv->message_area == NULL) +		return; + +	gedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size); + +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area)); +	 +	if (total_size == 0) +	{ +		if (size != 0) +			gedit_progress_message_area_pulse ( +					GEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area));	 +		else +			gedit_progress_message_area_set_fraction ( +				GEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area), +				0); +	} +	else +	{ +		gdouble frac; + +		frac = (gdouble)size / (gdouble)total_size; + +		gedit_progress_message_area_set_fraction ( +				GEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area), +				frac);		 +	} +} + +static void +document_loading (GeditDocument *document, +		  goffset        size, +		  goffset        total_size, +		  GeditTab      *tab) +{ +	gdouble et; +	gdouble total_time; + +	g_return_if_fail ((tab->priv->state == GEDIT_TAB_STATE_LOADING) || +		 	  (tab->priv->state == GEDIT_TAB_STATE_REVERTING)); + +	gedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size); + +	if (tab->priv->timer == NULL) +	{ +		g_return_if_fail (tab->priv->times_called == 0); +		tab->priv->timer = g_timer_new (); +	} + +	et = g_timer_elapsed (tab->priv->timer, NULL); + +	/* et : total_time = size : total_size */ +	total_time = (et * total_size) / size; + +	if ((total_time - et) > 3.0) +	{ +		show_loading_message_area (tab); +	} + +	message_area_set_progress (tab, size, total_size); +} + +static gboolean +remove_tab_idle (GeditTab *tab) +{ +	remove_tab (tab); + +	return FALSE; +} + +static void +document_loaded (GeditDocument *document, +		 const GError  *error, +		 GeditTab      *tab) +{ +	GtkWidget *emsg; +	GFile *location; +	gchar *uri; +	const GeditEncoding *encoding; + +	g_return_if_fail ((tab->priv->state == GEDIT_TAB_STATE_LOADING) || +			  (tab->priv->state == GEDIT_TAB_STATE_REVERTING)); +	g_return_if_fail (tab->priv->auto_save_timeout <= 0); + +	if (tab->priv->timer != NULL) +	{ +		g_timer_destroy (tab->priv->timer); +		tab->priv->timer = NULL; +	} +	tab->priv->times_called = 0; + +	set_message_area (tab, NULL); + +	location = gedit_document_get_location (document); +	uri = gedit_document_get_uri (document); + +	/* if the error is CONVERSION FALLBACK don't treat it as a normal error */ +	if (error != NULL && +	    (error->domain != GEDIT_DOCUMENT_ERROR || error->code != GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK)) +	{ +		if (tab->priv->state == GEDIT_TAB_STATE_LOADING) +			gedit_tab_set_state (tab, GEDIT_TAB_STATE_LOADING_ERROR); +		else +			gedit_tab_set_state (tab, GEDIT_TAB_STATE_REVERTING_ERROR); + +		encoding = gedit_document_get_encoding (document); + +		if (error->domain == G_IO_ERROR && +		    error->code == G_IO_ERROR_CANCELLED) +		{ +			/* remove the tab, but in an idle handler, since +			 * we are in the handler of doc loaded and we  +			 * don't want doc and tab to be finalized now. +			 */ +			g_idle_add ((GSourceFunc) remove_tab_idle, tab); + +			goto end; +		} +		else +		{ +			_gedit_recent_remove (GEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), uri); + +			if (tab->priv->state == GEDIT_TAB_STATE_LOADING_ERROR) +			{ +				emsg = gedit_io_loading_error_message_area_new (uri, +										tab->priv->tmp_encoding, +										error); +				g_signal_connect (emsg, +						  "response", +						  G_CALLBACK (io_loading_error_message_area_response), +						  tab); +			} +			else +			{ +				g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_REVERTING_ERROR); +				 +				emsg = gedit_unrecoverable_reverting_error_message_area_new (uri, +											     error); + +				g_signal_connect (emsg, +						  "response", +						  G_CALLBACK (unrecoverable_reverting_error_message_area_response), +						  tab); +			} + +			set_message_area (tab, emsg); +		} + +#if !GTK_CHECK_VERSION (2, 17, 1) +		gedit_message_area_set_default_response (GEDIT_MESSAGE_AREA (emsg), +							 GTK_RESPONSE_CANCEL); +#else +		gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg), +						   GTK_RESPONSE_CANCEL); +#endif + +		gtk_widget_show (emsg); + +		g_object_unref (location); +		g_free (uri); + +		return; +	} +	else +	{ +		gchar *mime; +		GList *all_documents; +		GList *l; + +		g_return_if_fail (uri != NULL); + +		mime = gedit_document_get_mime_type (document); +		_gedit_recent_add (GEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), +				   uri, +				   mime); +		g_free (mime); + +		if (error && +		    error->domain == GEDIT_DOCUMENT_ERROR && +		    error->code == GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK) +		{ +			GtkWidget *emsg; + +			_gedit_document_set_readonly (document, TRUE); + +			emsg = gedit_io_loading_error_message_area_new (uri, +									tab->priv->tmp_encoding, +									error); + +			set_message_area (tab, emsg); + +			g_signal_connect (emsg, +					  "response", +					  G_CALLBACK (io_loading_error_message_area_response), +					  tab); + +#if !GTK_CHECK_VERSION (2, 17, 1) +			gedit_message_area_set_default_response (GEDIT_MESSAGE_AREA (emsg), +								 GTK_RESPONSE_CANCEL); +#else +			gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg), +							   GTK_RESPONSE_CANCEL); +#endif + +			gtk_widget_show (emsg); +		} + +		/* Scroll to the cursor when the document is loaded */ +		gedit_view_scroll_to_cursor (GEDIT_VIEW (tab->priv->view)); + +		all_documents = gedit_app_get_documents (gedit_app_get_default ()); + +		for (l = all_documents; l != NULL; l = g_list_next (l)) +		{ +			GeditDocument *d = GEDIT_DOCUMENT (l->data); +			 +			if (d != document) +			{ +				GFile *loc; + +				loc = gedit_document_get_location (d); + +				if ((loc != NULL) && +			    	    g_file_equal (location, loc)) +			    	{ +			    		GtkWidget *w; +			    		GeditView *view; + +			    		view = gedit_tab_get_view (tab); + +			    		tab->priv->not_editable = TRUE; + +			    		w = gedit_file_already_open_warning_message_area_new (uri); + +					set_message_area (tab, w); + +#if !GTK_CHECK_VERSION (2, 17, 1) +					gedit_message_area_set_default_response (GEDIT_MESSAGE_AREA (w), +										 GTK_RESPONSE_CANCEL); +#else +					gtk_info_bar_set_default_response (GTK_INFO_BAR (w), +									   GTK_RESPONSE_CANCEL); +#endif + +					gtk_widget_show (w); + +					g_signal_connect (w, +							  "response", +							  G_CALLBACK (file_already_open_warning_message_area_response), +							  tab); + +			    		g_object_unref (loc); +			    		break; +			    	} +			    	 +			    	if (loc != NULL) +					g_object_unref (loc); +			} +		} + +		g_list_free (all_documents); + +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); +		 +		install_auto_save_timeout_if_needed (tab); + +		tab->priv->ask_if_externally_modified = TRUE; +	} + + end: +	g_object_unref (location); +	g_free (uri); + +	tab->priv->tmp_line_pos = 0; +	tab->priv->tmp_encoding = NULL; +} + +static void +document_saving (GeditDocument    *document, +		 goffset  size, +		 goffset  total_size, +		 GeditTab         *tab) +{ +	gdouble et; +	gdouble total_time; + +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_SAVING); + +	gedit_debug_message (DEBUG_TAB, "%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, size, total_size); + + +	if (tab->priv->timer == NULL) +	{ +		g_return_if_fail (tab->priv->times_called == 0); +		tab->priv->timer = g_timer_new (); +	} + +	et = g_timer_elapsed (tab->priv->timer, NULL); + +	/* et : total_time = size : total_size */ +	total_time = (et * total_size)/size; + +	if ((total_time - et) > 3.0) +	{ +		show_saving_message_area (tab); +	} + +	message_area_set_progress (tab, size, total_size); + +	tab->priv->times_called++; +} + +static void +end_saving (GeditTab *tab) +{ +	/* Reset tmp data for saving */ +	g_free (tab->priv->tmp_save_uri); +	tab->priv->tmp_save_uri = NULL; +	tab->priv->tmp_encoding = NULL; +	 +	install_auto_save_timeout_if_needed (tab); +} + +static void  +unrecoverable_saving_error_message_area_response (GtkWidget        *message_area, +						  gint              response_id, +						  GeditTab         *tab) +{ +	GeditView *view; +	 +	if (tab->priv->print_preview != NULL) +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW); +	else +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + +	end_saving (tab); +	 +	set_message_area (tab, NULL); + +	view = gedit_tab_get_view (tab); + +	gtk_widget_grab_focus (GTK_WIDGET (view));	 +} + +static void  +no_backup_error_message_area_response (GtkWidget        *message_area, +				       gint              response_id, +				       GeditTab         *tab) +{ +	if (response_id == GTK_RESPONSE_YES) +	{ +		GeditDocument *doc; + +		doc = gedit_tab_get_document (tab); +		g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +		set_message_area (tab, NULL); + +		g_return_if_fail (tab->priv->tmp_save_uri != NULL); +		g_return_if_fail (tab->priv->tmp_encoding != NULL); + +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); + +		/* don't bug the user again with this... */ +		tab->priv->save_flags |= GEDIT_DOCUMENT_SAVE_IGNORE_BACKUP; + +		g_return_if_fail (tab->priv->auto_save_timeout <= 0); +		 +		/* Force saving */ +		gedit_document_save (doc, tab->priv->save_flags); +	} +	else +	{ +		unrecoverable_saving_error_message_area_response (message_area, +								  response_id, +								  tab); +	} +} + +static void +externally_modified_error_message_area_response (GtkWidget        *message_area, +						 gint              response_id, +						 GeditTab         *tab) +{ +	if (response_id == GTK_RESPONSE_YES) +	{ +		GeditDocument *doc; + +		doc = gedit_tab_get_document (tab); +		g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +		 +		set_message_area (tab, NULL); + +		g_return_if_fail (tab->priv->tmp_save_uri != NULL); +		g_return_if_fail (tab->priv->tmp_encoding != NULL); + +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); + +		g_return_if_fail (tab->priv->auto_save_timeout <= 0); +		 +		/* ignore mtime should not be persisted in save flags across saves */ + +		/* Force saving */ +		gedit_document_save (doc, tab->priv->save_flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME); +	} +	else +	{		 +		unrecoverable_saving_error_message_area_response (message_area, +								  response_id, +								  tab); +	} +} + +static void  +recoverable_saving_error_message_area_response (GtkWidget        *message_area, +						gint              response_id, +						GeditTab         *tab) +{ +	GeditDocument *doc; + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	 +	if (response_id == GTK_RESPONSE_OK) +	{ +		const GeditEncoding *encoding; +		 +		encoding = gedit_conversion_error_message_area_get_encoding ( +									GTK_WIDGET (message_area)); + +		g_return_if_fail (encoding != NULL); + +		set_message_area (tab, NULL); + +		g_return_if_fail (tab->priv->tmp_save_uri != NULL); +				 +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); +			 +		tab->priv->tmp_encoding = encoding; + +		gedit_debug_message (DEBUG_TAB, "Force saving with URI '%s'", tab->priv->tmp_save_uri); +			  +		g_return_if_fail (tab->priv->auto_save_timeout <= 0); +		 +		gedit_document_save_as (doc, +					tab->priv->tmp_save_uri, +					tab->priv->tmp_encoding, +					tab->priv->save_flags); +	} +	else +	{		 +		unrecoverable_saving_error_message_area_response (message_area, +								  response_id, +								  tab); +	} +} + +static void +document_saved (GeditDocument *document, +		const GError  *error, +		GeditTab      *tab) +{ +	GtkWidget *emsg; + +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_SAVING); + +	g_return_if_fail (tab->priv->tmp_save_uri != NULL); +	g_return_if_fail (tab->priv->tmp_encoding != NULL);	 +	g_return_if_fail (tab->priv->auto_save_timeout <= 0); +	 +	g_timer_destroy (tab->priv->timer); +	tab->priv->timer = NULL; +	tab->priv->times_called = 0; +	 +	set_message_area (tab, NULL); +	 +	if (error != NULL) +	{ +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING_ERROR); +		 +		if (error->domain == GEDIT_DOCUMENT_ERROR && +		    error->code == GEDIT_DOCUMENT_ERROR_EXTERNALLY_MODIFIED) +		{ +			/* This error is recoverable */ +			emsg = gedit_externally_modified_saving_error_message_area_new ( +							tab->priv->tmp_save_uri,  +							error); +			g_return_if_fail (emsg != NULL); + +			set_message_area (tab, emsg); + +			g_signal_connect (emsg, +					  "response", +					  G_CALLBACK (externally_modified_error_message_area_response), +					  tab); +		} +		else if ((error->domain == GEDIT_DOCUMENT_ERROR && +			  error->code == GEDIT_DOCUMENT_ERROR_CANT_CREATE_BACKUP) || +		         (error->domain == G_IO_ERROR && +			  error->code == G_IO_ERROR_CANT_CREATE_BACKUP)) +		{ +			/* This error is recoverable */ +			emsg = gedit_no_backup_saving_error_message_area_new ( +							tab->priv->tmp_save_uri,  +							error); +			g_return_if_fail (emsg != NULL); + +			set_message_area (tab, emsg); + +			g_signal_connect (emsg, +					  "response", +					  G_CALLBACK (no_backup_error_message_area_response), +					  tab); +		} +		else if (error->domain == GEDIT_DOCUMENT_ERROR || +			 (error->domain == G_IO_ERROR && +			  error->code != G_IO_ERROR_INVALID_DATA && +			  error->code != G_IO_ERROR_PARTIAL_INPUT)) +		{ +			/* These errors are _NOT_ recoverable */ +			_gedit_recent_remove  (GEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), +					       tab->priv->tmp_save_uri); + +			emsg = gedit_unrecoverable_saving_error_message_area_new (tab->priv->tmp_save_uri,  +								  error); +			g_return_if_fail (emsg != NULL); +	 +			set_message_area (tab, emsg); + +			g_signal_connect (emsg, +					  "response", +					  G_CALLBACK (unrecoverable_saving_error_message_area_response), +					  tab); +		} +		else +		{ +			/* This error is recoverable */ +			g_return_if_fail (error->domain == G_CONVERT_ERROR || +			                  error->domain == G_IO_ERROR); + +			emsg = gedit_conversion_error_while_saving_message_area_new ( +									tab->priv->tmp_save_uri, +									tab->priv->tmp_encoding, +									error); + +			set_message_area (tab, emsg); + +			g_signal_connect (emsg, +					  "response", +					  G_CALLBACK (recoverable_saving_error_message_area_response), +					  tab); +		} + +#if !GTK_CHECK_VERSION (2, 17, 1) +		gedit_message_area_set_default_response (GEDIT_MESSAGE_AREA (emsg), +							 GTK_RESPONSE_CANCEL); +#else +		gtk_info_bar_set_default_response (GTK_INFO_BAR (emsg), +						   GTK_RESPONSE_CANCEL); +#endif + +		gtk_widget_show (emsg); +	} +	else +	{ +		gchar *mime = gedit_document_get_mime_type (document); + +		_gedit_recent_add (GEDIT_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), +				   tab->priv->tmp_save_uri, +				   mime); +		g_free (mime); + +		if (tab->priv->print_preview != NULL) +			gedit_tab_set_state (tab, GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW); +		else +			gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + +		tab->priv->ask_if_externally_modified = TRUE; +			 +		end_saving (tab); +	} +} + +static void  +externally_modified_notification_message_area_response (GtkWidget        *message_area, +							gint              response_id, +							GeditTab         *tab) +{ +	GeditView *view; + +	set_message_area (tab, NULL); +	view = gedit_tab_get_view (tab); + +	if (response_id == GTK_RESPONSE_OK) +	{ +		_gedit_tab_revert (tab); +	} +	else +	{ +		tab->priv->ask_if_externally_modified = FALSE; + +		/* go back to normal state */ +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); +	} + +	gtk_widget_grab_focus (GTK_WIDGET (view)); +} + +static void +display_externally_modified_notification (GeditTab *tab) +{ +	GtkWidget *message_area; +	GeditDocument *doc; +	gchar *uri; +	gboolean document_modified; + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	/* uri cannot be NULL, we're here because +	 * the file we're editing changed on disk */ +	uri = gedit_document_get_uri (doc); +	g_return_if_fail (uri != NULL); + +	document_modified = gtk_text_buffer_get_modified (GTK_TEXT_BUFFER(doc)); +	message_area = gedit_externally_modified_message_area_new (uri, document_modified); +	g_free (uri); + +	tab->priv->message_area = NULL; +	set_message_area (tab, message_area); +	gtk_widget_show (message_area); + +	g_signal_connect (message_area, +			  "response", +			  G_CALLBACK (externally_modified_notification_message_area_response), +			  tab); +} + +static gboolean +view_focused_in (GtkWidget     *widget, +                 GdkEventFocus *event, +                 GeditTab      *tab) +{ +	GeditDocument *doc; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), FALSE); + +	/* we try to detect file changes only in the normal state */ +	if (tab->priv->state != GEDIT_TAB_STATE_NORMAL) +	{ +		return FALSE; +	} + +	/* we already asked, don't bug the user again */ +	if (!tab->priv->ask_if_externally_modified) +	{ +		return FALSE; +	} + +	doc = gedit_tab_get_document (tab); + +	/* If file was never saved or is remote we do not check */ +	if (!gedit_document_is_local (doc)) +	{ +		return FALSE; +	} + +	if (_gedit_document_check_externally_modified (doc)) +	{ +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION); + +		display_externally_modified_notification (tab); + +		return FALSE; +	} + +	return FALSE; +} + +static GMountOperation * +tab_mount_operation_factory (GeditDocument *doc, +			     gpointer userdata) +{ +	GeditTab *tab = GEDIT_TAB (userdata); +	GtkWidget *window; + +	window = gtk_widget_get_toplevel (GTK_WIDGET (tab)); +	return gtk_mount_operation_new (GTK_WINDOW (window)); +} + +static void +gedit_tab_init (GeditTab *tab) +{ +	GtkWidget *sw; +	GeditDocument *doc; +	GeditLockdownMask lockdown; + +	tab->priv = GEDIT_TAB_GET_PRIVATE (tab); + +	tab->priv->state = GEDIT_TAB_STATE_NORMAL; + +	tab->priv->not_editable = FALSE; + +	tab->priv->save_flags = 0; + +	tab->priv->ask_if_externally_modified = TRUE; +	 +	/* Create the scrolled window */ +	sw = gtk_scrolled_window_new (NULL, NULL); +	tab->priv->view_scrolled_window = sw; + +	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), +					GTK_POLICY_AUTOMATIC, +					GTK_POLICY_AUTOMATIC); + +	/* Manage auto save data */ +	lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); +	tab->priv->auto_save = gedit_prefs_manager_get_auto_save () && +			       !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK); +	tab->priv->auto_save = (tab->priv->auto_save != FALSE); + +	tab->priv->auto_save_interval = gedit_prefs_manager_get_auto_save_interval (); +	if (tab->priv->auto_save_interval <= 0) +		tab->priv->auto_save_interval = GPM_DEFAULT_AUTO_SAVE_INTERVAL; + +	/* Create the view */ +	doc = gedit_document_new (); +	g_object_set_data (G_OBJECT (doc), GEDIT_TAB_KEY, tab); + +	_gedit_document_set_mount_operation_factory (doc, +						     tab_mount_operation_factory, +						     tab); + +	tab->priv->view = gedit_view_new (doc); +	g_object_unref (doc); +	gtk_widget_show (tab->priv->view); +	g_object_set_data (G_OBJECT (tab->priv->view), GEDIT_TAB_KEY, tab); + +	gtk_box_pack_end (GTK_BOX (tab), sw, TRUE, TRUE, 0); +	gtk_container_add (GTK_CONTAINER (sw), tab->priv->view); +	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), +					     GTK_SHADOW_IN);	 +	gtk_widget_show (sw); + +	g_signal_connect (doc, +			  "notify::uri", +			  G_CALLBACK (document_uri_notify_handler), +			  tab); +	g_signal_connect (doc, +			  "notify::shortname", +			  G_CALLBACK (document_shortname_notify_handler), +			  tab); +	g_signal_connect (doc, +			  "modified_changed", +			  G_CALLBACK (document_modified_changed), +			  tab); +	g_signal_connect (doc, +			  "loading", +			  G_CALLBACK (document_loading), +			  tab); +	g_signal_connect (doc, +			  "loaded", +			  G_CALLBACK (document_loaded), +			  tab); +	g_signal_connect (doc, +			  "saving", +			  G_CALLBACK (document_saving), +			  tab); +	g_signal_connect (doc, +			  "saved", +			  G_CALLBACK (document_saved), +			  tab); + +	g_signal_connect_after (tab->priv->view, +				"focus-in-event", +				G_CALLBACK (view_focused_in), +				tab); + +	g_signal_connect_after (tab->priv->view, +				"realize", +				G_CALLBACK (view_realized), +				tab); +} + +GtkWidget * +_gedit_tab_new (void) +{ +	return GTK_WIDGET (g_object_new (GEDIT_TYPE_TAB, NULL)); +} + +/* Whether create is TRUE, creates a new empty document if location does  +   not refer to an existing file */ +GtkWidget * +_gedit_tab_new_from_uri (const gchar         *uri, +			 const GeditEncoding *encoding, +			 gint                 line_pos,			 +			 gboolean             create) +{ +	GeditTab *tab; + +	g_return_val_if_fail (uri != NULL, NULL); + +	tab = GEDIT_TAB (_gedit_tab_new ()); + +	_gedit_tab_load (tab, +			 uri, +			 encoding, +			 line_pos, +			 create); + +	return GTK_WIDGET (tab); +}		 + +/** + * gedit_tab_get_view: + * @tab: a #GeditTab + * + * Gets the #GeditView inside @tab. + * + * Returns: the #GeditView inside @tab + */ +GeditView * +gedit_tab_get_view (GeditTab *tab) +{ +	return GEDIT_VIEW (tab->priv->view); +} + +/** + * gedit_tab_get_document: + * @tab: a #GeditTab + * + * Gets the #GeditDocument associated to @tab. + * + * Returns: the #GeditDocument associated to @tab + */ +GeditDocument * +gedit_tab_get_document (GeditTab *tab) +{ +	return GEDIT_DOCUMENT (gtk_text_view_get_buffer ( +					GTK_TEXT_VIEW (tab->priv->view))); +} + +#define MAX_DOC_NAME_LENGTH 40 + +gchar * +_gedit_tab_get_name (GeditTab *tab) +{ +	GeditDocument *doc; +	gchar *name; +	gchar *docname; +	gchar *tab_name; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + +	doc = gedit_tab_get_document (tab); + +	name = gedit_document_get_short_name_for_display (doc); + +	/* Truncate the name so it doesn't get insanely wide. */ +	docname = gedit_utils_str_middle_truncate (name, MAX_DOC_NAME_LENGTH); + +	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) +	{ +		tab_name = g_strdup_printf ("*%s", docname); +	}  +	else  +	{ + #if 0		 +		if (gedit_document_get_readonly (doc))  +		{ +			tab_name = g_strdup_printf ("%s [%s]", docname,  +						/*Read only*/ _("RO")); +		}  +		else  +		{ +			tab_name = g_strdup_printf ("%s", docname); +		} +#endif +		tab_name = g_strdup (docname); +	} +	 +	g_free (docname); +	g_free (name); + +	return tab_name; +} + +gchar * +_gedit_tab_get_tooltips	(GeditTab *tab) +{ +	GeditDocument *doc; +	gchar *tip; +	gchar *uri; +	gchar *ruri; +	gchar *ruri_markup; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + +	doc = gedit_tab_get_document (tab); + +	uri = gedit_document_get_uri_for_display (doc); +	g_return_val_if_fail (uri != NULL, NULL); + +	ruri = 	gedit_utils_replace_home_dir_with_tilde (uri); +	g_free (uri); + +	ruri_markup = g_markup_printf_escaped ("<i>%s</i>", ruri); + +	switch (tab->priv->state) +	{ +		gchar *content_type; +		gchar *mime_type; +		gchar *content_description; +		gchar *content_full_description;  +		gchar *encoding; +		const GeditEncoding *enc; + +		case GEDIT_TAB_STATE_LOADING_ERROR: +			tip = g_strdup_printf (_("Error opening file %s"), +					       ruri_markup); +			break; + +		case GEDIT_TAB_STATE_REVERTING_ERROR: +			tip = g_strdup_printf (_("Error reverting file %s"), +					       ruri_markup); +			break;			 + +		case GEDIT_TAB_STATE_SAVING_ERROR: +			tip =  g_strdup_printf (_("Error saving file %s"), +						ruri_markup); +			break;			 +		default: +			content_type = gedit_document_get_content_type (doc); +			mime_type = gedit_document_get_mime_type (doc); +			content_description = g_content_type_get_description (content_type); + +			if (content_description == NULL) +				content_full_description = g_strdup (mime_type); +			else +				content_full_description = g_strdup_printf ("%s (%s)",  +						content_description, mime_type); + +			g_free (content_type); +			g_free (mime_type); +			g_free (content_description); + +			enc = gedit_document_get_encoding (doc); + +			if (enc == NULL) +				encoding = g_strdup (_("Unicode (UTF-8)")); +			else +				encoding = gedit_encoding_to_string (enc); + +			tip =  g_markup_printf_escaped ("<b>%s</b> %s\n\n" +						        "<b>%s</b> %s\n" +						        "<b>%s</b> %s", +						        _("Name:"), ruri, +						        _("MIME Type:"), content_full_description, +						        _("Encoding:"), encoding); + +			g_free (encoding); +			g_free (content_full_description); + +			break; +	} + +	g_free (ruri);	 +	g_free (ruri_markup); +	 +	return tip; +} + +static GdkPixbuf * +resize_icon (GdkPixbuf *pixbuf, +	     gint       size) +{ +	gint width, height; + +	width = gdk_pixbuf_get_width (pixbuf);  +	height = gdk_pixbuf_get_height (pixbuf); + +	/* if the icon is larger than the nominal size, scale down */ +	if (MAX (width, height) > size)  +	{ +		GdkPixbuf *scaled_pixbuf; +		 +		if (width > height)  +		{ +			height = height * size / width; +			width = size; +		}  +		else  +		{ +			width = width * size / height; +			height = size; +		} +		 +		scaled_pixbuf = gdk_pixbuf_scale_simple	(pixbuf,  +							 width,  +							 height,  +							 GDK_INTERP_BILINEAR); +		g_object_unref (pixbuf); +		pixbuf = scaled_pixbuf; +	} + +	return pixbuf; +} + +static GdkPixbuf * +get_stock_icon (GtkIconTheme *theme,  +		const gchar  *stock, +		gint          size) +{ +	GdkPixbuf *pixbuf; +	 +	pixbuf = gtk_icon_theme_load_icon (theme, stock, size, 0, NULL); +	if (pixbuf == NULL) +		return NULL; +		 +	return resize_icon (pixbuf, size); +} + +static GdkPixbuf * +get_icon (GtkIconTheme *theme,  +	  GFile        *location, +	  gint          size) +{ +	GdkPixbuf *pixbuf; +	GtkIconInfo *icon_info; +	GFileInfo *info; +	GIcon *gicon; + +	if (location == NULL) +		return get_stock_icon (theme, GTK_STOCK_FILE, size); + +	/* FIXME: Doing a sync stat is bad, this should be fixed */ +	info = g_file_query_info (location,  +	                          G_FILE_ATTRIBUTE_STANDARD_ICON,  +	                          G_FILE_QUERY_INFO_NONE,  +	                          NULL,  +	                          NULL); +	if (info == NULL) +		return get_stock_icon (theme, GTK_STOCK_FILE, size); + +	gicon = g_file_info_get_icon (info); + +	if (gicon == NULL) +	{ +		g_object_unref (info); +		return get_stock_icon (theme, GTK_STOCK_FILE, size); +	} + +	icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, size, 0); +	g_object_unref (info);	 +	 +	if (icon_info == NULL) +		return get_stock_icon (theme, GTK_STOCK_FILE, size); +	 +	pixbuf = gtk_icon_info_load_icon (icon_info, NULL); +	gtk_icon_info_free (icon_info); +	 +	if (pixbuf == NULL) +		return get_stock_icon (theme, GTK_STOCK_FILE, size); +		 +	return resize_icon (pixbuf, size); +} + +/* FIXME: add support for theme changed. I think it should be as easy as +   call g_object_notify (tab, "name") when the icon theme changes */ +GdkPixbuf * +_gedit_tab_get_icon (GeditTab *tab) +{ +	GdkPixbuf *pixbuf; +	GtkIconTheme *theme; +	GdkScreen *screen; +	gint icon_size; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); + +	screen = gtk_widget_get_screen (GTK_WIDGET (tab)); + +	theme = gtk_icon_theme_get_for_screen (screen); +	g_return_val_if_fail (theme != NULL, NULL); + +	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (tab)), +					   GTK_ICON_SIZE_MENU,  +					   NULL, +					   &icon_size); + +	switch (tab->priv->state) +	{ +		case GEDIT_TAB_STATE_LOADING: +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_OPEN,  +						 icon_size); +			break; + +		case GEDIT_TAB_STATE_REVERTING: +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_REVERT_TO_SAVED,  +						 icon_size);						  +			break; + +		case GEDIT_TAB_STATE_SAVING: +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_SAVE,  +						 icon_size); +			break; + +		case GEDIT_TAB_STATE_PRINTING: +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_PRINT,  +						 icon_size); +			break; + +		case GEDIT_TAB_STATE_PRINT_PREVIEWING: +		case GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW:		 +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_PRINT_PREVIEW,  +						 icon_size); +			break; + +		case GEDIT_TAB_STATE_LOADING_ERROR: +		case GEDIT_TAB_STATE_REVERTING_ERROR: +		case GEDIT_TAB_STATE_SAVING_ERROR: +		case GEDIT_TAB_STATE_GENERIC_ERROR: +			pixbuf = get_stock_icon (theme,  +						 GTK_STOCK_DIALOG_ERROR,  +						 icon_size); +			break; + +		case GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION: +			pixbuf = get_stock_icon (theme, +						 GTK_STOCK_DIALOG_WARNING, +						 icon_size); +			break; + +		default: +		{ +			GFile *location; +			GeditDocument *doc; + +			doc = gedit_tab_get_document (tab); + +			location = gedit_document_get_location (doc); +			pixbuf = get_icon (theme, location, icon_size); + +			if (location) +				g_object_unref (location); +		} +	} + +	return pixbuf; +} + +/** + * gedit_tab_get_from_document: + * @doc: a #GeditDocument + * + * Gets the #GeditTab associated with @doc. + * + * Returns: the #GeditTab associated with @doc + */ +GeditTab * +gedit_tab_get_from_document (GeditDocument *doc) +{ +	gpointer res; +	 +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); +	 +	res = g_object_get_data (G_OBJECT (doc), GEDIT_TAB_KEY); +	 +	return (res != NULL) ? GEDIT_TAB (res) : NULL; +} + +void +_gedit_tab_load (GeditTab            *tab, +		 const gchar         *uri, +		 const GeditEncoding *encoding, +		 gint                 line_pos, +		 gboolean             create) +{ +	GeditDocument *doc; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_NORMAL); + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_LOADING); + +	tab->priv->tmp_line_pos = line_pos; +	tab->priv->tmp_encoding = encoding; + +	if (tab->priv->auto_save_timeout > 0) +		remove_auto_save_timeout (tab); + +	gedit_document_load (doc, +			     uri, +			     encoding, +			     line_pos, +			     create); +} + +void +_gedit_tab_revert (GeditTab *tab) +{ +	GeditDocument *doc; +	gchar *uri; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail ((tab->priv->state == GEDIT_TAB_STATE_NORMAL) || +			  (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + +	if (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) +	{ +		set_message_area (tab, NULL); +	} + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_REVERTING); + +	uri = gedit_document_get_uri (doc); +	g_return_if_fail (uri != NULL); + +	tab->priv->tmp_line_pos = 0; +	tab->priv->tmp_encoding = gedit_document_get_encoding (doc); + +	if (tab->priv->auto_save_timeout > 0) +		remove_auto_save_timeout (tab); + +	gedit_document_load (doc, +			     uri, +			     tab->priv->tmp_encoding, +			     0, +			     FALSE); + +	g_free (uri); +} + +void +_gedit_tab_save (GeditTab *tab) +{ +	GeditDocument *doc; +	GeditDocumentSaveFlags save_flags; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail ((tab->priv->state == GEDIT_TAB_STATE_NORMAL) || +			  (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || +			  (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)); +	g_return_if_fail (tab->priv->tmp_save_uri == NULL); +	g_return_if_fail (tab->priv->tmp_encoding == NULL); + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (!gedit_document_is_untitled (doc)); + +	if (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) +	{ +		/* We already told the user about the external +		 * modification: hide the message area and set +		 * the save flag. +		 */ + +		set_message_area (tab, NULL); +		save_flags = tab->priv->save_flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME; +	} +	else +	{ +		save_flags = tab->priv->save_flags; +	} + +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); + +	/* uri used in error messages, will be freed in document_saved */ +	tab->priv->tmp_save_uri = gedit_document_get_uri (doc); +	tab->priv->tmp_encoding = gedit_document_get_encoding (doc);  + +	if (tab->priv->auto_save_timeout > 0) +		remove_auto_save_timeout (tab); +		 +	gedit_document_save (doc, save_flags); +} + +static gboolean +gedit_tab_auto_save (GeditTab *tab) +{ +	GeditDocument *doc; + +	gedit_debug (DEBUG_TAB); +	 +	g_return_val_if_fail (tab->priv->tmp_save_uri == NULL, FALSE); +	g_return_val_if_fail (tab->priv->tmp_encoding == NULL, FALSE); +	 +	doc = gedit_tab_get_document (tab); +	 +	g_return_val_if_fail (!gedit_document_is_untitled (doc), FALSE); +	g_return_val_if_fail (!gedit_document_get_readonly (doc), FALSE); + +	g_return_val_if_fail (tab->priv->auto_save_timeout > 0, FALSE); +	g_return_val_if_fail (tab->priv->auto_save, FALSE); +	g_return_val_if_fail (tab->priv->auto_save_interval > 0, FALSE); + +	if (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER(doc))) +	{ +		gedit_debug_message (DEBUG_TAB, "Document not modified"); + +		return TRUE; +	} +			 +	if ((tab->priv->state != GEDIT_TAB_STATE_NORMAL) && +	    (tab->priv->state != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) +	{ +		/* Retry after 30 seconds */ +		guint timeout; + +		gedit_debug_message (DEBUG_TAB, "Retry after 30 seconds"); + +		/* Add a new timeout */ +		timeout = g_timeout_add_seconds (30, +						 (GSourceFunc) gedit_tab_auto_save, +						 tab); + +		tab->priv->auto_save_timeout = timeout; + +	    	/* Returns FALSE so the old timeout is "destroyed" */ +		return FALSE; +	} +	 +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); + +	/* uri used in error messages, will be freed in document_saved */ +	tab->priv->tmp_save_uri = gedit_document_get_uri (doc); +	tab->priv->tmp_encoding = gedit_document_get_encoding (doc);  + +	/* Set auto_save_timeout to 0 since the timeout is going to be destroyed */ +	tab->priv->auto_save_timeout = 0; + +	/* Since we are autosaving, we need to preserve the backup that was produced +	   the last time the user "manually" saved the file. In the case a recoverable +	   error happens while saving, the last backup is not preserved since the user +	   expressed his willing of saving the file */ +	gedit_document_save (doc, tab->priv->save_flags | GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP); +	 +	gedit_debug_message (DEBUG_TAB, "Done"); +	 +	/* Returns FALSE so the old timeout is "destroyed" */ +	return FALSE; +} + +void +_gedit_tab_save_as (GeditTab                 *tab, +                    const gchar              *uri, +                    const GeditEncoding      *encoding, +                    GeditDocumentNewlineType  newline_type) +{ +	GeditDocument *doc; +	GeditDocumentSaveFlags save_flags; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail ((tab->priv->state == GEDIT_TAB_STATE_NORMAL) || +			  (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || +			  (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)); +	g_return_if_fail (encoding != NULL); + +	g_return_if_fail (tab->priv->tmp_save_uri == NULL); +	g_return_if_fail (tab->priv->tmp_encoding == NULL); + +	doc = gedit_tab_get_document (tab); +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); + +	/* reset the save flags, when saving as */ +	tab->priv->save_flags = 0; + +	if (tab->priv->state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) +	{ +		/* We already told the user about the external +		 * modification: hide the message area and set +		 * the save flag. +		 */ + +		set_message_area (tab, NULL); +		save_flags = tab->priv->save_flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME; +	} +	else +	{ +		save_flags = tab->priv->save_flags; +	} + +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING); + +	/* uri used in error messages... strdup because errors are async +	 * and the string can go away, will be freed in document_saved */ +	tab->priv->tmp_save_uri = g_strdup (uri); +	tab->priv->tmp_encoding = encoding; + +	if (tab->priv->auto_save_timeout > 0) +		remove_auto_save_timeout (tab); + +	/* FIXME: this should behave the same as encoding, setting it here +	   makes it persistent (if save fails, it's remembered). It's not +	   a very big deal, but would be nice to have them follow the +	   same pattern. This can be changed once we break API for 3.0 */ +	gedit_document_set_newline_type (doc, newline_type); +	gedit_document_save_as (doc, uri, encoding, tab->priv->save_flags); +} + +#define GEDIT_PAGE_SETUP_KEY "gedit-page-setup-key" +#define GEDIT_PRINT_SETTINGS_KEY "gedit-print-settings-key" + +static GtkPageSetup * +get_page_setup (GeditTab *tab) +{ +	gpointer data; +	GeditDocument *doc; + +	doc = gedit_tab_get_document (tab); + +	data = g_object_get_data (G_OBJECT (doc), +				  GEDIT_PAGE_SETUP_KEY); + +	if (data == NULL) +	{ +		return _gedit_app_get_default_page_setup (gedit_app_get_default()); +	} +	else +	{ +		return gtk_page_setup_copy (GTK_PAGE_SETUP (data)); +	} +} + +static GtkPrintSettings * +get_print_settings (GeditTab *tab) +{ +	gpointer data; +	GeditDocument *doc; + +	doc = gedit_tab_get_document (tab); + +	data = g_object_get_data (G_OBJECT (doc), +				  GEDIT_PRINT_SETTINGS_KEY); + +	if (data == NULL) +	{ +		return _gedit_app_get_default_print_settings (gedit_app_get_default()); +	} +	else +	{ +		return gtk_print_settings_copy (GTK_PRINT_SETTINGS (data)); +	} +} + +/* FIXME: show the message area only if the operation will be "long" */ +static void +printing_cb (GeditPrintJob       *job, +	     GeditPrintJobStatus  status, +	     GeditTab            *tab) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area));	 + +	gtk_widget_show (tab->priv->message_area); + +	gedit_progress_message_area_set_text (GEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area), +					      gedit_print_job_get_status_string (job)); + +	gedit_progress_message_area_set_fraction (GEDIT_PROGRESS_MESSAGE_AREA (tab->priv->message_area), +						  gedit_print_job_get_progress (job)); +} + +static void +store_print_settings (GeditTab      *tab, +		      GeditPrintJob *job) +{ +	GeditDocument *doc; +	GtkPrintSettings *settings; +	GtkPageSetup *page_setup; + +	doc = gedit_tab_get_document (tab); + +	settings = gedit_print_job_get_print_settings (job); + +	/* clear n-copies settings since we do not want to +	 * persist that one */ +	gtk_print_settings_unset (settings, +				  GTK_PRINT_SETTINGS_N_COPIES); + +	/* remember settings for this document */ +	g_object_set_data_full (G_OBJECT (doc), +				GEDIT_PRINT_SETTINGS_KEY, +				g_object_ref (settings), +				(GDestroyNotify)g_object_unref); + +	/* make them the default */ +	_gedit_app_set_default_print_settings (gedit_app_get_default (), +					       settings); + +	page_setup = gedit_print_job_get_page_setup (job); + +	/* remember page setup for this document */ +	g_object_set_data_full (G_OBJECT (doc), +				GEDIT_PAGE_SETUP_KEY, +				g_object_ref (page_setup), +				(GDestroyNotify)g_object_unref); + +	/* make it the default */ +	_gedit_app_set_default_page_setup (gedit_app_get_default (), +					   page_setup); +} + +static void +done_printing_cb (GeditPrintJob       *job, +		  GeditPrintJobResult  result, +		  const GError        *error, +		  GeditTab            *tab) +{ +	GeditView *view; + +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_PRINT_PREVIEWING || +			  tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW || +			  tab->priv->state == GEDIT_TAB_STATE_PRINTING); + +	if (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) +	{ +		/* print preview has been destroyed... */ +		tab->priv->print_preview = NULL; +	} +	else +	{ +		g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area)); + +		set_message_area (tab, NULL); /* destroy the message area */ +	} + +	// TODO: check status and error + +	if (result ==  GEDIT_PRINT_JOB_RESULT_OK) +	{ +		store_print_settings (tab, job); +	} + +#if 0 +	if (tab->priv->print_preview != NULL) +	{ +		/* If we were printing while showing the print preview, +		   see bug #352658 */ +		gtk_widget_destroy (tab->priv->print_preview); +		g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_PRINTING); +	} +#endif + +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + +	view = gedit_tab_get_view (tab); +	gtk_widget_grab_focus (GTK_WIDGET (view)); + + 	g_object_unref (tab->priv->print_job); +	tab->priv->print_job = NULL; +} + +#if 0 +static void +print_preview_destroyed (GtkWidget *preview, +			 GeditTab  *tab) +{ +	tab->priv->print_preview = NULL; + +	if (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) +	{ +		GeditView *view; + +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); + +		view = gedit_tab_get_view (tab); +		gtk_widget_grab_focus (GTK_WIDGET (view)); +	} +	else +	{ +		/* This should happen only when printing while showing the print +		 * preview. In this case let us continue whithout changing +		 * the state and show the document. See bug #352658 */ +		gtk_widget_show (tab->priv->view_scrolled_window); +		 +		g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_PRINTING); +	}	 +} +#endif + +static void +show_preview_cb (GeditPrintJob       *job, +		 GeditPrintPreview   *preview, +		 GeditTab            *tab) +{ +//	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_PRINT_PREVIEWING); +	g_return_if_fail (tab->priv->print_preview == NULL); + +	set_message_area (tab, NULL); /* destroy the message area */ + +	tab->priv->print_preview = GTK_WIDGET (preview); +	gtk_box_pack_end (GTK_BOX (tab), +			  tab->priv->print_preview, +			  TRUE, +			  TRUE, +			  0); +	gtk_widget_show (tab->priv->print_preview); +	gtk_widget_grab_focus (tab->priv->print_preview); + +/* when the preview gets destroyed we get "done" signal +	g_signal_connect (tab->priv->print_preview, +			  "destroy", +			  G_CALLBACK (print_preview_destroyed), +			  tab);	 +*/ +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW); +} + +#if 0 + +static void +set_print_preview (GeditTab  *tab, +		   GtkWidget *print_preview) +{ +	if (tab->priv->print_preview == print_preview) +		return; +		 +	if (tab->priv->print_preview != NULL) +		gtk_widget_destroy (tab->priv->print_preview); + +	tab->priv->print_preview = print_preview; + +	gtk_box_pack_end (GTK_BOX (tab), +			  tab->priv->print_preview, +			  TRUE, +			  TRUE, +			  0);		 + +	gtk_widget_grab_focus (tab->priv->print_preview); + +	g_signal_connect (tab->priv->print_preview, +			  "destroy", +			  G_CALLBACK (print_preview_destroyed), +			  tab); +} + +static void +preview_finished_cb (GtkSourcePrintJob *pjob, GeditTab *tab) +{ +	MatePrintJob *gjob; +	GtkWidget *preview = NULL; + +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area)); +	set_message_area (tab, NULL); /* destroy the message area */ +	 +	gjob = gtk_source_print_job_get_print_job (pjob); + +	preview = gedit_print_job_preview_new (gjob);	 + 	g_object_unref (gjob); +	 +	set_print_preview (tab, preview); +	 +	gtk_widget_show (preview); +	g_object_unref (pjob); +	 +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW); +} + + +#endif + +static void +print_cancelled (GtkWidget        *area, +                 gint              response_id, +                 GeditTab         *tab) +{ +	g_return_if_fail (GEDIT_IS_PROGRESS_MESSAGE_AREA (tab->priv->message_area)); + +	gedit_print_job_cancel (tab->priv->print_job); + +	g_debug ("print_cancelled"); +} + +static void +show_printing_message_area (GeditTab *tab, gboolean preview) +{ +	GtkWidget *area; + +	if (preview) +		area = gedit_progress_message_area_new (GTK_STOCK_PRINT_PREVIEW, +							"", +							TRUE); +	else +		area = gedit_progress_message_area_new (GTK_STOCK_PRINT, +							"", +							TRUE); + +	g_signal_connect (area, +			  "response", +			  G_CALLBACK (print_cancelled), +			  tab); +	   +	set_message_area (tab, area); +} + +#if !GTK_CHECK_VERSION (2, 17, 4) + +static void +page_setup_done_cb (GtkPageSetup *setup, +		    GeditTab     *tab) +{ +	if (setup != NULL) +	{ +		GeditDocument *doc; + +		doc = gedit_tab_get_document (tab); + +		/* remember it for this document */ +		g_object_set_data_full (G_OBJECT (doc), +					GEDIT_PAGE_SETUP_KEY, +					g_object_ref (setup), +					(GDestroyNotify)g_object_unref); + +		/* make it the default */ +		_gedit_app_set_default_page_setup (gedit_app_get_default(), +						   setup); +	} +} + +void  +_gedit_tab_page_setup (GeditTab *tab) +{ +	GtkPageSetup *setup; +	GtkPrintSettings *settings; + +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	setup = get_page_setup (tab); +	settings = get_print_settings (tab); + +	gtk_print_run_page_setup_dialog_async (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), +		 			       setup, +		 			       settings, +					       (GtkPageSetupDoneFunc) page_setup_done_cb, +					       tab); + +	/* CHECK: should we unref setup and settings? */ +} + +#endif + +static void +gedit_tab_print_or_print_preview (GeditTab                *tab, +				  GtkPrintOperationAction  print_action) +{ +	GeditView *view; +	gboolean is_preview; +	GtkPageSetup *setup; +	GtkPrintSettings *settings; +	GtkPrintOperationResult res; +	GError *error = NULL; + +	g_return_if_fail (tab->priv->print_job == NULL); +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_NORMAL); + +	view = gedit_tab_get_view (tab); + +	is_preview = (print_action == GTK_PRINT_OPERATION_ACTION_PREVIEW); + +	tab->priv->print_job = gedit_print_job_new (view); +	g_object_add_weak_pointer (G_OBJECT (tab->priv->print_job),  +				   (gpointer *) &tab->priv->print_job); + +	show_printing_message_area (tab, is_preview); + +	g_signal_connect (tab->priv->print_job, +			  "printing", +			  G_CALLBACK (printing_cb), +			  tab); +	g_signal_connect (tab->priv->print_job, +			  "show-preview", +			  G_CALLBACK (show_preview_cb), +			  tab); +	g_signal_connect (tab->priv->print_job, +			  "done", +			  G_CALLBACK (done_printing_cb), +			  tab); + +	if (is_preview) +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_PRINT_PREVIEWING); +	else +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_PRINTING); + +	setup = get_page_setup (tab); +	settings = get_print_settings (tab); + +	res = gedit_print_job_print (tab->priv->print_job, +				     print_action, +				     setup, +				     settings, +				     GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab))), +				     &error); + +	// TODO: manage res in the correct way +	if (res == GTK_PRINT_OPERATION_RESULT_ERROR) +	{ +		/* FIXME: go in error state */ +		gedit_tab_set_state (tab, GEDIT_TAB_STATE_NORMAL); +		g_warning ("Async print preview failed (%s)", error->message); +		g_object_unref (tab->priv->print_job); +		g_error_free (error); +	} +} + +void  +_gedit_tab_print (GeditTab     *tab) +{ +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	/* FIXME: currently we can have just one printoperation going on +	 * at a given time, so before starting the print we close the preview. +	 * Would be nice to handle it properly though */ +	if (tab->priv->state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) +	{ +		gtk_widget_destroy (tab->priv->print_preview); +	} + +	gedit_tab_print_or_print_preview (tab, +					  GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG); +} + +void +_gedit_tab_print_preview (GeditTab     *tab) +{ +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	gedit_tab_print_or_print_preview (tab, +					  GTK_PRINT_OPERATION_ACTION_PREVIEW); +} + +void  +_gedit_tab_mark_for_closing (GeditTab *tab) +{ +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail (tab->priv->state == GEDIT_TAB_STATE_NORMAL); +	 +	gedit_tab_set_state (tab, GEDIT_TAB_STATE_CLOSING); +} + +gboolean +_gedit_tab_can_close (GeditTab *tab) +{ +	GeditDocument *doc; +	GeditTabState  ts; + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), FALSE); + +	ts = gedit_tab_get_state (tab); + +	/* if we are loading or reverting, the tab can be closed */ +	if ((ts == GEDIT_TAB_STATE_LOADING)       || +	    (ts == GEDIT_TAB_STATE_LOADING_ERROR) || +	    (ts == GEDIT_TAB_STATE_REVERTING)     || +	    (ts == GEDIT_TAB_STATE_REVERTING_ERROR)) /* CHECK: I'm not sure this is the right behavior for REVERTING ERROR */ +		return TRUE; + +	/* Do not close tab with saving errors */ +	if (ts == GEDIT_TAB_STATE_SAVING_ERROR) +		return FALSE; +		 +	doc = gedit_tab_get_document (tab); + +	/* TODO: we need to save the file also if it has been externally +	   modified - Paolo (Oct 10, 2005) */ + +	return (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)) && +		!gedit_document_get_deleted (doc)); +} + +/** + * gedit_tab_get_auto_save_enabled: + * @tab: a #GeditTab + *  + * Gets the current state for the autosave feature + *  + * Return value: %TRUE if the autosave is enabled, else %FALSE + **/ +gboolean +gedit_tab_get_auto_save_enabled	(GeditTab *tab) +{ +	gedit_debug (DEBUG_TAB); + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), FALSE); + +	return tab->priv->auto_save; +} + +/** + * gedit_tab_set_auto_save_enabled: + * @tab: a #GeditTab + * @enable: enable (%TRUE) or disable (%FALSE) auto save + *  + * Enables or disables the autosave feature. It does not install an + * autosave timeout if the document is new or is read-only + **/ +void +gedit_tab_set_auto_save_enabled	(GeditTab *tab,  +				 gboolean enable) +{ +	GeditDocument *doc = NULL; +	GeditLockdownMask lockdown; +	 +	gedit_debug (DEBUG_TAB); + +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	/* Force disabling when lockdown is active */ +	lockdown = gedit_app_get_lockdown (gedit_app_get_default()); +	if (lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK) +		enable = FALSE; +	 +	doc = gedit_tab_get_document (tab); + +	if (tab->priv->auto_save == enable) +		return; + +	tab->priv->auto_save = enable; + + 	if (enable &&  + 	    (tab->priv->auto_save_timeout <=0) && + 	    !gedit_document_is_untitled (doc) && + 	    !gedit_document_get_readonly (doc)) + 	{ + 		if ((tab->priv->state != GEDIT_TAB_STATE_LOADING) && +		    (tab->priv->state != GEDIT_TAB_STATE_SAVING) && +		    (tab->priv->state != GEDIT_TAB_STATE_REVERTING) && +		    (tab->priv->state != GEDIT_TAB_STATE_LOADING_ERROR) && +		    (tab->priv->state != GEDIT_TAB_STATE_SAVING_ERROR) && +		    (tab->priv->state != GEDIT_TAB_STATE_REVERTING_ERROR)) +		{ +			install_auto_save_timeout (tab); +		} +		/* else: the timeout will be installed when loading/saving/reverting +		         will terminate */ +		 +		return; +	} + 		 + 	if (!enable && (tab->priv->auto_save_timeout > 0)) + 	{ +		remove_auto_save_timeout (tab); +		 + 		return;  + 	}  + + 	g_return_if_fail ((!enable && (tab->priv->auto_save_timeout <= 0)) ||   + 			  gedit_document_is_untitled (doc) || gedit_document_get_readonly (doc));  +} + +/** + * gedit_tab_get_auto_save_interval: + * @tab: a #GeditTab + *  + * Gets the current interval for the autosaves + *  + * Return value: the value of the autosave + **/ +gint  +gedit_tab_get_auto_save_interval (GeditTab *tab) +{ +	gedit_debug (DEBUG_TAB); + +	g_return_val_if_fail (GEDIT_IS_TAB (tab), 0); + +	return tab->priv->auto_save_interval; +} + +/** + * gedit_tab_set_auto_save_interval: + * @tab: a #GeditTab + * @interval: the new interval + *  + * Sets the interval for the autosave feature. It does nothing if the + * interval is the same as the one already present. It removes the old + * interval timeout and adds a new one with the autosave passed as + * argument. + **/ +void  +gedit_tab_set_auto_save_interval (GeditTab *tab,  +				  gint interval) +{ +	GeditDocument *doc = NULL; +	 +	gedit_debug (DEBUG_TAB); +	 +	g_return_if_fail (GEDIT_IS_TAB (tab)); + +	doc = gedit_tab_get_document(tab); + +	g_return_if_fail (GEDIT_IS_DOCUMENT (doc)); +	g_return_if_fail (interval > 0); + +	if (tab->priv->auto_save_interval == interval) +		return; + +	tab->priv->auto_save_interval = interval; +		 +	if (!tab->priv->auto_save) +		return; + +	if (tab->priv->auto_save_timeout > 0) +	{ +		g_return_if_fail (!gedit_document_is_untitled (doc)); +		g_return_if_fail (!gedit_document_get_readonly (doc)); + +		remove_auto_save_timeout (tab); + +		install_auto_save_timeout (tab); +	} +} + +void +gedit_tab_set_info_bar (GeditTab  *tab, +                        GtkWidget *info_bar) +{ +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail (info_bar == NULL || GTK_IS_WIDGET (info_bar)); + +	/* FIXME: this can cause problems with the tab state machine */ +	set_message_area (tab, info_bar); +} diff --git a/gedit/gedit-tab.h b/gedit/gedit-tab.h new file mode 100755 index 00000000..68262083 --- /dev/null +++ b/gedit/gedit-tab.h @@ -0,0 +1,165 @@ +/* + * gedit-tab.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_TAB_H__ +#define __GEDIT_TAB_H__ + +#include <gtk/gtk.h> + +#include <gedit/gedit-view.h> +#include <gedit/gedit-document.h> + +G_BEGIN_DECLS + +typedef enum +{ +	GEDIT_TAB_STATE_NORMAL = 0, +	GEDIT_TAB_STATE_LOADING, +	GEDIT_TAB_STATE_REVERTING, +	GEDIT_TAB_STATE_SAVING,	 +	GEDIT_TAB_STATE_PRINTING, +	GEDIT_TAB_STATE_PRINT_PREVIEWING, +	GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW, +	GEDIT_TAB_STATE_GENERIC_NOT_EDITABLE, +	GEDIT_TAB_STATE_LOADING_ERROR, +	GEDIT_TAB_STATE_REVERTING_ERROR,	 +	GEDIT_TAB_STATE_SAVING_ERROR, +	GEDIT_TAB_STATE_GENERIC_ERROR, +	GEDIT_TAB_STATE_CLOSING, +	GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION, +	GEDIT_TAB_NUM_OF_STATES /* This is not a valid state */ +} GeditTabState; + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_TAB              (gedit_tab_get_type()) +#define GEDIT_TAB(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_TAB, GeditTab)) +#define GEDIT_TAB_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_TAB, GeditTabClass)) +#define GEDIT_IS_TAB(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_TAB)) +#define GEDIT_IS_TAB_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_TAB)) +#define GEDIT_TAB_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_TAB, GeditTabClass)) + +/* Private structure type */ +typedef struct _GeditTabPrivate GeditTabPrivate; + +/* + * Main object structure + */ +typedef struct _GeditTab GeditTab; + +struct _GeditTab  +{ +	GtkVBox vbox; + +	/*< private > */ +	GeditTabPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditTabClass GeditTabClass; + +struct _GeditTabClass  +{ +	GtkVBoxClass parent_class; +}; + +/* + * Public methods + */ +GType 		 gedit_tab_get_type 		(void) G_GNUC_CONST; + +GeditView	*gedit_tab_get_view		(GeditTab            *tab); + +/* This is only an helper function */ +GeditDocument	*gedit_tab_get_document		(GeditTab            *tab); + +GeditTab	*gedit_tab_get_from_document	(GeditDocument       *doc); + +GeditTabState	 gedit_tab_get_state		(GeditTab	     *tab); + +gboolean	 gedit_tab_get_auto_save_enabled	 +						(GeditTab            *tab);  + +void		 gedit_tab_set_auto_save_enabled	 +						(GeditTab            *tab,  +						 gboolean            enable); + +gint		 gedit_tab_get_auto_save_interval  +						(GeditTab            *tab); + +void		 gedit_tab_set_auto_save_interval  +						(GeditTab            *tab,  +						 gint                interval); + +void		 gedit_tab_set_info_bar		(GeditTab            *tab, +						 GtkWidget           *info_bar); +/* + * Non exported methods + */ +GtkWidget 	*_gedit_tab_new 		(void); + +/* Whether create is TRUE, creates a new empty document if location does  +   not refer to an existing file */ +GtkWidget	*_gedit_tab_new_from_uri	(const gchar         *uri, +						 const GeditEncoding *encoding, +						 gint                 line_pos, +						 gboolean             create); +gchar 		*_gedit_tab_get_name		(GeditTab            *tab); +gchar 		*_gedit_tab_get_tooltips	(GeditTab            *tab); +GdkPixbuf 	*_gedit_tab_get_icon		(GeditTab            *tab); +void		 _gedit_tab_load		(GeditTab            *tab, +						 const gchar         *uri, +						 const GeditEncoding *encoding, +						 gint                 line_pos, +						 gboolean             create); +void		 _gedit_tab_revert		(GeditTab            *tab); +void		 _gedit_tab_save		(GeditTab            *tab); +void		 _gedit_tab_save_as		(GeditTab            *tab, +						 const gchar         *uri, +						 const GeditEncoding *encoding, +						 GeditDocumentNewlineType newline_type); + +void		 _gedit_tab_print		(GeditTab            *tab); +void		 _gedit_tab_print_preview	(GeditTab            *tab); + +void		 _gedit_tab_mark_for_closing	(GeditTab	     *tab); + +gboolean	 _gedit_tab_can_close		(GeditTab	     *tab); + +#if !GTK_CHECK_VERSION (2, 17, 4) +void		 _gedit_tab_page_setup		(GeditTab            *tab); +#endif + +G_END_DECLS + +#endif  /* __GEDIT_TAB_H__  */ diff --git a/gedit/gedit-ui.h b/gedit/gedit-ui.h new file mode 100755 index 00000000..8a536251 --- /dev/null +++ b/gedit/gedit-ui.h @@ -0,0 +1,188 @@ +/* + * gedit-ui.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_UI_H__ +#define __GEDIT_UI_H__ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> + +#include "gedit-commands.h" + +G_BEGIN_DECLS + +static const GtkActionEntry gedit_always_sensitive_menu_entries[] = +{ +	/* Toplevel */ +	{ "File", NULL, N_("_File") }, +	{ "Edit", NULL, N_("_Edit") }, +	{ "View", NULL, N_("_View") }, +	{ "Search", NULL, N_("_Search") }, +	{ "Tools", NULL, N_("_Tools") }, +	{ "Documents", NULL, N_("_Documents") }, +	{ "Help", NULL, N_("_Help") }, + +	/* File menu */ +	{ "FileNew", GTK_STOCK_NEW, NULL, "<control>N", +	  N_("Create a new document"), G_CALLBACK (_gedit_cmd_file_new) }, +	{ "FileOpen", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", +	  N_("Open a file"), G_CALLBACK (_gedit_cmd_file_open) }, + +	/* Edit menu */ +	{ "EditPreferences", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), NULL, +	  N_("Configure the application"), G_CALLBACK (_gedit_cmd_edit_preferences) }, + +	/* Help menu */ +	{"HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", +	 N_("Open the gedit manual"), G_CALLBACK (_gedit_cmd_help_contents) }, +	{ "HelpAbout", GTK_STOCK_ABOUT, NULL, NULL, +	 N_("About this application"), G_CALLBACK (_gedit_cmd_help_about) }, +	 +	/* Fullscreen toolbar */ +	{ "LeaveFullscreen", GTK_STOCK_LEAVE_FULLSCREEN, NULL, +	  NULL, N_("Leave fullscreen mode"), +	  G_CALLBACK (_gedit_cmd_view_leave_fullscreen_mode) } +}; + +static const GtkActionEntry gedit_menu_entries[] = +{ +	/* File menu */ +	{ "FileSave", GTK_STOCK_SAVE, NULL, "<control>S", +	  N_("Save the current file"), G_CALLBACK (_gedit_cmd_file_save) }, +	{ "FileSaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), "<shift><control>S", +	  N_("Save the current file with a different name"), G_CALLBACK (_gedit_cmd_file_save_as) }, +	{ "FileRevert", GTK_STOCK_REVERT_TO_SAVED, NULL, NULL, +	  N_("Revert to a saved version of the file"), G_CALLBACK (_gedit_cmd_file_revert) }, +#if !GTK_CHECK_VERSION (2, 17, 4) +	{ "FilePageSetup", GTK_STOCK_PAGE_SETUP, N_("Page Set_up..."), NULL, +	  N_("Set up the page settings"), G_CALLBACK (_gedit_cmd_file_page_setup) }, +#endif +	{ "FilePrintPreview", GTK_STOCK_PRINT_PREVIEW, N_("Print Previe_w"),"<control><shift>P", +	  N_("Print preview"), G_CALLBACK (_gedit_cmd_file_print_preview) }, +	 { "FilePrint", GTK_STOCK_PRINT, N_("_Print..."), "<control>P", +	  N_("Print the current page"), G_CALLBACK (_gedit_cmd_file_print) }, + +	/* Edit menu */ +	{ "EditUndo", GTK_STOCK_UNDO, NULL, "<control>Z", +	  N_("Undo the last action"), G_CALLBACK (_gedit_cmd_edit_undo) }, +	{ "EditRedo", GTK_STOCK_REDO, NULL, "<shift><control>Z", +	  N_("Redo the last undone action"), G_CALLBACK (_gedit_cmd_edit_redo) }, +	{ "EditCut", GTK_STOCK_CUT, NULL, "<control>X", +	  N_("Cut the selection"), G_CALLBACK (_gedit_cmd_edit_cut) }, +	{ "EditCopy", GTK_STOCK_COPY, NULL, "<control>C", +	  N_("Copy the selection"), G_CALLBACK (_gedit_cmd_edit_copy) }, +	{ "EditPaste", GTK_STOCK_PASTE, NULL, "<control>V", +	  N_("Paste the clipboard"), G_CALLBACK (_gedit_cmd_edit_paste) }, +	{ "EditDelete", GTK_STOCK_DELETE, NULL, NULL, +	  N_("Delete the selected text"), G_CALLBACK (_gedit_cmd_edit_delete) }, +	{ "EditSelectAll", GTK_STOCK_SELECT_ALL, N_("Select _All"), "<control>A", +	  N_("Select the entire document"), G_CALLBACK (_gedit_cmd_edit_select_all) }, + +	/* View menu */ +	{ "ViewHighlightMode", NULL, N_("_Highlight Mode") }, + +	/* Search menu */ +	{ "SearchFind", GTK_STOCK_FIND, N_("_Find..."), "<control>F", +	  N_("Search for text"), G_CALLBACK (_gedit_cmd_search_find) }, +	{ "SearchFindNext", NULL, N_("Find Ne_xt"), "<control>G", +	  N_("Search forwards for the same text"), G_CALLBACK (_gedit_cmd_search_find_next) }, +	{ "SearchFindPrevious", NULL, N_("Find Pre_vious"), "<shift><control>G", +	  N_("Search backwards for the same text"), G_CALLBACK (_gedit_cmd_search_find_prev) }, +#ifndef OS_OSX +	{ "SearchReplace", GTK_STOCK_FIND_AND_REPLACE, N_("_Replace..."), "<control>H", +	  N_("Search for and replace text"), G_CALLBACK (_gedit_cmd_search_replace) }, +#else +	{ "SearchReplace", GTK_STOCK_FIND_AND_REPLACE, N_("_Replace..."), "<control><alt>F", +	  N_("Search for and replace text"), G_CALLBACK (_gedit_cmd_search_replace) }, +#endif +	{ "SearchClearHighlight", NULL, N_("_Clear Highlight"), "<shift><control>K", +	  N_("Clear highlighting of search matches"), G_CALLBACK (_gedit_cmd_search_clear_highlight) }, +	{ "SearchGoToLine", GTK_STOCK_JUMP_TO, N_("Go to _Line..."), "<control>I", +	  N_("Go to a specific line"), G_CALLBACK (_gedit_cmd_search_goto_line) }, +	{ "SearchIncrementalSearch", GTK_STOCK_FIND, N_("_Incremental Search..."), "<control>K", +	  N_("Incrementally search for text"), G_CALLBACK (_gedit_cmd_search_incremental_search) }, + +	/* Documents menu */ +	{ "FileSaveAll", GTK_STOCK_SAVE, N_("_Save All"), "<shift><control>L", +	  N_("Save all open files"), G_CALLBACK (_gedit_cmd_file_save_all) }, +	{ "FileCloseAll", GTK_STOCK_CLOSE, N_("_Close All"), "<shift><control>W", +	  N_("Close all open files"), G_CALLBACK (_gedit_cmd_file_close_all) }, +	{ "DocumentsPreviousDocument", NULL, N_("_Previous Document"), "<alt><control>Page_Up", +	  N_("Activate previous document"), G_CALLBACK (_gedit_cmd_documents_previous_document) }, +	{ "DocumentsNextDocument", NULL, N_("_Next Document"), "<alt><control>Page_Down", +	  N_("Activate next document"), G_CALLBACK (_gedit_cmd_documents_next_document) }, +	{ "DocumentsMoveToNewWindow", NULL, N_("_Move to New Window"), NULL, +	  N_("Move the current document to a new window"), G_CALLBACK (_gedit_cmd_documents_move_to_new_window) } +}; + +/* separate group, needs to be sensitive on OS X even when there are no tabs */ +static const GtkActionEntry gedit_close_menu_entries[] = +{ +	{ "FileClose", GTK_STOCK_CLOSE, NULL, "<control>W", +	  N_("Close the current file"), G_CALLBACK (_gedit_cmd_file_close) } +}; + +/* separate group, should be sensitive even when there are no tabs */ +static const GtkActionEntry gedit_quit_menu_entries[] = +{ +	{ "FileQuit", GTK_STOCK_QUIT, NULL, "<control>Q", +	  N_("Quit the program"), G_CALLBACK (_gedit_cmd_file_quit) } +}; + +static const GtkToggleActionEntry gedit_always_sensitive_toggle_menu_entries[] = +{ +	{ "ViewToolbar", NULL, N_("_Toolbar"), NULL, +	  N_("Show or hide the toolbar in the current window"), +	  G_CALLBACK (_gedit_cmd_view_show_toolbar), TRUE }, +	{ "ViewStatusbar", NULL, N_("_Statusbar"), NULL, +	  N_("Show or hide the statusbar in the current window"), +	  G_CALLBACK (_gedit_cmd_view_show_statusbar), TRUE }, +	{ "ViewFullscreen", GTK_STOCK_FULLSCREEN, NULL, "F11", +	  N_("Edit text in fullscreen"), +	  G_CALLBACK (_gedit_cmd_view_toggle_fullscreen_mode), FALSE } +}; + +/* separate group, should be always sensitive except when there are no panes */ +static const GtkToggleActionEntry gedit_panes_toggle_menu_entries[] = +{ +	{ "ViewSidePane", NULL, N_("Side _Pane"), "F9", +	  N_("Show or hide the side pane in the current window"), +	  G_CALLBACK (_gedit_cmd_view_show_side_pane), FALSE }, +	{ "ViewBottomPane", NULL, N_("_Bottom Pane"), "<control>F9", +	  N_("Show or hide the bottom pane in the current window"), +	  G_CALLBACK (_gedit_cmd_view_show_bottom_pane), FALSE } +}; + +G_END_DECLS + +#endif  /* __GEDIT_UI_H__  */ diff --git a/gedit/gedit-ui.xml b/gedit/gedit-ui.xml new file mode 100755 index 00000000..21c31a72 --- /dev/null +++ b/gedit/gedit-ui.xml @@ -0,0 +1,203 @@ +<!-- + * gedit-ui.xml + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + * + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ +--> + +<ui> + +  <menubar name="MenuBar"> +    <menu name="FileMenu" action="File"> +      <menuitem name="FileNewMenu" action="FileNew"/> +      <placeholder name="FileOps_1"/> +      <menuitem name="FileOpenMenu" action="FileOpen"/> +      <placeholder name="FileOps_2"/> +      <separator/> +      <menuitem name="FileSaveMenu" action="FileSave"/> +      <menuitem name="FileSaveAsMenu" action="FileSaveAs"/> +      <placeholder name="FileOps_3"/> +      <menuitem name="FileRevertMenu" action="FileRevert"/> +      <placeholder name="FileOps_4"/> +      <separator/> +      <placeholder name="FileOps_5"/> +      <menuitem name="FilePrintPreviewMenu" action="FilePrintPreview"/> +      <menuitem name="FilePrintMenu" action="FilePrint"/> +      <placeholder name="FileRecentsPlaceholder"> +        <separator/> +      </placeholder> +      <separator/> +      <menuitem name="FileCloseMenu" action="FileClose"/> +      <menuitem name="FileQuitMenu" action="FileQuit"/> +    </menu> + +    <menu name="EditMenu" action="Edit"> +      <menuitem name="EditUndoMenu" action="EditUndo"/> +      <menuitem name="EditRedoMenu" action="EditRedo"/> +      <separator/> +      <menuitem name="EditCutMenu" action="EditCut"/> +      <menuitem name="EditCopyMenu" action="EditCopy"/> +      <menuitem name="EditPasteMenu" action="EditPaste"/> +      <menuitem name="EditDeleteMenu" action="EditDelete"/> +      <placeholder name="EditOps_1" />  +      <separator/> +      <placeholder name="EditOps_2" />  +      <menuitem name="EditSelectAllMenu" action="EditSelectAll"/> +      <placeholder name="EditOps_3" /> +      <separator/> +      <placeholder name="EditOps_4" /> +      <separator/> +      <placeholder name="EditOps_5" /> +      <separator/> +      <placeholder name="EditOps_6" /> +      <separator/> +      <menuitem name="EditPreferencesMenu" action="EditPreferences"/> +    </menu> + +    <menu name="ViewMenu" action="View"> +      <menuitem name="ViewToolbarMenu" action="ViewToolbar"/> +      <menuitem name="ViewStatusbarMenu" action="ViewStatusbar"/> +      <menuitem name="ViewSidePaneMenu" action="ViewSidePane"/> +      <menuitem name="ViewBottomPaneMenu" action="ViewBottomPane"/> +      <separator/> +      <menuitem name="ViewFullscreenMenu" action="ViewFullscreen"/> +      <separator/> +      <menu name="ViewHighlightModeMenu" action="ViewHighlightMode"> +        <placeholder name="LanguagesMenuPlaceholder"> +        </placeholder> +      </menu> +    </menu> + +    <menu name="SearchMenu" action="Search"> +      <menuitem name="SearchFindMenu" action="SearchFind"/> +      <menuitem name="SearchFindNextMenu" action="SearchFindNext"/> +      <menuitem name="SearchFindPreviousMenu" action="SearchFindPrevious"/> +      <menuitem name="SearchIncrementalSearchMenu" action="SearchIncrementalSearch"/> +      <placeholder name="SearchOps_1" /> +      <separator/> +      <placeholder name="SearchOps_2" /> +      <separator/> +      <menuitem name="SearchReplaceMenu" action="SearchReplace"/> +      <placeholder name="SearchOps_3" /> +      <separator/> +      <placeholder name="SearchOps_4" /> +      <separator/> +      <menuitem name="SearchClearHighlight" action="SearchClearHighlight"/>       +      <placeholder name="SearchOps_5" /> +      <separator/> +      <placeholder name="SearchOps_6" /> +      <separator/> +      <menuitem name="SearchGoToLineMenu" action="SearchGoToLine"/>       +      <placeholder name="SearchOps_7" /> +      <separator/> +      <placeholder name="SearchOps_8" /> +    </menu> + +    <menu name="ToolsMenu" action="Tools"> +      <placeholder name="ToolsOps_1" /> +      <separator/> +      <placeholder name="ToolsOps_2" /> +      <separator/> +      <placeholder name="ToolsOps_3" /> +      <separator/> +      <placeholder name="ToolsOps_4" /> +      <separator/> +      <placeholder name="ToolsOps_5" /> +    </menu> + +    <placeholder name="ExtraMenu_1" /> + +    <menu name="DocumentsMenu" action="Documents"> +      <menuitem action="FileSaveAll" /> +      <menuitem action="FileCloseAll" /> +      <placeholder name="DocumentsOps_1" /> +      <separator/> +      <placeholder name="DocumentsOps_2" /> +      <separator/> +      <placeholder name="DocumentsOps_3" /> +      <menuitem action="DocumentsPreviousDocument" />       +      <menuitem action="DocumentsNextDocument" /> +      <separator/> +      <menuitem action="DocumentsMoveToNewWindow"/> +      <placeholder name="DocumentsListPlaceholder"> +        <separator/> +      </placeholder> +    </menu> + +    <menu name="HelpMenu" action="Help"> +      <menuitem name="HelpContentsMenu" action="HelpContents"/> +      <menuitem name="HelpAboutMenu" action="HelpAbout"/> +    </menu> +  </menubar> + +  <toolbar name="ToolBar"> +    <toolitem action="FileNew"/> +    <toolitem action="FileSave"/> +    <separator/> +    <toolitem action="FilePrint"/> +    <separator/> +    <toolitem action="EditUndo"/> +    <toolitem action="EditRedo"/> +    <separator/> +    <toolitem action="EditCut"/> +    <toolitem action="EditCopy"/> +    <toolitem action="EditPaste"/> +    <separator/> +    <toolitem action="SearchFind"/> +    <toolitem action="SearchReplace"/> +  </toolbar> + +  <toolbar name="FullscreenToolBar"> +    <toolitem action="FileNew"/> +    <toolitem action="FileSave"/> +    <separator/> +    <toolitem action="FilePrint"/> +    <separator/> +    <toolitem action="EditUndo"/> +    <toolitem action="EditRedo"/> +    <separator/> +    <toolitem action="EditCut"/> +    <toolitem action="EditCopy"/> +    <toolitem action="EditPaste"/> +    <separator/> +    <toolitem action="SearchFind"/> +    <toolitem action="SearchReplace"/> +    <separator expand="true"/> +    <toolitem action="LeaveFullscreen"/> +  </toolbar> + +  <popup name="NotebookPopup" action="NotebookPopupAction"> +    <menuitem action="DocumentsMoveToNewWindow"/> +    <separator/> +    <menuitem action="FileSave"/> +    <menuitem action="FileSaveAs"/> +    <separator/> +    <menuitem action="FilePrint"/> +    <separator/> +    <placeholder name="NotebookPupupOps_1"/>    +    <separator/> +    <menuitem name="FileCloseMenu" action="FileClose"/> +  </popup> + +</ui> diff --git a/gedit/gedit-utils.c b/gedit/gedit-utils.c new file mode 100755 index 00000000..c34b746f --- /dev/null +++ b/gedit/gedit-utils.c @@ -0,0 +1,1546 @@ +/* + * gedit-utils.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi  + * Copyright (C) 2003-2005 Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gedit-utils.h" + +#include "gedit-document.h" +#include "gedit-prefs-manager.h" +#include "gedit-debug.h" + +/* For the workspace/viewport stuff */ +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#endif + +#define STDIN_DELAY_MICROSECONDS 100000 + +/* Returns true if uri is a file: uri and is not a chained uri */ +gboolean +gedit_utils_uri_has_file_scheme (const gchar *uri) +{ +	GFile *gfile; +	gboolean res; + +	gfile = g_file_new_for_uri (uri); +	res = g_file_has_uri_scheme (gfile, "file"); +	 +	g_object_unref (gfile); +	return res; +} + +/* FIXME: we should check for chained URIs */ +gboolean +gedit_utils_uri_has_writable_scheme (const gchar *uri) +{ +	GFile *gfile; +	gchar *scheme; +	GSList *writable_schemes; +	gboolean res; + +	gfile = g_file_new_for_uri (uri); +	scheme = g_file_get_uri_scheme (gfile); + +	g_return_val_if_fail (scheme != NULL, FALSE); + +	g_object_unref (gfile); + +	writable_schemes = gedit_prefs_manager_get_writable_vfs_schemes (); + +	/* CHECK: should we use g_ascii_strcasecmp? - Paolo (Nov 6, 2005) */ +	res = (g_slist_find_custom (writable_schemes, +				    scheme, +				    (GCompareFunc)strcmp) != NULL); + +	g_slist_foreach (writable_schemes, (GFunc)g_free, NULL); +	g_slist_free (writable_schemes); + +	g_free (scheme); + +	return res; +} + +static void +widget_get_origin (GtkWidget *widget, gint *x, gint *y) + +{ +	GdkWindow *window; + +	window = gtk_widget_get_window (widget); +	gdk_window_get_origin (window, x, y); +} + +void +gedit_utils_menu_position_under_widget (GtkMenu  *menu, +					gint     *x, +					gint     *y, +					gboolean *push_in, +					gpointer  user_data) +{ +	GtkWidget *widget; +	GtkRequisition requisition; + +	widget = GTK_WIDGET (user_data); +	widget_get_origin (widget, x, y); + +	gtk_widget_size_request (GTK_WIDGET (menu), &requisition); + +	if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) +	{ +		*x += widget->allocation.x + widget->allocation.width - requisition.width; +	} +	else +	{ +		*x += widget->allocation.x; +	} + +	*y += widget->allocation.y + widget->allocation.height; + +	*push_in = TRUE; +} + +void +gedit_utils_menu_position_under_tree_view (GtkMenu  *menu, +					   gint     *x, +					   gint     *y, +					   gboolean *push_in, +					   gpointer  user_data) +{ +	GtkTreeView *tree = GTK_TREE_VIEW (user_data); +	GtkTreeModel *model; +	GtkTreeSelection *selection; +	GtkTreeIter iter; +	 +	model = gtk_tree_view_get_model (tree); +	g_return_if_fail (model != NULL); + +	selection = gtk_tree_view_get_selection (tree); +	g_return_if_fail (selection != NULL); + +	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) +	{ +		GtkTreePath *path; +		GdkRectangle rect; + +		widget_get_origin (GTK_WIDGET (tree), x, y); +			 +		path = gtk_tree_model_get_path (model, &iter); +		gtk_tree_view_get_cell_area (tree, path, +					     gtk_tree_view_get_column (tree, 0), /* FIXME 0 for RTL ? */ +					     &rect); +		gtk_tree_path_free (path); +		 +		*x += rect.x; +		*y += rect.y + rect.height; +		 +		if (gtk_widget_get_direction (GTK_WIDGET (tree)) == GTK_TEXT_DIR_RTL) +		{ +			GtkRequisition requisition; +			gtk_widget_size_request (GTK_WIDGET (menu), &requisition); +			*x += rect.width - requisition.width; +		} +	} +	else +	{ +		/* no selection -> regular "under widget" positioning */ +		gedit_utils_menu_position_under_widget (menu, +							x, y, push_in, +							tree); +	} +} + +/* FIXME: remove this with gtk 2.12, it has gdk_color_to_string */ +gchar *  +gedit_gdk_color_to_string (GdkColor color) +{ +	return g_strdup_printf ("#%04x%04x%04x", +				color.red,  +				color.green, +				color.blue); +} + +GtkWidget * +gedit_gtk_button_new_with_stock_icon (const gchar *label, +				      const gchar *stock_id) +{ +	GtkWidget *button; + +	button = gtk_button_new_with_mnemonic (label); +	gtk_button_set_image (GTK_BUTTON (button), +			      gtk_image_new_from_stock (stock_id, +							GTK_ICON_SIZE_BUTTON)); + +        return button; +} + +GtkWidget * +gedit_dialog_add_button (GtkDialog   *dialog, +			 const gchar *text, +			 const gchar *stock_id, +			 gint         response_id) +{ +	GtkWidget *button; + +	g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); +	g_return_val_if_fail (text != NULL, NULL); +	g_return_val_if_fail (stock_id != NULL, NULL); + +	button = gedit_gtk_button_new_with_stock_icon (text, stock_id); +	g_return_val_if_fail (button != NULL, NULL); + +	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + +	gtk_widget_show (button); + +	gtk_dialog_add_action_widget (dialog, button, response_id);	 + +	return button; +} + +/* + * n: len of the string in bytes + */ +gboolean  +g_utf8_caselessnmatch (const char *s1, const char *s2, gssize n1, gssize n2) +{ +	gchar *casefold; +	gchar *normalized_s1; +      	gchar *normalized_s2; +	gint len_s1; +	gint len_s2; +	gboolean ret = FALSE; + +	g_return_val_if_fail (s1 != NULL, FALSE); +	g_return_val_if_fail (s2 != NULL, FALSE); +	g_return_val_if_fail (n1 > 0, FALSE); +	g_return_val_if_fail (n2 > 0, FALSE); + +	casefold = g_utf8_casefold (s1, n1); +	normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); +	g_free (casefold); + +	casefold = g_utf8_casefold (s2, n2); +	normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); +	g_free (casefold); + +	len_s1 = strlen (normalized_s1); +	len_s2 = strlen (normalized_s2); + +	if (len_s1 < len_s2) +		goto finally_2; + +	ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0); +	 +finally_2: +	g_free (normalized_s1); +	g_free (normalized_s2);	 + +	return ret; +} + +/** + * 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 + * Description : This function sets up name and description + * for a specified gtk widget. + */ +void +gedit_utils_set_atk_name_description (GtkWidget *widget,  +				      const gchar *name, +				      const gchar *description) +{ +	AtkObject *aobj; + +	aobj = gtk_widget_get_accessible (widget); + +	if (!(GTK_IS_ACCESSIBLE (aobj))) +		return; + +	if(name) +		atk_object_set_name (aobj, name); + +	if(description) +		atk_object_set_description (aobj, description); +} + +/** + * gedit_set_atk__relation + * @obj1,@obj2 : specified widgets. + * @rel_type : the type of relation to set up. + * Description : This function establishes atk relation + * between 2 specified widgets. + */ +void +gedit_utils_set_atk_relation (GtkWidget *obj1,  +			      GtkWidget *obj2,  +			      AtkRelationType rel_type ) +{ +	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)); +} + +gboolean +gedit_utils_uri_exists (const gchar* text_uri) +{ +	GFile *gfile; +	gboolean res; +		 +	g_return_val_if_fail (text_uri != NULL, FALSE); +	 +	gedit_debug_message (DEBUG_UTILS, "text_uri: %s", text_uri); + +	gfile = g_file_new_for_uri (text_uri); +	res = g_file_query_exists (gfile, NULL); + +	g_object_unref (gfile); + +	gedit_debug_message (DEBUG_UTILS, res ? "TRUE" : "FALSE"); + +	return res; +} + +gchar * +gedit_utils_escape_search_text (const gchar* text) +{ +	GString *str; +	gint length; +	const gchar *p; + 	const gchar *end; + +	if (text == NULL) +		return NULL; + +	gedit_debug_message (DEBUG_SEARCH, "Text: %s", text); + +    	length = strlen (text); + +	/* no escape when typing. +	 * The short circuit works only for ascii, but we only +	 * care about not escaping a single '\' */ +	if (length == 1) +		return g_strdup (text); + +	str = g_string_new (""); + +	p = text; +  	end = text + length; + +  	while (p != end) +    	{ +      		const gchar *next; +      		next = g_utf8_next_char (p); + +		switch (*p) +        	{ +       			case '\n': +          			g_string_append (str, "\\n"); +          			break; +			case '\r': +          			g_string_append (str, "\\r"); +          			break; +			case '\t': +          			g_string_append (str, "\\t"); +          			break; +			case '\\': +          			g_string_append (str, "\\\\"); +          			break; +        		default: +          			g_string_append_len (str, p, next - p); +          			break; +        	} + +      		p = next; +    	} + +	return g_string_free (str, FALSE); +} + +gchar * +gedit_utils_unescape_search_text (const gchar *text) +{ +	GString *str; +	gint length; +	gboolean drop_prev = FALSE; +	const gchar *cur; +	const gchar *end; +	const gchar *prev; +	 +	if (text == NULL) +		return NULL; + +	length = strlen (text); + +	str = g_string_new (""); + +	cur = text; +	end = text + length; +	prev = NULL; +	 +	while (cur != end)  +	{ +		const gchar *next; +		next = g_utf8_next_char (cur); + +		if (prev && (*prev == '\\'))  +		{ +			switch (*cur)  +			{ +				case 'n': +					str = g_string_append (str, "\n"); +				break; +				case 'r': +					str = g_string_append (str, "\r"); +				break; +				case 't': +					str = g_string_append (str, "\t"); +				break; +				case '\\': +					str = g_string_append (str, "\\"); +					drop_prev = TRUE; +				break; +				default: +					str = g_string_append (str, "\\"); +					str = g_string_append_len (str, cur, next - cur); +				break; +			} +		}  +		else if (*cur != '\\')  +		{ +			str = g_string_append_len (str, cur, next - cur); +		}  +		else if ((next == end) && (*cur == '\\'))  +		{ +			str = g_string_append (str, "\\"); +		} +		 +		if (!drop_prev) +		{ +			prev = cur; +		} +		else  +		{ +			prev = NULL; +			drop_prev = FALSE; +		} + +		cur = next; +	} + +	return g_string_free (str, FALSE); +} + +void  +gedit_warning (GtkWindow *parent, const gchar *format, ...) +{ +	va_list         args; +	gchar          *str; +	GtkWidget      *dialog; +	GtkWindowGroup *wg = NULL; +	 +	g_return_if_fail (format != NULL); + +	if (parent != NULL) +		wg = gtk_window_get_group (parent); +		 +	va_start (args, format); +	str = g_strdup_vprintf (format, args); +	va_end (args); + +	dialog = gtk_message_dialog_new_with_markup ( +			parent, +			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, +		   	GTK_MESSAGE_ERROR, +		   	GTK_BUTTONS_OK, +			"%s", str); + +	g_free (str); + +	if (wg != NULL) +		gtk_window_group_add_window (wg, GTK_WINDOW (dialog)); +		 +	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + +	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + +	g_signal_connect (G_OBJECT (dialog), +			  "response", +			  G_CALLBACK (gtk_widget_destroy), +			  NULL); +			   +	gtk_widget_show (dialog); +} + +/* + * Doubles underscore to avoid spurious menu accels. + */ +gchar *  +gedit_utils_escape_underscores (const gchar* text, +				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); +} + +/* the following functions are taken from eel */ + +static gchar * +gedit_utils_str_truncate (const gchar *string, +			  guint        truncate_length, +			  gboolean middle) +{ +	GString     *truncated; +	guint        length; +	guint        n_chars; +	guint        num_left_chars; +	guint        right_offset; +	guint        delimiter_length; +	const gchar *delimiter = "\342\200\246"; + +	g_return_val_if_fail (string != NULL, NULL); + +	length = strlen (string); + +	g_return_val_if_fail (g_utf8_validate (string, length, NULL), NULL); + +	/* It doesnt make sense to truncate strings to less than +	 * the size of the delimiter plus 2 characters (one on each +	 * side) +	 */ +	delimiter_length = g_utf8_strlen (delimiter, -1); +	if (truncate_length < (delimiter_length + 2)) { +		return g_strdup (string); +	} + +	n_chars = g_utf8_strlen (string, length); + +	/* Make sure the string is not already small enough. */ +	if (n_chars <= truncate_length) { +		return g_strdup (string); +	} + +	/* Find the 'middle' where the truncation will occur. */ +	if (middle) +	{ +		num_left_chars = (truncate_length - delimiter_length) / 2; +		right_offset = n_chars - truncate_length + num_left_chars + delimiter_length; + +		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); +	} +	 +	return g_string_free (truncated, FALSE); +} + +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); +} + +gchar * +gedit_utils_make_valid_utf8 (const char *name) +{ +	GString *string; +	const char *remainder, *invalid; +	int remaining_bytes, valid_bytes; + +	g_return_val_if_fail (name != NULL, NULL); + +	string = NULL; +	remainder = name; +	remaining_bytes = strlen (name); + +	while (remaining_bytes != 0) { +		if (g_utf8_validate (remainder, remaining_bytes, &invalid)) { +			break; +		} +		valid_bytes = invalid - remainder; + +		if (string == NULL) { +			string = g_string_sized_new (remaining_bytes); +		} +		g_string_append_len (string, remainder, valid_bytes); +		/* append U+FFFD REPLACEMENT CHARACTER */ +		g_string_append (string, "\357\277\275"); + +		remaining_bytes -= valid_bytes + 1; +		remainder = invalid + 1; +	} + +	if (string == NULL) { +		return g_strdup (name); +	} + +	g_string_append (string, remainder); +	 +	g_assert (g_utf8_validate (string->str, -1, NULL)); + +	return g_string_free (string, FALSE); +} + +/* Note that this function replace home dir with ~ */ +gchar * +gedit_utils_uri_get_dirname (const gchar *uri) +{ +	gchar *res; +	gchar *str; + +	g_return_val_if_fail (uri != NULL, NULL); + +	/* CHECK: does it work with uri chaining? - Paolo */ +	str = g_path_get_dirname (uri); +	g_return_val_if_fail (str != NULL, g_strdup (".")); + +	if ((strlen (str) == 1) && (*str == '.')) +	{ +		g_free (str); +		 +		return NULL; +	} + +	res = gedit_utils_replace_home_dir_with_tilde (str); + +	g_free (str); +	 +	return res; +} + +/** + * gedit_utils_location_get_dirname_for_display + * @file: the location + * + * 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 ~. + * + * Returns: a string to display the dirname + */ +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; +		gchar *path; + +		mount_name = g_mount_get_name (mount); +		g_object_unref (mount); + +		/* obtain the "path" patrt of the uri */ +		if (gedit_utils_decode_uri (uri, +					    NULL, NULL, +					    NULL, NULL, +					    &path)) +		{ +			gchar *dirname; +			 +			dirname = gedit_utils_uri_get_dirname (path); +			res = g_strdup_printf ("%s %s", mount_name, dirname); + +			g_free (path); +			g_free (dirname); +			g_free (mount_name); +		} +		else +		{ +			res = mount_name; +		} +	} +	else +	{ +		/* fallback for local files or uris without mounts */ +		res = gedit_utils_uri_get_dirname (uri); +	} + +	g_free (uri); + +	return res; +} + +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); +		 +		return g_strdup ("~"); +	} + +	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); +		 +		return res;		 +	} + +	g_free (home); + +	return g_strdup (uri); +} + +/* the following two functions are courtesy of galeon */ + +/** + * gedit_utils_get_current_workspace: Get the current workspace + * + * 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) +{ +#ifdef GDK_WINDOWING_X11 +	GdkWindow *root_win; +	GdkDisplay *display; +	Atom type; +	gint format; +	gulong nitems; +	gulong bytes_after; +	guint *current_desktop; +	gint err, result; +	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); + +	gdk_error_trap_push (); +	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) ¤t_desktop); +	err = gdk_error_trap_pop (); + +	if (err != Success || result != Success) +		return ret; + +	if (type == XA_CARDINAL && format == 32 && nitems > 0) +		ret = current_desktop[0]; + +	XFree (current_desktop); +	return ret; +#else +	/* FIXME: on mac etc proably there are native APIs +	 * to get the current workspace etc */ +	return 0; +#endif +} + +/** + * gedit_utils_get_window_workspace: Get the workspace the window is on + * + * This function gets the workspace that the #GtkWindow is visible on, + * it returns GEDIT_ALL_WORKSPACES if the window is sticky, or if + * the window manager doesn support this function + */ +guint +gedit_utils_get_window_workspace (GtkWindow *gtkwindow) +{ +#ifdef GDK_WINDOWING_X11 +	GdkWindow *window; +	GdkDisplay *display; +	Atom type; +	gint format; +	gulong nitems; +	gulong bytes_after; +	guint *workspace; +	gint err, result; +	guint ret = GEDIT_ALL_WORKSPACES; + +	g_return_val_if_fail (GTK_IS_WINDOW (gtkwindow), 0); +	g_return_val_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (gtkwindow)), 0); + +	window = gtk_widget_get_window (GTK_WIDGET (gtkwindow)); +	display = gdk_drawable_get_display (window); + +	gdk_error_trap_push (); +	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); +	err = gdk_error_trap_pop (); + +	if (err != Success || result != Success) +		return ret; + +	if (type == XA_CARDINAL && format == 32 && nitems > 0) +		ret = workspace[0]; + +	XFree (workspace); +	return ret; +#else +	/* FIXME: on mac etc proably there are native APIs +	 * to get the current workspace etc */ +	return 0; +#endif +} + +/** + * gedit_utils_get_current_viewport: Get the current viewport origin + * + * 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) +{ +#ifdef GDK_WINDOWING_X11 +	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); + +	gdk_error_trap_push (); +	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); +	err = gdk_error_trap_pop (); + +	if (err != Success || result != Success) +		return; + +	if (type != XA_CARDINAL || format != 32 || nitems < 2) +	{ +		XFree (coordinates); +		return; +	} + +	*x = coordinates[0]; +	*y = coordinates[1]; +	XFree (coordinates); +#else +	/* FIXME: on mac etc proably there are native APIs +	 * to get the current workspace etc */ +	*x = 0; +	*y = 0; +#endif +} + +void +gedit_utils_activate_url (GtkAboutDialog *about, +			  const gchar    *url, +			  gpointer        data) +{ +	gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (about)), url, GDK_CURRENT_TIME, NULL); +} + +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; + +	if (!is_valid_scheme_character (*p)) { +		return FALSE; +	} + +	do { +		p++; +	} while (is_valid_scheme_character (*p)); + +	return *p == ':'; +} + +gboolean +gedit_utils_is_valid_uri (const gchar *uri) +{ +	const guchar *p; + +	if (uri == NULL) +		return FALSE; + +	if (!has_valid_scheme (uri)) +		return FALSE; + +	/* We expect to have a fully valid set of characters */ +	for (p = (const guchar *)uri; *p; p++) { +		if (*p == '%') +		{ +			++p; +			if (!g_ascii_isxdigit (*p)) +				return FALSE; + +			++p;		 +			if (!g_ascii_isxdigit (*p)) +				return FALSE; +		} +		else +		{ +			if (*p <= 32 || *p >= 128) +				return FALSE; +		} +	} + +	return TRUE; +} + +static GtkWidget * +handle_builder_error (const gchar *message, ...) +{ +	GtkWidget *label; +	gchar *msg; +	gchar *msg_plain; +	va_list args; + +	va_start (args, message); +	msg_plain = g_strdup_vprintf (message, args); +	va_end (args); + +	label = gtk_label_new (NULL); +	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + +	msg = g_strconcat ("<span size=\"large\" weight=\"bold\">", +			   msg_plain, "</span>\n\n", +			   _("Please check your installation."), +			   NULL); + +	gtk_label_set_markup (GTK_LABEL (label), msg); + +	g_free (msg_plain); +	g_free (msg); + +	gtk_misc_set_padding (GTK_MISC (label), 5, 5); + +	return label; +} + +/** + * 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. + */ +gboolean +gedit_utils_get_ui_objects (const gchar  *filename, +			    gchar       **root_objects, +			    GtkWidget   **error_widget, +			    const gchar  *object_name, +			    ...) +{ + +	GtkBuilder *builder; +	va_list args; +	const gchar *name; +	GError *error = NULL; +	gchar *filename_markup; +	gboolean ret = TRUE; + +	g_return_val_if_fail (filename != NULL, FALSE); +	g_return_val_if_fail (error_widget != NULL, FALSE); +	g_return_val_if_fail (object_name != NULL, FALSE); + +	filename_markup = g_markup_printf_escaped ("<i>%s</i>", filename); +	*error_widget = NULL; + +	builder = gtk_builder_new (); +	 +	if (root_objects != NULL) +	{ +		gtk_builder_add_objects_from_file (builder,  +						   filename,  +						   root_objects,  +						   &error); +	} +	else +	{ +		gtk_builder_add_from_file (builder, +					   filename, +					   &error); +	} + +	if (error != NULL) +	{ +		*error_widget = handle_builder_error (_("Unable to open UI file %s. Error: %s"), +						      filename_markup, +						      error->message); +		g_error_free (error); +		g_free (filename_markup); +		g_object_unref (builder); + +		return FALSE; +	} + +	va_start (args, object_name); +	for (name = object_name; name; name = va_arg (args, const gchar *) ) +	{ +		GObject **gobj; + +		gobj = va_arg (args, GObject **); +		*gobj = gtk_builder_get_object (builder, name); + +		if (!*gobj) +		{ +			*error_widget = handle_builder_error (_("Unable to find the object '%s' inside file %s."),  +							      name,  +							      filename_markup), +			ret = FALSE; +			break; +		} + +		/* 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); +				} +			} +		} +	} +	va_end (args); + +	g_free (filename_markup); +	g_object_unref (builder); + +	return ret; +} + +gchar * +gedit_utils_make_canonical_uri_from_shell_arg (const gchar *str) +{	 +	GFile *gfile; +	gchar *uri; + +	g_return_val_if_fail (str != NULL, NULL); +	g_return_val_if_fail (*str != '\0', NULL); +	 +	/* Note for the future:  +	 * FIXME: is still still relevant? +	 * +	 * <federico> paolo: and flame whoever tells  +	 * you that file:///mate/test_files/hëllò  +	 * doesn't work --- that's not a valid URI +	 * +	 * <paolo> federico: well, another solution that  +	 * does not requires patch to _from_shell_args  +	 * is to check that the string returned by it  +	 * contains only ASCII chars +	 * <federico> paolo: hmmmm, isn't there  +	 * mate_vfs_is_uri_valid() or something? +	 * <paolo>: I will use gedit_utils_is_valid_uri () +	 * +	 */ + +	gfile = g_file_new_for_commandline_arg (str); +	uri = g_file_get_uri (gfile); +	g_object_unref (gfile); + +	if (gedit_utils_is_valid_uri (uri)) +		return uri; +	 +	g_free (uri); +	return NULL; +} + +/** + * gedit_utils_file_has_parent: + * @gfile: the GFile to check the parent for + * + * Return TRUE if the specified gfile has a parent (is not the root), FALSE + * otherwise + */ +gboolean +gedit_utils_file_has_parent (GFile *gfile) +{ +	GFile *parent; +	gboolean ret; +	 +	parent = g_file_get_parent (gfile); +	ret = parent != NULL; +	 +	if (parent) +		g_object_unref (parent); +	 +	return ret; +} + +/** + * gedit_utils_basename_for_display: + * @uri: uri for which the basename should be displayed + * + * Return the basename of a file suitable for display to users. + */ +gchar * +gedit_utils_basename_for_display (gchar const *uri) +{ +	gchar *name; +	GFile *gfile; +	gchar *hn; + +	g_return_val_if_fail (uri != NULL, NULL); + +	gfile = g_file_new_for_uri (uri); + +	/* First, try to query the display name, but only on local files */ +	if (g_file_has_uri_scheme (gfile, "file")) +	{ +		GFileInfo *info; +		info = g_file_query_info (gfile, +					  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,  +					  G_FILE_QUERY_INFO_NONE,  +					  NULL,  +					  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; +		 +			local_path = g_file_get_path (gfile); +			name = g_filename_display_basename (local_path); +			g_free (local_path); +		} +	} +	else if (gedit_utils_file_has_parent (gfile) || !gedit_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) +	{ +		/* 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 */ +		gchar *parse_name; +		gchar *base; + +		parse_name = g_file_get_parse_name (gfile); +		base = g_filename_display_basename (parse_name); +		name = g_uri_unescape_string (base, NULL); +		 +		g_free (base);		 +		g_free (parse_name); +	} +	else +	{ +		/* display '/ on <host>' using the decoded host */ +		gchar *hn_utf8; + +		if  (hn != NULL) +			hn_utf8 = gedit_utils_make_valid_utf8 (hn); +		else +			/* we should never get here */ +			hn_utf8 = g_strdup ("?"); + +		/* Translators: '/ on <remote-share>' */ +		name = g_strdup_printf (_("/ on %s"), hn_utf8); + +		g_free (hn_utf8); +		g_free (hn); +	} + +	g_object_unref (gfile); + +	return name; +} + +/** + * gedit_utils_uri_for_display: + * @uri: uri to be displayed. + * + * Filter, modify, unescape and change @uri to make it appropriate + * for display to users. + *  + * This function is a convenient wrapper for g_file_get_parse_name + * + * Return value: a string which represents @uri and can be displayed. + */ +gchar * +gedit_utils_uri_for_display (const gchar *uri) +{ +	GFile *gfile; +	gchar *parse_name; + +	gfile = g_file_new_for_uri (uri); +	parse_name = g_file_get_parse_name (gfile); +	g_object_unref (gfile); + +	return parse_name; +} + +/** + * gedit_utils_drop_get_uris: + * @selection_data: the #GtkSelectionData from drag_data_received + * @info: the info from drag_data_received + * + * Create a list of valid uri's from a uri-list drop. + *  + * Return value: a string array which will hold the uris or NULL if there  + *		 were no valid uris. g_strfreev should be used when the  + *		 string array is no longer used + */ +gchar ** +gedit_utils_drop_get_uris (GtkSelectionData *selection_data) +{ +	gchar **uris; +	gint i; +	gint p = 0; +	gchar **uri_list; + +	uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); +	uri_list = g_new0(gchar *, g_strv_length (uris) + 1); + +	for (i = 0; uris[i] != NULL; i++) +	{ +		gchar *uri; +		 +		uri = gedit_utils_make_canonical_uri_from_shell_arg (uris[i]); +		 +		/* Silently ignore malformed URI/filename */ +		if (uri != NULL) +			uri_list[p++] = uri; +	} + +	if (*uri_list == NULL) +	{ +		g_free(uri_list); +		return NULL; +	} + +	return uri_list; +} + +static void +null_ptr (gchar **ptr) +{ +	if (ptr) +		*ptr = NULL; +} + +/** + * gedit_utils_decode_uri: + * @uri: the uri to decode + * @scheme: return value pointer for the uri's scheme (e.g. http, sftp, ...) + * @user: return value pointer for the uri user info + * @port: return value pointer for the uri port + * @host: return value pointer for the uri host + * @path: return value pointer for the uri path + * + * 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 + * NULL to ignore certain parts of the uri. If the function returns TRUE, then + * all return value pointers should be freed using g_free + *  + * Return value: TRUE if the uri could be properly decoded, FALSE otherwise. + */ +gboolean +gedit_utils_decode_uri (const gchar *uri, +			gchar **scheme, +			gchar **user, +			gchar **host, +			gchar **port, +			gchar **path +) +{ +	/* Largely copied from glib/gio/gdummyfile.c:_g_decode_uri. This  +	 * 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 ] +	 */  + +	p = uri; +	 +	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 == '.')) +			return FALSE; +	} +	 +	if (scheme) +	{ +		*scheme = g_malloc (p - uri); +		out = *scheme; +	 +		for (in = uri; in < p - 1; in++) +			*out++ = g_ascii_tolower (*in); +			 +		*out = '\0'; +	} +	 +	hier_part_start = p; +	hier_part_end = p + strlen (p); +	 +	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; +		 +		authority_start = hier_part_start + 2; +		/* authority is always followed by / or nothing */ +		authority_end = memchr (authority_start, '/', hier_part_end - authority_start); +		 +		if (authority_end == NULL) +			authority_end = hier_part_end; + +		/* 3.2: +		 * authority = [ userinfo "@" ] host [ ":" port ] +		 */ + +		userinfo_end = memchr (authority_start, '@', authority_end - authority_start); +		 +		if (userinfo_end) +		{ +			userinfo_start = authority_start; +			 +			if (user) +				*user = g_uri_unescape_segment (userinfo_start, userinfo_end, NULL); +			 +			if (user && *user == NULL) +			{ +				if (scheme) +					g_free (*scheme); + +				return FALSE; +			} +	 +			host_start = userinfo_end + 1; +		} +		else +			host_start = authority_start; + +		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 +			host_end = authority_end; + +		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, "/"); +	 +	return TRUE; +} diff --git a/gedit/gedit-utils.h b/gedit/gedit-utils.h new file mode 100755 index 00000000..ddf1d9c5 --- /dev/null +++ b/gedit/gedit-utils.h @@ -0,0 +1,162 @@ +/* + * gedit-utils.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002 - 2005 Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_UTILS_H__ +#define __GEDIT_UTILS_H__ + +#include <glib.h> +#include <gtk/gtk.h> +#include <atk/atk.h> +#include <gedit/gedit-encodings.h> + +G_BEGIN_DECLS + +/* useful macro */ +#define GBOOLEAN_TO_POINTER(i) (GINT_TO_POINTER ((i) ? 2 : 1)) +#define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((GPOINTER_TO_INT(i) == 2) ? TRUE : FALSE)) + +#define IS_VALID_BOOLEAN(v) (((v == TRUE) || (v == FALSE)) ? TRUE : FALSE) + +enum { GEDIT_ALL_WORKSPACES = 0xffffffff }; + +gboolean	 gedit_utils_uri_has_writable_scheme	(const gchar *uri); +gboolean	 gedit_utils_uri_has_file_scheme	(const gchar *uri); + +void		 gedit_utils_menu_position_under_widget (GtkMenu  *menu, +							 gint     *x, +							 gint     *y, +							 gboolean *push_in, +							 gpointer  user_data); + +void		 gedit_utils_menu_position_under_tree_view +							(GtkMenu  *menu, +							 gint     *x, +							 gint     *y, +							 gboolean *push_in, +							 gpointer  user_data); + +gchar		*gedit_gdk_color_to_string		(GdkColor color); + +GtkWidget	*gedit_gtk_button_new_with_stock_icon	(const gchar *label, +							 const gchar *stock_id); + +GtkWidget	*gedit_dialog_add_button		(GtkDialog   *dialog, +							 const gchar *text, +							 const gchar *stock_id,  +							 gint         response_id); + +gchar		*gedit_utils_escape_underscores		(const gchar *text, +							 gssize       length); + +gchar		*gedit_utils_str_middle_truncate	(const gchar *string,  +							 guint        truncate_length); + +gchar		*gedit_utils_str_end_truncate		(const gchar *string, +							 guint        truncate_length); + +gboolean	 g_utf8_caselessnmatch			(const char *s1, +							 const char *s2, +							 gssize n1, +							 gssize n2); + +void		 gedit_utils_set_atk_name_description	(GtkWidget  *widget, +							 const gchar *name, +							 const gchar *description); + +void		 gedit_utils_set_atk_relation		(GtkWidget       *obj1, +							 GtkWidget       *obj2, +							 AtkRelationType  rel_type); + +gboolean	 gedit_utils_uri_exists			(const gchar* text_uri); + +gchar		*gedit_utils_escape_search_text		(const gchar *text); + +gchar		*gedit_utils_unescape_search_text	(const gchar *text); + +void		 gedit_warning				(GtkWindow  *parent, +							 const gchar *format, +							 ...) G_GNUC_PRINTF(2, 3); + +gchar		*gedit_utils_make_valid_utf8		(const char *name); + +/* Note that this function replace home dir with ~ */ +gchar		*gedit_utils_uri_get_dirname		(const char *uri); + +gchar		*gedit_utils_location_get_dirname_for_display +							(GFile *location); + +gchar		*gedit_utils_replace_home_dir_with_tilde (const gchar *uri); + +guint		 gedit_utils_get_current_workspace	(GdkScreen *screen); + +guint		 gedit_utils_get_window_workspace	(GtkWindow *gtkwindow); + +void		 gedit_utils_get_current_viewport	(GdkScreen    *screen, +							 gint         *x, +							 gint         *y); + +void		 gedit_utils_activate_url		(GtkAboutDialog *about, +							 const gchar    *url, +							 gpointer        data); + +gboolean	 gedit_utils_is_valid_uri		(const gchar *uri); + +gboolean	 gedit_utils_get_ui_objects		(const gchar  *filename, +                                                         gchar       **root_objects, +							 GtkWidget   **error_widget, +							 const gchar  *object_name, +							 ...) G_GNUC_NULL_TERMINATED; + +gboolean         gedit_utils_file_has_parent            (GFile *gfile); + +/* Return NULL if str is not a valid URI and/or filename */ +gchar		*gedit_utils_make_canonical_uri_from_shell_arg +							(const gchar *str); +		 +gchar		*gedit_utils_uri_for_display 	        (const gchar *uri); +gchar           *gedit_utils_basename_for_display	(const gchar *uri); +gboolean	 gedit_utils_decode_uri 		(const gchar *uri, +							 gchar **scheme, +							 gchar **user, +							 gchar **port, +							 gchar **host, +							 gchar **path); + + +/* Turns data from a drop into a list of well formatted uris */ +gchar 	       **gedit_utils_drop_get_uris		(GtkSelectionData *selection_data); + +G_END_DECLS + +#endif /* __GEDIT_UTILS_H__ */ + + diff --git a/gedit/gedit-view.c b/gedit/gedit-view.c new file mode 100755 index 00000000..b999d298 --- /dev/null +++ b/gedit/gedit-view.c @@ -0,0 +1,2181 @@ +/* + * gedit-view.c + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi  + * Copyright (C) 2003-2005 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes. + * + * $Id$  + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gdk/gdkkeysyms.h> + +#include <glib/gi18n.h> + +#include "gedit-view.h" +#include "gedit-debug.h" +#include "gedit-prefs-manager.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-marshal.h" +#include "gedit-utils.h" + + +#define GEDIT_VIEW_SCROLL_MARGIN 0.02 +#define GEDIT_VIEW_SEARCH_DIALOG_TIMEOUT (30*1000) /* 30 seconds */ + +#define MIN_SEARCH_COMPLETION_KEY_LEN	3 + +#define GEDIT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GEDIT_TYPE_VIEW, GeditViewPrivate)) + +typedef enum +{ +	GOTO_LINE, +	SEARCH +} SearchMode; + +enum +{ +	TARGET_URI_LIST = 100 +}; + +struct _GeditViewPrivate +{ +	SearchMode   search_mode; +	 +	GtkTextIter  start_search_iter; + +	/* used to restore the search state if an +	 * incremental search is cancelled +	 */ + 	gchar       *old_search_text; +	guint        old_search_flags; + +	/* used to remeber the state of the last +	 * incremental search (the document search +	 * state may be changed by the search dialog) +	 */ +	guint        search_flags; +	gboolean     wrap_around; + +	GtkWidget   *search_window; +	GtkWidget   *search_entry; + +	guint        typeselect_flush_timeout; +	guint        search_entry_changed_id; +	 +	gboolean     disable_popdown; +	 +	GtkTextBuffer *current_buffer; +}; + +/* The search entry completion is shared among all the views */ +GtkListStore *search_completion_model = NULL; + +static void	gedit_view_destroy		(GtkObject        *object); +static void	gedit_view_finalize		(GObject          *object); +static gint     gedit_view_focus_out		(GtkWidget        *widget, +						 GdkEventFocus    *event); +static gboolean gedit_view_drag_motion		(GtkWidget        *widget, +						 GdkDragContext   *context, +						 gint              x, +						 gint              y, +						 guint             timestamp); +static void     gedit_view_drag_data_received   (GtkWidget        *widget, +						 GdkDragContext   *context, +						 gint              x, +						 gint              y, +						 GtkSelectionData *selection_data, +						 guint             info, +						 guint             timestamp); +static gboolean gedit_view_drag_drop		(GtkWidget        *widget, +	      					 GdkDragContext   *context, +	      					 gint              x, +	      					 gint              y, +	      					 guint             timestamp); +static gboolean	gedit_view_button_press_event	(GtkWidget        *widget, +						 GdkEventButton   *event); + +static gboolean start_interactive_search	(GeditView        *view); +static gboolean start_interactive_goto_line	(GeditView        *view); +static gboolean reset_searched_text		(GeditView        *view); + +static void	hide_search_window 		(GeditView        *view, +						 gboolean          cancel); + + +static gint	gedit_view_expose	 	(GtkWidget        *widget, +						 GdkEventExpose   *event); +static void 	search_highlight_updated_cb	(GeditDocument    *doc, +						 GtkTextIter      *start, +						 GtkTextIter      *end, +						 GeditView        *view); + +static void	gedit_view_delete_from_cursor 	(GtkTextView     *text_view, +						 GtkDeleteType    type, +						 gint             count); + +G_DEFINE_TYPE(GeditView, gedit_view, GTK_TYPE_SOURCE_VIEW) + +/* Signals */ +enum +{ +	START_INTERACTIVE_SEARCH, +	START_INTERACTIVE_GOTO_LINE, +	RESET_SEARCHED_TEXT, +	DROP_URIS, +	LAST_SIGNAL +}; + +static guint view_signals [LAST_SIGNAL] = { 0 }; + +typedef enum +{ +	GEDIT_SEARCH_ENTRY_NORMAL, +	GEDIT_SEARCH_ENTRY_NOT_FOUND +} GeditSearchEntryBgColor; + +static void +document_read_only_notify_handler (GeditDocument *document,  +			           GParamSpec    *pspec, +				   GeditView     *view) +{ +	gedit_debug (DEBUG_VIEW); + +	gtk_text_view_set_editable (GTK_TEXT_VIEW (view),  +				    !gedit_document_get_readonly (document)); +} + +static void +gedit_view_class_init (GeditViewClass *klass) +{ +	GObjectClass     *object_class = G_OBJECT_CLASS (klass); +	GtkObjectClass   *gtkobject_class = GTK_OBJECT_CLASS (klass); +	GtkWidgetClass   *widget_class = GTK_WIDGET_CLASS (klass); +	GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass); +	 +	GtkBindingSet    *binding_set; + +	gtkobject_class->destroy = gedit_view_destroy; +	object_class->finalize = gedit_view_finalize; + +	widget_class->focus_out_event = gedit_view_focus_out; +	widget_class->expose_event = gedit_view_expose; +	 +	/* +	 * Override the gtk_text_view_drag_motion and drag_drop +	 * functions to get URIs +	 * +	 * If the mime type is text/uri-list, then we will accept +	 * the potential drop, or request the data (depending on the +	 * function). +	 * +	 * If the drag context has any other mime type, then pass the +	 * information onto the GtkTextView's standard handlers. +	 * (widget_class->function_name). +	 * +	 * See bug #89881 for details +	 */ +	widget_class->drag_motion = gedit_view_drag_motion; +	widget_class->drag_data_received = gedit_view_drag_data_received; +	widget_class->drag_drop = gedit_view_drag_drop; +	widget_class->button_press_event = gedit_view_button_press_event; +	klass->start_interactive_search = start_interactive_search; +	klass->start_interactive_goto_line = start_interactive_goto_line; +	klass->reset_searched_text = reset_searched_text;	 + +	text_view_class->delete_from_cursor = gedit_view_delete_from_cursor; +	 +	view_signals[START_INTERACTIVE_SEARCH] = +    		g_signal_new ("start_interactive_search", +		  	      G_TYPE_FROM_CLASS (object_class), +		  	      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +		  	      G_STRUCT_OFFSET (GeditViewClass, start_interactive_search), +			      NULL, NULL, +			      gedit_marshal_BOOLEAN__NONE, +			      G_TYPE_BOOLEAN, 0);	 + +	view_signals[START_INTERACTIVE_GOTO_LINE] = +    		g_signal_new ("start_interactive_goto_line", +		  	      G_TYPE_FROM_CLASS (object_class), +		  	      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +		  	      G_STRUCT_OFFSET (GeditViewClass, start_interactive_goto_line), +			      NULL, NULL, +			      gedit_marshal_BOOLEAN__NONE, +			      G_TYPE_BOOLEAN, 0); + +	view_signals[RESET_SEARCHED_TEXT] = +    		g_signal_new ("reset_searched_text", +		  	      G_TYPE_FROM_CLASS (object_class), +		  	      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +		  	      G_STRUCT_OFFSET (GeditViewClass, reset_searched_text), +			      NULL, NULL, +			      gedit_marshal_BOOLEAN__NONE, +			      G_TYPE_BOOLEAN, 0);		 + +	/* A new signal DROP_URIS has been added to allow plugins to intercept +	 * the default dnd behaviour of 'text/uri-list'. GeditView now handles +	 * dnd in the default handlers of drag_drop, drag_motion and  +	 * drag_data_received. The view emits drop_uris from drag_data_received +	 * if valid uris have been dropped. Plugins should connect to  +	 * drag_motion, drag_drop and drag_data_received to change this  +	 * default behaviour. They should _NOT_ use this signal because this +	 * will not prevent gedit from loading the uri +	 */ +	view_signals[DROP_URIS] = +    		g_signal_new ("drop_uris", +		  	      G_TYPE_FROM_CLASS (object_class), +		  	      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +		  	      G_STRUCT_OFFSET (GeditViewClass, drop_uris), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__BOXED, +			      G_TYPE_NONE, 1, G_TYPE_STRV); +			       +	g_type_class_add_private (klass, sizeof (GeditViewPrivate)); +	 +	binding_set = gtk_binding_set_by_class (klass); +	 +	gtk_binding_entry_add_signal (binding_set, +				      GDK_k, +				      GDK_CONTROL_MASK, +				      "start_interactive_search", 0); +		 +	gtk_binding_entry_add_signal (binding_set, +				      GDK_i, +				      GDK_CONTROL_MASK, +				      "start_interactive_goto_line", 0); +	 +	gtk_binding_entry_add_signal (binding_set, +				      GDK_k, +				      GDK_CONTROL_MASK | GDK_SHIFT_MASK, +				      "reset_searched_text", 0); + +	gtk_binding_entry_add_signal (binding_set,  +				      GDK_d,  +				      GDK_CONTROL_MASK, +				      "delete_from_cursor", 2, +				      G_TYPE_ENUM, GTK_DELETE_PARAGRAPHS, +				      G_TYPE_INT, 1); +} + +static void +current_buffer_removed (GeditView *view) +{ +	if (view->priv->current_buffer) +	{ +		g_signal_handlers_disconnect_by_func (view->priv->current_buffer, +						      document_read_only_notify_handler, +						      view); +		g_signal_handlers_disconnect_by_func (view->priv->current_buffer, +						      search_highlight_updated_cb, +						      view); +				      +		g_object_unref (view->priv->current_buffer); +		view->priv->current_buffer = NULL; +	} +} + +static void +on_notify_buffer_cb (GeditView  *view, +		     GParamSpec *arg1, +		     gpointer    userdata) +{ +	GtkTextBuffer *buffer; +	 +	current_buffer_removed (view); +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	 +	if (buffer == NULL || !GEDIT_IS_DOCUMENT (buffer)) +		return; + +	view->priv->current_buffer = g_object_ref (buffer); +	g_signal_connect (buffer, +			  "notify::read-only", +			  G_CALLBACK (document_read_only_notify_handler), +			  view); + +	gtk_text_view_set_editable (GTK_TEXT_VIEW (view),  +				    !gedit_document_get_readonly (GEDIT_DOCUMENT (buffer))); + +	g_signal_connect (buffer, +			  "search_highlight_updated", +			  G_CALLBACK (search_highlight_updated_cb), +			  view); +} + +static void  +gedit_view_init (GeditView *view) +{ +	GtkTargetList *tl; +	 +	gedit_debug (DEBUG_VIEW); +	 +	view->priv = GEDIT_VIEW_GET_PRIVATE (view); + +	/* +	 *  Set tab, fonts, wrap mode, colors, etc. according +	 *  to preferences  +	 */ +	if (!gedit_prefs_manager_get_use_default_font ()) +	{ +		gchar *editor_font; + +		editor_font = gedit_prefs_manager_get_editor_font (); + +		gedit_view_set_font (view, FALSE, editor_font); + +		g_free (editor_font); +	} +	else +	{ +		gedit_view_set_font (view, TRUE, NULL); +	} + +	g_object_set (G_OBJECT (view),  +		      "wrap_mode", gedit_prefs_manager_get_wrap_mode (), +		      "show_line_numbers", gedit_prefs_manager_get_display_line_numbers (), +		      "auto_indent", gedit_prefs_manager_get_auto_indent (), +		      "tab_width", gedit_prefs_manager_get_tabs_size (), +		      "insert_spaces_instead_of_tabs", gedit_prefs_manager_get_insert_spaces (), +		      "show_right_margin", gedit_prefs_manager_get_display_right_margin (),  +		      "right_margin_position", gedit_prefs_manager_get_right_margin_position (), +		      "highlight_current_line", gedit_prefs_manager_get_highlight_current_line (), +		      "smart_home_end", gedit_prefs_manager_get_smart_home_end (), +		      "indent_on_tab", TRUE, +		      NULL); + +	view->priv->typeselect_flush_timeout = 0; +	view->priv->wrap_around = TRUE; + +	/* Drag and drop support */	 +	tl = gtk_drag_dest_get_target_list (GTK_WIDGET (view)); + +	if (tl != NULL) +		gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST); +		 +	/* Act on buffer change */ +	g_signal_connect (view,  +			  "notify::buffer",  +			  G_CALLBACK (on_notify_buffer_cb), +			  NULL); +} + +static void +gedit_view_destroy (GtkObject *object) +{ +	GeditView *view; + +	view = GEDIT_VIEW (object); + +	if (view->priv->search_window != NULL) +	{ +		gtk_widget_destroy (view->priv->search_window); +		view->priv->search_window = NULL; +		view->priv->search_entry = NULL; +		 +		if (view->priv->typeselect_flush_timeout != 0) +		{ +			g_source_remove (view->priv->typeselect_flush_timeout); +			view->priv->typeselect_flush_timeout = 0; +		} +	} +	 +	/* Disconnect notify buffer because the destroy of the textview will +	   set the buffer to NULL, and we call get_buffer in the notify which +	   would reinstate a GtkTextBuffer which we don't want */ +	current_buffer_removed (view); +	g_signal_handlers_disconnect_by_func (view, on_notify_buffer_cb, NULL); +	 +	(* GTK_OBJECT_CLASS (gedit_view_parent_class)->destroy) (object); +} + +static void +gedit_view_finalize (GObject *object) +{ +	GeditView *view; + +	view = GEDIT_VIEW (object); + +	current_buffer_removed (view); + +	g_free (view->priv->old_search_text); + +	(* G_OBJECT_CLASS (gedit_view_parent_class)->finalize) (object); +} + +static gint +gedit_view_focus_out (GtkWidget *widget, GdkEventFocus *event) +{ +	GeditView *view = GEDIT_VIEW (widget); +	 +	gtk_widget_queue_draw (widget); +	 +	/* hide interactive search dialog */ +	if (view->priv->search_window != NULL) +		hide_search_window (view, FALSE); +	 +	(* GTK_WIDGET_CLASS (gedit_view_parent_class)->focus_out_event) (widget, event); +	 +	return FALSE; +} + +/** + * gedit_view_new: + * @doc: a #GeditDocument + *  + * Creates a new #GeditView object displaying the @doc document.  + * @doc cannot be NULL. + * + * Return value: a new #GeditView + **/ +GtkWidget * +gedit_view_new (GeditDocument *doc) +{ +	GtkWidget *view; + +	gedit_debug_message (DEBUG_VIEW, "START"); + +	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL); + +	view = GTK_WIDGET (g_object_new (GEDIT_TYPE_VIEW, "buffer", doc, NULL)); + +	gedit_debug_message (DEBUG_VIEW, "END: %d", G_OBJECT (view)->ref_count); + +	gtk_widget_show_all (view); + +	return view; +} + +void +gedit_view_cut_clipboard (GeditView *view) +{ +	GtkTextBuffer *buffer; +	GtkClipboard *clipboard; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), +					      GDK_SELECTION_CLIPBOARD); + +	/* FIXME: what is default editability of a buffer? */ +  	gtk_text_buffer_cut_clipboard (buffer, +  				       clipboard, +				       !gedit_document_get_readonly ( +				       		GEDIT_DOCUMENT (buffer))); +  	 +	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), +				      gtk_text_buffer_get_insert (buffer), +				      GEDIT_VIEW_SCROLL_MARGIN, +				      FALSE, +				      0.0, +				      0.0); +} + +void +gedit_view_copy_clipboard (GeditView *view) +{ +	GtkTextBuffer *buffer; +	GtkClipboard *clipboard; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), +					      GDK_SELECTION_CLIPBOARD); + +  	gtk_text_buffer_copy_clipboard (buffer, clipboard); + +	/* on copy do not scroll, we are already on screen */ +} + +void +gedit_view_paste_clipboard (GeditView *view) +{ +  	GtkTextBuffer *buffer; +	GtkClipboard *clipboard; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), +					      GDK_SELECTION_CLIPBOARD); + +	/* FIXME: what is default editability of a buffer? */ +  	gtk_text_buffer_paste_clipboard (buffer, +					 clipboard, +					 NULL, +					 !gedit_document_get_readonly ( +						GEDIT_DOCUMENT (buffer))); + +	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), +				      gtk_text_buffer_get_insert (buffer), +				      GEDIT_VIEW_SCROLL_MARGIN, +				      FALSE, +				      0.0, +				      0.0); +} + +/** + * gedit_view_delete_selection: + * @view: a #GeditView + *  + * Deletes the text currently selected in the #GtkTextBuffer associated + * to the view and scroll to the cursor position. + **/ +void +gedit_view_delete_selection (GeditView *view) +{ +  	GtkTextBuffer *buffer = NULL; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	/* FIXME: what is default editability of a buffer? */ +	gtk_text_buffer_delete_selection (buffer, +					  TRUE, +					  !gedit_document_get_readonly ( +						GEDIT_DOCUMENT (buffer))); +						 +	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), +				      gtk_text_buffer_get_insert (buffer), +				      GEDIT_VIEW_SCROLL_MARGIN, +				      FALSE, +				      0.0, +				      0.0); +} + +/** + * gedit_view_select_all: + * @view: a #GeditView + *  + * Selects all the text displayed in the @view. + **/ +void +gedit_view_select_all (GeditView *view) +{ +	GtkTextBuffer *buffer = NULL; +	GtkTextIter start, end; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	gtk_text_buffer_get_bounds (buffer, &start, &end); +	gtk_text_buffer_select_range (buffer, &start, &end); +} + +/** + * gedit_view_scroll_to_cursor: + * @view: a #GeditView + *  + * Scrolls the @view to the cursor position. + **/ +void +gedit_view_scroll_to_cursor (GeditView *view) +{ +	GtkTextBuffer* buffer = NULL; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); +	 +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	g_return_if_fail (buffer != NULL); + +	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), +				      gtk_text_buffer_get_insert (buffer), +				      0.25, +				      FALSE, +				      0.0, +				      0.0); +} + +/** + * gedit_view_set_font: + * @view: a #GeditView + * @def: whether to reset the default font + * @font_name: the name of the font to use + *  + * If @def is #TRUE, resets the font of the @view to the default font + * otherwise sets it to @font_name. + **/ +void +gedit_view_set_font (GeditView   *view,  +		     gboolean     def,  +		     const gchar *font_name) +{ +	PangoFontDescription *font_desc = NULL; + +	gedit_debug (DEBUG_VIEW); + +	g_return_if_fail (GEDIT_IS_VIEW (view)); + +	if (def) +	{ +		gchar *font; + +		font = gedit_prefs_manager_get_system_font (); +		font_desc = pango_font_description_from_string (font); +		g_free (font); +	} +	else +	{ +		g_return_if_fail (font_name != NULL); + +		font_desc = pango_font_description_from_string (font_name); +	} + +	g_return_if_fail (font_desc != NULL); + +	gtk_widget_modify_font (GTK_WIDGET (view), font_desc); + +	pango_font_description_free (font_desc);		 +} + +static void +add_search_completion_entry (const gchar *str) +{ +	gchar        *text; +	gboolean      valid; +	GtkTreeModel *model; +	GtkTreeIter   iter; + +	if (str == NULL) +		return; + +	text = gedit_utils_unescape_search_text (str); + +	if (g_utf8_strlen (text, -1) < MIN_SEARCH_COMPLETION_KEY_LEN) +	{ +		g_free (text); +		return; +	} + +	g_return_if_fail (GTK_IS_TREE_MODEL (search_completion_model)); + +	model = GTK_TREE_MODEL (search_completion_model);	 + +	/* Get the first iter in the list */ +	valid = gtk_tree_model_get_iter_first (model, &iter); + +	while (valid) +	{ +		/* Walk through the list, reading each row */ +     		gchar *str_data; + +		gtk_tree_model_get (model,  +				    &iter,  +                          	    0,  +                          	    &str_data, +                          	    -1); + +		if (strcmp (text, str_data) == 0) +		{ +			g_free (text); +			g_free (str_data); +			gtk_list_store_move_after (GTK_LIST_STORE (model), +						   &iter, +						   NULL); + +			return; +		} + +		g_free (str_data); + +		valid = gtk_tree_model_iter_next (model, &iter); +    	} + +	gtk_list_store_prepend (GTK_LIST_STORE (model), &iter); +	gtk_list_store_set (GTK_LIST_STORE (model), +			    &iter, +			    0, +			    text, +			    -1); + +	g_free (text); +} + +static void +set_entry_background (GtkWidget               *entry, +		      GeditSearchEntryBgColor  col) +{ +	if (col == GEDIT_SEARCH_ENTRY_NOT_FOUND) +	{ +		GdkColor red; +		GdkColor white; + +		/* FIXME: a11y and theme */ + +		gdk_color_parse ("#FF6666", &red); +		gdk_color_parse ("white", &white); + +		gtk_widget_modify_base (entry, +				        GTK_STATE_NORMAL, +				        &red); +		gtk_widget_modify_text (entry, +				        GTK_STATE_NORMAL, +				        &white); +	} +	else /* reset */ +	{ +		gtk_widget_modify_base (entry, +				        GTK_STATE_NORMAL, +				        NULL); +		gtk_widget_modify_text (entry, +				        GTK_STATE_NORMAL, +				        NULL); +	} +} + +static gboolean +run_search (GeditView        *view, +            const gchar      *entry_text, +	    gboolean          search_backward, +	    gboolean          wrap_around, +            gboolean          typing) +{ +	GtkTextIter    start_iter; +	GtkTextIter    match_start; +	GtkTextIter    match_end;	 +	gboolean       found = FALSE; +	GeditDocument *doc; + +	g_return_val_if_fail (view->priv->search_mode == SEARCH, FALSE); + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +	 +	start_iter = view->priv->start_search_iter; +	 +	if (*entry_text != '\0') +	{	 +		if (!search_backward) +		{ +			if (!typing) +			{ +				/* forward and _NOT_ typing */ +				gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), +							      &start_iter, +							      &match_end); +		 +				gtk_text_iter_order (&match_end, &start_iter); +			} +		 +			/* run search */ +			found = gedit_document_search_forward (doc, +							       &start_iter, +							       NULL, +							       &match_start, +							       &match_end); +		}						        +		else if (!typing) +		{ +			/* backward and not typing */ +			gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), +							      &start_iter, +							      &match_end); +			 +			/* run search */ +			found = gedit_document_search_backward (doc, +							        NULL, +							        &start_iter, +							        &match_start, +							        &match_end); +		}  +		else +		{ +			/* backward (while typing) */ +			g_return_val_if_reached (FALSE); + +		} +		 +		if (!found && wrap_around) +		{ +			if (!search_backward) +				found = gedit_document_search_forward (doc, +								       NULL, +								       NULL, /* FIXME: set the end_inter */ +								       &match_start, +								       &match_end); +			else +				found = gedit_document_search_backward (doc, +								        NULL, /* FIXME: set the start_inter */ +								        NULL,  +								        &match_start, +								        &match_end); +		} +	} +	else +	{ +		gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), +						      &start_iter,  +						      NULL);	 +	}	 +	 +	if (found) +	{ +		gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), +					&match_start); + +		gtk_text_buffer_move_mark_by_name (GTK_TEXT_BUFFER (doc), +					"selection_bound", &match_end); +	} +	else +	{ +		if (typing) +		{ +			gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), +						      &view->priv->start_search_iter); +		} +	} +						       +	if (found || (*entry_text == '\0')) +	{				    +		gedit_view_scroll_to_cursor (view); + +		set_entry_background (view->priv->search_entry, +				      GEDIT_SEARCH_ENTRY_NORMAL);	 +	} +	else +	{ +		set_entry_background (view->priv->search_entry, +				      GEDIT_SEARCH_ENTRY_NOT_FOUND); +	} + +	return found; +} + +/* Cut and paste from gtkwindow.c */ +static void +send_focus_change (GtkWidget *widget, +		   gboolean   in) +{ +	GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE); + +	g_object_ref (widget); +    +	if (in) +		GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); +	else +		GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + +	fevent->focus_change.type = GDK_FOCUS_CHANGE; +	fevent->focus_change.window = g_object_ref (widget->window); +	fevent->focus_change.in = in; +   +	gtk_widget_event (widget, fevent); +   +	g_object_notify (G_OBJECT (widget), "has-focus"); + +	g_object_unref (widget); +	gdk_event_free (fevent); +} + +static void +hide_search_window (GeditView *view, gboolean cancel) +{ +	if (view->priv->disable_popdown) +		return; + +	if (view->priv->search_entry_changed_id != 0) +	{ +		g_signal_handler_disconnect (view->priv->search_entry, +					     view->priv->search_entry_changed_id); +		view->priv->search_entry_changed_id = 0; +    	} + +	if (view->priv->typeselect_flush_timeout != 0) +	{ +		g_source_remove (view->priv->typeselect_flush_timeout); +		view->priv->typeselect_flush_timeout = 0; +	} + +	/* send focus-in event */ +	send_focus_change (GTK_WIDGET (view->priv->search_entry), FALSE); +	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), TRUE); +	gtk_widget_hide (view->priv->search_window); +	 +	if (cancel) +	{ +		GtkTextBuffer *buffer; +		 +		buffer = GTK_TEXT_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +		gtk_text_buffer_place_cursor (buffer, &view->priv->start_search_iter); +		 +		gedit_view_scroll_to_cursor (view); +	} + +	/* make sure a focus event is sent for the edit area */ +	send_focus_change (GTK_WIDGET (view), TRUE); +} + +static gboolean +search_entry_flush_timeout (GeditView *view) +{ +	GDK_THREADS_ENTER (); + +  	view->priv->typeselect_flush_timeout = 0; +	hide_search_window (view, FALSE); + +	GDK_THREADS_LEAVE (); + +	return FALSE; +} + +static void +update_search_window_position (GeditView *view) +{ +	gint x, y; +	gint view_x, view_y; +	GdkWindow *view_window = GTK_WIDGET (view)->window; + +	gtk_widget_realize (view->priv->search_window); + +	gdk_window_get_origin (view_window, &view_x, &view_y); +   +	x = MAX (12, view_x + 12); +	y = MAX (12, view_y - 12); +	 +	gtk_window_move (GTK_WINDOW (view->priv->search_window), x, y); +} + +static gboolean +search_window_delete_event (GtkWidget   *widget, +			    GdkEventAny *event, +			    GeditView   *view) +{ +	hide_search_window (view, FALSE); + +	return TRUE; +} + +static gboolean +search_window_button_press_event (GtkWidget      *widget, +				  GdkEventButton *event, +				  GeditView      *view) +{ +	hide_search_window (view, FALSE); +	 +	gtk_propagate_event (GTK_WIDGET (view), (GdkEvent *)event); + +	return FALSE; +} + +static void +search_again (GeditView *view, +	      gboolean   search_backward) +{ +	const gchar *entry_text; + +	g_return_if_fail (view->priv->search_mode == SEARCH); +		 +	/* SEARCH mode */	 +	/* renew the flush timeout */ +	if (view->priv->typeselect_flush_timeout != 0) +	{ +		g_source_remove (view->priv->typeselect_flush_timeout); +		view->priv->typeselect_flush_timeout = +			g_timeout_add (GEDIT_VIEW_SEARCH_DIALOG_TIMEOUT, +		       		       (GSourceFunc)search_entry_flush_timeout, +		       		       view); +	} +	 +	entry_text = gtk_entry_get_text (GTK_ENTRY (view->priv->search_entry)); +	 +	add_search_completion_entry (entry_text); +		 +	run_search (view, +		    entry_text, +		    search_backward, +		    view->priv->wrap_around, +		    FALSE); +} + +static gboolean +search_window_scroll_event (GtkWidget      *widget, +			    GdkEventScroll *event, +			    GeditView      *view) +{ +	gboolean retval = FALSE; + +	if (view->priv->search_mode == GOTO_LINE) +		return retval; +		 +	/* SEARCH mode */	 +	if (event->direction == GDK_SCROLL_UP) +	{ +		search_again (view, TRUE); +		retval = TRUE; +	} +	else if (event->direction == GDK_SCROLL_DOWN) +	{ +      		search_again (view, FALSE); +      		retval = TRUE; +	} + +	return retval; +} + +static gboolean +search_window_key_press_event (GtkWidget   *widget, +			       GdkEventKey *event, +			       GeditView   *view) +{ +	gboolean retval = FALSE; +	guint modifiers; + +	modifiers = gtk_accelerator_get_default_mod_mask (); + +	/* Close window */ +	if (event->keyval == GDK_Tab) +	{ +		hide_search_window (view, FALSE); +		retval = TRUE; +	} + +	/* Close window and cancel the search */ +	if (event->keyval == GDK_Escape) +	{ +		if (view->priv->search_mode == SEARCH) +		{ +			GeditDocument *doc; + +			/* restore document search so that Find Next does the right thing */ +			doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +			gedit_document_set_search_text (doc,  +							view->priv->old_search_text, +							view->priv->old_search_flags); +						 +		} +		 +		hide_search_window (view, TRUE); +		retval = TRUE; +	} +	 +	if (view->priv->search_mode == GOTO_LINE) +		return retval; +		 +	/* SEARCH mode */ + +	/* select previous matching iter */ +	if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up) +	{ +		search_again (view, TRUE); +		retval = TRUE; +	} + +	if (((event->state & modifiers) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) &&  +	    (event->keyval == GDK_g || event->keyval == GDK_G)) +	{ +		search_again (view, TRUE); +		retval = TRUE; +	} + +	/* select next matching iter */ +	if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down) +	{ +		search_again (view, FALSE); +		retval = TRUE; +	} + +	if (((event->state & modifiers) == GDK_CONTROL_MASK) &&  +	    (event->keyval == GDK_g || event->keyval == GDK_G)) +	{ +		search_again (view, FALSE); +		retval = TRUE; +	} + +	return retval; +} + +static void +search_entry_activate (GtkEntry  *entry, +		       GeditView *view) +{ +	hide_search_window (view, FALSE); +} + +static void +wrap_around_menu_item_toggled (GtkCheckMenuItem *checkmenuitem, +			       GeditView        *view) +{	 +	view->priv->wrap_around = gtk_check_menu_item_get_active (checkmenuitem); +} + +static void +match_entire_word_menu_item_toggled (GtkCheckMenuItem *checkmenuitem, +				     GeditView        *view) +{ +	GEDIT_SEARCH_SET_ENTIRE_WORD (view->priv->search_flags, +				      gtk_check_menu_item_get_active (checkmenuitem)); +} + +static void +match_case_menu_item_toggled (GtkCheckMenuItem *checkmenuitem, +			      GeditView        *view) +{ +	GEDIT_SEARCH_SET_CASE_SENSITIVE (view->priv->search_flags, +					 gtk_check_menu_item_get_active (checkmenuitem)); +} + +static gboolean +real_search_enable_popdown (gpointer data) +{ +	GeditView *view = (GeditView *)data; + +	GDK_THREADS_ENTER (); + +	view->priv->disable_popdown = FALSE; + +	GDK_THREADS_LEAVE (); + +	return FALSE; +} + +static void +search_enable_popdown (GtkWidget *widget, +		       GeditView *view) +{ +	g_timeout_add (200, real_search_enable_popdown, view); +	 +	/* renew the flush timeout */ +	if (view->priv->typeselect_flush_timeout != 0) +		g_source_remove (view->priv->typeselect_flush_timeout); + +	view->priv->typeselect_flush_timeout = +		g_timeout_add (GEDIT_VIEW_SEARCH_DIALOG_TIMEOUT, +	       		       (GSourceFunc)search_entry_flush_timeout, +	       		       view); +} + +static void +search_entry_populate_popup (GtkEntry  *entry, +			     GtkMenu   *menu, +			     GeditView *view) +{ +	GtkWidget *menu_item; + +	view->priv->disable_popdown = TRUE; +	g_signal_connect (menu, "hide", +		    	  G_CALLBACK (search_enable_popdown), view); + +	if (view->priv->search_mode == GOTO_LINE) +		return; + +	/* separator */ +	menu_item = gtk_menu_item_new (); +	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); +	gtk_widget_show (menu_item); + +	/* create "Wrap Around" menu item. */ +	menu_item = gtk_check_menu_item_new_with_mnemonic (_("_Wrap Around")); +	g_signal_connect (G_OBJECT (menu_item), "toggled", +			  G_CALLBACK (wrap_around_menu_item_toggled),  +			  view); +	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), +					view->priv->wrap_around); +	gtk_widget_show (menu_item); + +	/* create "Match Entire Word Only" menu item. */ +	menu_item = gtk_check_menu_item_new_with_mnemonic (_("Match _Entire Word Only")); +	g_signal_connect (G_OBJECT (menu_item), "toggled", +			  G_CALLBACK (match_entire_word_menu_item_toggled),  +			  view); +	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), +					GEDIT_SEARCH_IS_ENTIRE_WORD (view->priv->search_flags)); +	gtk_widget_show (menu_item); + +	/* create "Match Case" menu item. */ +	menu_item = gtk_check_menu_item_new_with_mnemonic (_("_Match Case")); +	g_signal_connect (G_OBJECT (menu_item), "toggled", +			  G_CALLBACK (match_case_menu_item_toggled),  +			  view); +	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), +					GEDIT_SEARCH_IS_CASE_SENSITIVE (view->priv->search_flags)); +	gtk_widget_show (menu_item); +} + +static void +search_entry_insert_text (GtkEditable *editable,  +			  const gchar *text,  +			  gint         length,  +			  gint        *position, +			  GeditView   *view) +{ +	if (view->priv->search_mode == GOTO_LINE) +	{ +		gunichar c; +		const gchar *p; +	 	const gchar *end; +	 	const gchar *next; + +		p = text; +		end = text + length; + +		if (p == end) +			return; + +		c = g_utf8_get_char (p); +		 +		if (((c == '-' || c == '+') && *position == 0) || +		    (c == ':' && *position != 0)) +		{ +			gchar *s = NULL; +		 +			if (c == ':') +			{ +				s = gtk_editable_get_chars (editable, 0, -1); +				s = g_utf8_strchr (s, -1, ':'); +			} +			 +			if (s == NULL || s == p) +			{ +				next = g_utf8_next_char (p); +				p = next; +			} +			 +			g_free (s); +		} + +		while (p != end) +		{ +			next = g_utf8_next_char (p); + +			c = g_utf8_get_char (p); + +			if (!g_unichar_isdigit (c)) { +				g_signal_stop_emission_by_name (editable, "insert_text"); +				gtk_widget_error_bell (view->priv->search_entry); +				break; +			} + +			p = next; +		} +	} +	else +	{ +		/* SEARCH mode */ +		static gboolean  insert_text = FALSE; +		gchar           *escaped_text; +		gint             new_len; + +		gedit_debug_message (DEBUG_SEARCH, "Text: %s", text); + +		/* To avoid recursive behavior */ +		if (insert_text) +			return; + +		escaped_text = gedit_utils_escape_search_text (text); + +		gedit_debug_message (DEBUG_SEARCH, "Escaped Text: %s", escaped_text); + +		new_len = strlen (escaped_text); + +		if (new_len == length) +		{ +			g_free (escaped_text); +			return; +		} + +		insert_text = TRUE; + +		g_signal_stop_emission_by_name (editable, "insert_text"); +		 +		gtk_editable_insert_text (editable, escaped_text, new_len, position); + +		insert_text = FALSE; + +		g_free (escaped_text); +	} +} + +static void +customize_for_search_mode (GeditView *view) +{ +	if (view->priv->search_mode == SEARCH) +	{ +		gtk_entry_set_icon_from_stock (GTK_ENTRY (view->priv->search_entry), +					       GTK_ENTRY_ICON_PRIMARY, +					       GTK_STOCK_FIND); +		 +		gtk_widget_set_tooltip_text (view->priv->search_entry, +					     _("String you want to search for")); +	} +	else +	{ +		gtk_entry_set_icon_from_stock (GTK_ENTRY (view->priv->search_entry), +					       GTK_ENTRY_ICON_PRIMARY, +					       GTK_STOCK_JUMP_TO); +		 +		gtk_widget_set_tooltip_text (view->priv->search_entry, +					     _("Line you want to move the cursor to")); +	} +} + +static gboolean +completion_func (GtkEntryCompletion *completion, +                 const char         *key, +		 GtkTreeIter        *iter, +		 gpointer            data) +{ +	gchar *item = NULL; +	gboolean ret = FALSE; +	GtkTreeModel *model; +	GeditViewPrivate *priv = (GeditViewPrivate *)data; +	const gchar *real_key; +		 +	if (priv->search_mode == GOTO_LINE) +		return FALSE; +		 +	real_key = gtk_entry_get_text (GTK_ENTRY (gtk_entry_completion_get_entry (completion))); +	 +	if (g_utf8_strlen (real_key, -1) <= MIN_SEARCH_COMPLETION_KEY_LEN) +		return FALSE; +		 +	model = gtk_entry_completion_get_model (completion); +	g_return_val_if_fail (gtk_tree_model_get_column_type (model, 0) == G_TYPE_STRING,  +			      FALSE); +			       +	gtk_tree_model_get (model,  +			    iter, +			    0,  +			    &item, +			    -1); +	 +	if (item == NULL) +		return FALSE; +		 +	if (GEDIT_SEARCH_IS_CASE_SENSITIVE (priv->search_flags)) +	{		 +		if (!strncmp (real_key, item, strlen (real_key))) +			ret = TRUE; +	} +	else +	{ +		gchar *normalized_string; +		gchar *case_normalized_string; +		 +		normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL); +		case_normalized_string = g_utf8_casefold (normalized_string, -1); +       +      		if (!strncmp (key, case_normalized_string, strlen (key))) +			ret = TRUE; + 		 +		g_free (normalized_string); +		g_free (case_normalized_string); +		 +	} +	 +	g_free (item); +	 +	return ret;	 +} + +static void +ensure_search_window (GeditView *view) +{   +	GtkWidget          *frame; +	GtkWidget          *vbox; +	GtkWidget          *toplevel; +	GtkEntryCompletion *completion; +	 +	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); + +	if (view->priv->search_window != NULL) +	{ +		if (GTK_WINDOW (toplevel)->group) +			gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, +						     GTK_WINDOW (view->priv->search_window)); +		else if (GTK_WINDOW (view->priv->search_window)->group) +	 		gtk_window_group_remove_window (GTK_WINDOW (view->priv->search_window)->group, +					 		GTK_WINDOW (view->priv->search_window)); +					 		 +		customize_for_search_mode (view); +		 +		return; +	} +    +	view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP); + +	if (GTK_WINDOW (toplevel)->group) +		gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, +					     GTK_WINDOW (view->priv->search_window)); +					      +	gtk_window_set_modal (GTK_WINDOW (view->priv->search_window), TRUE); +	 +	g_signal_connect (view->priv->search_window, "delete_event", +			  G_CALLBACK (search_window_delete_event), +			  view); +	g_signal_connect (view->priv->search_window, "key_press_event", +			  G_CALLBACK (search_window_key_press_event), +			  view); +	g_signal_connect (view->priv->search_window, "button_press_event", +			  G_CALLBACK (search_window_button_press_event), +			  view); +	g_signal_connect (view->priv->search_window, "scroll_event", +			  G_CALLBACK (search_window_scroll_event), +			  view); + +	frame = gtk_frame_new (NULL); +	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); +	gtk_widget_show (frame); +	gtk_container_add (GTK_CONTAINER (view->priv->search_window), frame); + +	vbox = gtk_vbox_new (FALSE, 0); +	gtk_widget_show (vbox); +	gtk_container_add (GTK_CONTAINER (frame), vbox); +	gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); + +	/* add entry */ +	view->priv->search_entry = gtk_entry_new (); +	gtk_widget_show (view->priv->search_entry); +	 +	g_signal_connect (view->priv->search_entry, "populate_popup", +			  G_CALLBACK (search_entry_populate_popup), +			  view); +	g_signal_connect (view->priv->search_entry, "activate",  +			  G_CALLBACK (search_entry_activate), +			  view); +	/* CHECK: do we really need to connect to preedit too? -- Paolo +	g_signal_connect (GTK_ENTRY (view->priv->search_entry)->im_context, "preedit-changed", +			  G_CALLBACK (gtk_view_search_preedit_changed), +			  view); +	*/		 +	g_signal_connect (view->priv->search_entry,  +			  "insert_text", +			  G_CALLBACK (search_entry_insert_text),  +			  view);	   +			   +	gtk_container_add (GTK_CONTAINER (vbox), +			   view->priv->search_entry); + +	if (search_completion_model == NULL) +	{ +		/* Create a tree model and use it as the completion model */ +		search_completion_model = gtk_list_store_new (1, G_TYPE_STRING); +	} +	 +	/* Create the completion object for the search entry */ +	completion = gtk_entry_completion_new (); +	gtk_entry_completion_set_model (completion,  +					GTK_TREE_MODEL (search_completion_model)); +		 +	/* Use model column 0 as the text column */ +	gtk_entry_completion_set_text_column (completion, 0); + +	gtk_entry_completion_set_minimum_key_length (completion, +						     MIN_SEARCH_COMPLETION_KEY_LEN); + +	gtk_entry_completion_set_popup_completion (completion, FALSE); +	gtk_entry_completion_set_inline_completion (completion, TRUE); +	 +	gtk_entry_completion_set_match_func (completion,  +					     completion_func, +					     view->priv, +					     NULL); + +	/* Assign the completion to the entry */ +	gtk_entry_set_completion (GTK_ENTRY (view->priv->search_entry),  +				  completion); +	g_object_unref (completion); + +	gtk_widget_realize (view->priv->search_entry); + +	customize_for_search_mode (view);	 +} + +static gboolean +get_selected_text (GtkTextBuffer *doc, gchar **selected_text, gint *len) +{ +	GtkTextIter start, end; + +	g_return_val_if_fail (selected_text != NULL, FALSE); +	g_return_val_if_fail (*selected_text == NULL, FALSE); + +	if (!gtk_text_buffer_get_selection_bounds (doc, &start, &end)) +	{ +		if (len != NULL) +			len = 0; + +		return FALSE; +	} + +	*selected_text = gtk_text_buffer_get_slice (doc, &start, &end, TRUE); + +	if (len != NULL) +		*len = g_utf8_strlen (*selected_text, -1); + +	return TRUE; +} + +static void +init_search_entry (GeditView *view) +{ +	GtkTextBuffer *buffer; +				 +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); +	 +	if (view->priv->search_mode == GOTO_LINE) +	{	 +		gint   line; +		gchar *line_str; +		 +		line = gtk_text_iter_get_line (&view->priv->start_search_iter); +		 +		line_str = g_strdup_printf ("%d", line + 1); +		 +		gtk_entry_set_text (GTK_ENTRY (view->priv->search_entry),  +				    line_str); +				     +		g_free (line_str); +		 +		return; +	} +	else +	{ +		/* SEARCH mode */ +		gboolean  selection_exists; +		gchar    *find_text = NULL; +		gchar    *old_find_text; +		guint     old_find_flags = 0; +		gint      sel_len = 0; + +		g_free (view->priv->old_search_text); +		 +		old_find_text = gedit_document_get_search_text (GEDIT_DOCUMENT (buffer),  +								&old_find_flags); +		if (old_find_text != NULL) +		{ +			view->priv->old_search_text = old_find_text; +			add_search_completion_entry (old_find_text); +		} + +		if (old_find_flags != 0) +		{ +			view->priv->old_search_flags = old_find_flags; +		} + +		selection_exists = get_selected_text (buffer,  +						      &find_text,  +						      &sel_len); +							      				 +		if (selection_exists  && (find_text != NULL) && (sel_len <= 160)) +		{ +			gtk_entry_set_text (GTK_ENTRY (view->priv->search_entry),  +					    find_text);	 +		} +		else +		{ +			gtk_entry_set_text (GTK_ENTRY (view->priv->search_entry),  +					    ""); +		} +		 +		g_free (find_text); +	} +} + +static void +search_init (GtkWidget *entry, +	     GeditView *view) +{ +	GeditDocument *doc; +	const gchar *entry_text; + +	/* renew the flush timeout */ +	if (view->priv->typeselect_flush_timeout != 0) +	{ +		g_source_remove (view->priv->typeselect_flush_timeout); +		view->priv->typeselect_flush_timeout = +			g_timeout_add (GEDIT_VIEW_SEARCH_DIALOG_TIMEOUT, +		       		       (GSourceFunc)search_entry_flush_timeout, +		       		       view); +	} +	 +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +			 +	entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); +	 +	if (view->priv->search_mode == SEARCH) +	{ +		gchar *search_text; +		guint  search_flags; + +		search_text = gedit_document_get_search_text (doc, &search_flags); + +		if ((search_text == NULL) || +		    (strcmp (search_text, entry_text) != 0) || +		     search_flags != view->priv->search_flags) +		{ +			gedit_document_set_search_text (doc,  +							entry_text, +							view->priv->search_flags); +		} + +		g_free (search_text); + +		run_search (view, +			    entry_text, +			    FALSE, +			    view->priv->wrap_around, +			    TRUE); +	} +	else +	{ +		if (*entry_text != '\0') +		{ +			gboolean moved, moved_offset; +			gint line; +			gint offset_line = 0; +			gint line_offset = 0; +			gchar **split_text = NULL; +			const gchar *text; +			 +			split_text = g_strsplit (entry_text, ":", -1); +			 +			if (g_strv_length (split_text) > 1) +			{ +				text = split_text[0]; +			} +			else +			{ +				text = entry_text; +			} +			 +			if (*text == '-') +			{ +				gint cur_line = gtk_text_iter_get_line (&view->priv->start_search_iter); +			 +				if (*(text + 1) != '\0') +					offset_line = MAX (atoi (text + 1), 0); +				 +				line = MAX (cur_line - offset_line, 0); +			} +			else if (*entry_text == '+') +			{ +				gint cur_line = gtk_text_iter_get_line (&view->priv->start_search_iter); +			 +				if (*(text + 1) != '\0') +					offset_line = MAX (atoi (text + 1), 0); +				 +				line = cur_line + offset_line; +			} +			else +			{ +				line = MAX (atoi (text) - 1, 0); +			} +			 +			if (split_text[1] != NULL) +			{ +				line_offset = atoi (split_text[1]); +			} +			 +			g_strfreev (split_text); +			 +			moved = gedit_document_goto_line (doc, line); +			moved_offset = gedit_document_goto_line_offset (doc, line, +									line_offset); +			 +			gedit_view_scroll_to_cursor (view); + +			if (!moved || !moved_offset) +				set_entry_background (view->priv->search_entry, +						      GEDIT_SEARCH_ENTRY_NOT_FOUND); +			else +				set_entry_background (view->priv->search_entry, +						      GEDIT_SEARCH_ENTRY_NORMAL); +		} +	} +} + +static gboolean +start_interactive_search_real (GeditView *view) +{	 +	GtkTextBuffer *buffer; +	 +	if ((view->priv->search_window != NULL) && +	    GTK_WIDGET_VISIBLE (view->priv->search_window)) +		return TRUE; + +	if (!GTK_WIDGET_HAS_FOCUS (view)) +		return FALSE; + +	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + +	if (view->priv->search_mode == SEARCH) +		gtk_text_buffer_get_selection_bounds (buffer, &view->priv->start_search_iter, NULL); +	else +		gtk_text_buffer_get_iter_at_mark (buffer, +						  &view->priv->start_search_iter, +						  gtk_text_buffer_get_insert (buffer)); + +	ensure_search_window (view); + +	/* done, show it */ +	update_search_window_position (view); +	gtk_widget_show (view->priv->search_window); + +	if (view->priv->search_entry_changed_id == 0) +	{ +      		view->priv->search_entry_changed_id = +			g_signal_connect (view->priv->search_entry, +					  "changed", +					  G_CALLBACK (search_init), +					  view); +	} + +	init_search_entry (view); + +	view->priv->typeselect_flush_timeout =   +		g_timeout_add (GEDIT_VIEW_SEARCH_DIALOG_TIMEOUT, +		   	       (GSourceFunc) search_entry_flush_timeout, +		   	       view); + +	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE); +	gtk_widget_grab_focus (view->priv->search_entry); +	 +	send_focus_change (view->priv->search_entry, TRUE); +	 +	return TRUE; +} + +static gboolean +reset_searched_text (GeditView *view) +{		 +	GeditDocument *doc; + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +	 +	gedit_document_set_search_text (doc, "", GEDIT_SEARCH_DONT_SET_FLAGS); +	 +	return TRUE; +} + +static gboolean +start_interactive_search (GeditView *view) +{		 +	view->priv->search_mode = SEARCH; +	 +	return start_interactive_search_real (view); +} + +static gboolean  +start_interactive_goto_line (GeditView *view) +{ +	view->priv->search_mode = GOTO_LINE; +	 +	return start_interactive_search_real (view); +} + +static gint +gedit_view_expose (GtkWidget      *widget, +                   GdkEventExpose *event) +{ +	GtkTextView *text_view; +	GeditDocument *doc; +	 +	text_view = GTK_TEXT_VIEW (widget); +	 +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (text_view)); +	 +	if ((event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)) && +	    gedit_document_get_enable_search_highlighting (doc)) +	{ +		GdkRectangle visible_rect; +		GtkTextIter iter1, iter2; +		 +		gtk_text_view_get_visible_rect (text_view, &visible_rect); +		gtk_text_view_get_line_at_y (text_view, &iter1, +					     visible_rect.y, NULL); +		gtk_text_view_get_line_at_y (text_view, &iter2, +					     visible_rect.y +					     + visible_rect.height, NULL); +		gtk_text_iter_forward_line (&iter2); +				      +		_gedit_document_search_region (doc, +					       &iter1, +					       &iter2); +	} + +	return (* GTK_WIDGET_CLASS (gedit_view_parent_class)->expose_event)(widget, event); +} + +static GdkAtom +drag_get_uri_target (GtkWidget      *widget, +		     GdkDragContext *context) +{ +	GdkAtom target; +	GtkTargetList *tl; +	 +	tl = gtk_target_list_new (NULL, 0); +	gtk_target_list_add_uri_targets (tl, 0); +	 +	target = gtk_drag_dest_find_target (widget, context, tl); +	gtk_target_list_unref (tl); +	 +	return target;	 +} + +static gboolean +gedit_view_drag_motion (GtkWidget      *widget, +			GdkDragContext *context, +			gint            x, +			gint            y, +			guint           timestamp) +{ +	gboolean result; + +	/* Chain up to allow textview to scroll and position dnd mark, note  +	 * that this needs to be checked if gtksourceview or gtktextview +	 * changes drag_motion behaviour */ +	result = GTK_WIDGET_CLASS (gedit_view_parent_class)->drag_motion (widget, context, x, y, timestamp); + +	/* If this is a URL, deal with it here */ +	if (drag_get_uri_target (widget, context) != GDK_NONE)  +	{ +		gdk_drag_status (context, context->suggested_action, timestamp); +		result = TRUE; +	} + +	return result; +} + +static void +gedit_view_drag_data_received (GtkWidget        *widget, +		       	       GdkDragContext   *context, +			       gint              x, +			       gint              y, +			       GtkSelectionData *selection_data, +			       guint             info, +			       guint             timestamp) +{ +	gchar **uri_list; +	 +	/* If this is an URL emit DROP_URIS, otherwise chain up the signal */ +	if (info == TARGET_URI_LIST) +	{ +		uri_list = gedit_utils_drop_get_uris (selection_data); +		 +		if (uri_list != NULL) +		{ +			g_signal_emit (widget, view_signals[DROP_URIS], 0, uri_list); +			g_strfreev (uri_list); +			 +			gtk_drag_finish (context, TRUE, FALSE, timestamp); +		} +	} +	else +	{ +		GTK_WIDGET_CLASS (gedit_view_parent_class)->drag_data_received (widget, context, x, y, selection_data, info, timestamp); +	} +} + +static gboolean +gedit_view_drag_drop (GtkWidget      *widget, +		      GdkDragContext *context, +		      gint            x, +		      gint            y, +		      guint           timestamp) +{ +	gboolean result; +	GdkAtom target; + +	/* If this is a URL, just get the drag data */ +	target = drag_get_uri_target (widget, context); + +	if (target != GDK_NONE) +	{ +		gtk_drag_get_data (widget, context, target, timestamp); +		result = TRUE; +	} +	else +	{ +		/* Chain up */ +		result = GTK_WIDGET_CLASS (gedit_view_parent_class)->drag_drop (widget, context, x, y, timestamp); +	} + +	return result; +} + +static void +show_line_numbers_toggled (GtkMenu   *menu, +			   GeditView *view) +{ +	gboolean show; + +	show = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu)); + +	gedit_prefs_manager_set_display_line_numbers (show); +} + +static GtkWidget * +create_line_numbers_menu (GtkWidget *view) +{ +	GtkWidget *menu; +	GtkWidget *item; + +	menu = gtk_menu_new (); + +	item = gtk_check_menu_item_new_with_mnemonic (_("_Display line numbers")); +	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), +					gtk_source_view_get_show_line_numbers (GTK_SOURCE_VIEW (view))); +	g_signal_connect (item, "toggled", +			  G_CALLBACK (show_line_numbers_toggled), view); +	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +	 +	gtk_widget_show_all (menu); +	 +	return menu; +} + +static void +show_line_numbers_menu (GtkWidget      *view, +			GdkEventButton *event) +{ +	GtkWidget *menu; + +	menu = create_line_numbers_menu (view); + +	gtk_menu_popup (GTK_MENU (menu),  +			NULL,  +			NULL, +			NULL,  +			NULL, +			event->button,  +			event->time); +} + +static gboolean +gedit_view_button_press_event (GtkWidget *widget, GdkEventButton *event) +{ +	if ((event->type == GDK_BUTTON_PRESS) &&  +	    (event->button == 3) && +	    (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (widget), +						        GTK_TEXT_WINDOW_LEFT))) +	{ +		show_line_numbers_menu (widget, event); + +		return TRUE; +	} + +	return GTK_WIDGET_CLASS (gedit_view_parent_class)->button_press_event (widget, event); +} + +static void 	 +search_highlight_updated_cb (GeditDocument *doc, +			     GtkTextIter   *start, +			     GtkTextIter   *end, +			     GeditView     *view) +{ +	GdkRectangle visible_rect; +	GdkRectangle updated_rect;	 +	GdkRectangle redraw_rect; +	gint y; +	gint height; +	GtkTextView *text_view; +	 +	text_view = GTK_TEXT_VIEW (view); + +	g_return_if_fail (gedit_document_get_enable_search_highlighting ( +				GEDIT_DOCUMENT (gtk_text_view_get_buffer (text_view)))); +	 +	/* get visible area */ +	gtk_text_view_get_visible_rect (text_view, &visible_rect); +	 +	/* get updated rectangle */ +	gtk_text_view_get_line_yrange (text_view, start, &y, &height); +	updated_rect.y = y; +	gtk_text_view_get_line_yrange (text_view, end, &y, &height); +	updated_rect.height = y + height - updated_rect.y; +	updated_rect.x = visible_rect.x; +	updated_rect.width = visible_rect.width; + +	/* intersect both rectangles to see whether we need to queue a redraw */ +	if (gdk_rectangle_intersect (&updated_rect, &visible_rect, &redraw_rect))  +	{ +		GdkRectangle widget_rect; +		 +		gtk_text_view_buffer_to_window_coords (text_view, +						       GTK_TEXT_WINDOW_WIDGET, +						       redraw_rect.x, +						       redraw_rect.y, +						       &widget_rect.x, +						       &widget_rect.y); +		 +		widget_rect.width = redraw_rect.width; +		widget_rect.height = redraw_rect.height; +		 +		gtk_widget_queue_draw_area (GTK_WIDGET (text_view), +					    widget_rect.x, +					    widget_rect.y, +					    widget_rect.width, +					    widget_rect.height); +	} +} + +/* There is no "official" way to reset the im context in GtkTextView */ +static void +reset_im_context (GtkTextView *text_view) +{ +	if (text_view->need_im_reset) +	{ +		text_view->need_im_reset = FALSE; +		gtk_im_context_reset (text_view->im_context); +	} +} + +static void +delete_line (GtkTextView *text_view, +	     gint         count) +{ +	GtkTextIter start; +	GtkTextIter end; +	GtkTextBuffer *buffer; + +	buffer = gtk_text_view_get_buffer (text_view); + +	reset_im_context (text_view); + +	/* If there is a selection delete the selected lines and +	 * ignore count */ +	if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end)) +	{ +		gtk_text_iter_order (&start, &end); +		 +		if (gtk_text_iter_starts_line (&end)) +		{ +			/* Do no delete the line with the cursor if the cursor +			 * is at the beginning of the line */ +			count = 0; +		} +		else	 +			count = 1; +	} +	 +	gtk_text_iter_set_line_offset (&start, 0); + +	if (count > 0) +	{		 +		gtk_text_iter_forward_lines (&end, count); + +		if (gtk_text_iter_is_end (&end)) +		{		 +			if (gtk_text_iter_backward_line (&start) && !gtk_text_iter_ends_line (&start)) +				gtk_text_iter_forward_to_line_end (&start); +		} +	} +	else if (count < 0)  +	{ +		if (!gtk_text_iter_ends_line (&end)) +			gtk_text_iter_forward_to_line_end (&end); + +		while (count < 0)  +		{ +			if (!gtk_text_iter_backward_line (&start)) +				break; +				 +			++count; +		} + +		if (count == 0) +		{ +			if (!gtk_text_iter_ends_line (&start)) +				gtk_text_iter_forward_to_line_end (&start); +		} +		else +			gtk_text_iter_forward_line (&end); +	} + +	if (!gtk_text_iter_equal (&start, &end)) +	{ +		GtkTextIter cur = start; +		gtk_text_iter_set_line_offset (&cur, 0); +		 +		gtk_text_buffer_begin_user_action (buffer); + +		gtk_text_buffer_place_cursor (buffer, &cur); + +		gtk_text_buffer_delete_interactive (buffer,  +						    &start, +						    &end, +						    gtk_text_view_get_editable (text_view)); + +		gtk_text_buffer_end_user_action (buffer); + +		gtk_text_view_scroll_mark_onscreen (text_view, +						    gtk_text_buffer_get_insert (buffer)); +	} +	else +	{ +		gtk_widget_error_bell (GTK_WIDGET (text_view)); +	} +} + +static void +gedit_view_delete_from_cursor (GtkTextView   *text_view, +			       GtkDeleteType  type, +			       gint           count) +{ +	/* We override the standard handler for delete_from_cursor since +	   the GTK_DELETE_PARAGRAPHS case is not implemented as we like (i.e. it +	   does not remove the carriage return in the previous line) +	 */ +	switch (type) +	{ +		case GTK_DELETE_PARAGRAPHS: +			delete_line (text_view, count); +			break; +		default: +			GTK_TEXT_VIEW_CLASS (gedit_view_parent_class)->delete_from_cursor(text_view, type, count); +			break; +	} +} diff --git a/gedit/gedit-view.h b/gedit/gedit-view.h new file mode 100755 index 00000000..dc0096bc --- /dev/null +++ b/gedit/gedit-view.h @@ -0,0 +1,108 @@ +/* + * gedit-view.h + * This file is part of gedit + * + * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence + * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi + * Copyright (C) 2002-2005 Paolo Maggi   + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA.  + */ +  +/* + * Modified by the gedit Team, 1998-2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + */ + +#ifndef __GEDIT_VIEW_H__ +#define __GEDIT_VIEW_H__ + +#include <gtk/gtk.h> + +#include <gedit/gedit-document.h> +#include <gtksourceview/gtksourceview.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_VIEW            (gedit_view_get_type ()) +#define GEDIT_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_VIEW, GeditView)) +#define GEDIT_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_VIEW, GeditViewClass)) +#define GEDIT_IS_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_VIEW)) +#define GEDIT_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_VIEW)) +#define GEDIT_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_VIEW, GeditViewClass)) + +/* Private structure type */ +typedef struct _GeditViewPrivate	GeditViewPrivate; + +/* + * Main object structure + */ +typedef struct _GeditView		GeditView; + +struct _GeditView +{ +	GtkSourceView view; + +	/*< private > */ +	GeditViewPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditViewClass		GeditViewClass; + +struct _GeditViewClass +{ +	GtkSourceViewClass parent_class; + +	/* FIXME: Do we need placeholders ? */ + +	/* Key bindings */ +	gboolean (* start_interactive_search)	(GeditView       *view); +	gboolean (* start_interactive_goto_line)(GeditView       *view); +	gboolean (* reset_searched_text)	(GeditView       *view); + +	void	 (* drop_uris)			(GeditView	 *view, +						 gchar          **uri_list); +}; + +/* + * Public methods + */ +GType		 gedit_view_get_type     	(void) G_GNUC_CONST; + +GtkWidget	*gedit_view_new			(GeditDocument   *doc); + +void		 gedit_view_cut_clipboard 	(GeditView       *view); +void		 gedit_view_copy_clipboard 	(GeditView       *view); +void		 gedit_view_paste_clipboard	(GeditView       *view); +void		 gedit_view_delete_selection	(GeditView       *view); +void		 gedit_view_select_all		(GeditView       *view); + +void		 gedit_view_scroll_to_cursor 	(GeditView       *view); + +void 		 gedit_view_set_font		(GeditView       *view, +						 gboolean         def, +						 const gchar     *font_name); + +G_END_DECLS + +#endif /* __GEDIT_VIEW_H__ */ diff --git a/gedit/gedit-window-private.h b/gedit/gedit-window-private.h new file mode 100755 index 00000000..399433af --- /dev/null +++ b/gedit/gedit-window-private.h @@ -0,0 +1,124 @@ +/* + * gedit-window-private.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * 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 + * MERCHANWINDOWILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_WINDOW_PRIVATE_H__ +#define __GEDIT_WINDOW_PRIVATE_H__ + +#include "gedit/gedit-window.h" +#include "gedit-prefs-manager.h" +#include "gedit-message-bus.h" + +#ifdef OS_OSX +#include <ige-mac-integration.h> +#endif + +G_BEGIN_DECLS + +/* WindowPrivate is in a separate .h so that we can access it from gedit-commands */ + +struct _GeditWindowPrivate +{ +	GtkWidget      *notebook; + +	GtkWidget      *side_panel; +	GtkWidget      *bottom_panel; + +	GtkWidget      *hpaned; +	GtkWidget      *vpaned; +	 +	GtkWidget      *tab_width_combo; +	GtkWidget      *language_combo; +	 +	GeditMessageBus *message_bus;	 + +	/* Widgets for fullscreen mode */ +	GtkWidget      *fullscreen_controls; +	guint           fullscreen_animation_timeout_id; +	gboolean        fullscreen_animation_enter; + +	/* statusbar and context ids for statusbar messages */ +	GtkWidget      *statusbar;	 +	guint           generic_message_cid; +	guint           tip_message_cid; +	guint 		tab_width_id; +	guint 		spaces_instead_of_tabs_id; +	guint 		language_changed_id; + +	/* Menus & Toolbars */ +	GtkUIManager   *manager; +	GtkActionGroup *action_group; +	GtkActionGroup *always_sensitive_action_group; +	GtkActionGroup *close_action_group; +	GtkActionGroup *quit_action_group; +	GtkActionGroup *panes_action_group; +	GtkActionGroup *languages_action_group; +	GtkActionGroup *documents_list_action_group; +	guint           documents_list_menu_ui_id; +	GtkWidget      *toolbar; +	GtkWidget      *toolbar_recent_menu; +	GtkWidget      *menubar; +	GeditToolbarSetting toolbar_style; + +	/* recent files */ +	GtkActionGroup *recents_action_group; +	guint           recents_menu_ui_id; +	gulong          recents_handler_id; + +	GeditTab       *active_tab; +	gint            num_tabs; + +	gint            num_tabs_with_error; + +	gint            width; +	gint            height;	 +	GdkWindowState  window_state; + +	gint            side_panel_size; +	gint            bottom_panel_size; + +	GeditWindowState state; + +	gint            bottom_panel_item_removed_handler_id; + +	GtkWindowGroup *window_group; + +	GFile          *default_location; + +#ifdef OS_OSX +	IgeMacMenuGroup *mac_menu_group; +#endif + +	gboolean        removing_tabs : 1; +	gboolean        dispose_has_run : 1; +}; + +G_END_DECLS + +#endif  /* __GEDIT_WINDOW_PRIVATE_H__  */ diff --git a/gedit/gedit-window.c b/gedit/gedit-window.c new file mode 100755 index 00000000..c4bf2403 --- /dev/null +++ b/gedit/gedit-window.c @@ -0,0 +1,4798 @@ +/* + * gedit-window.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <time.h> +#include <sys/types.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gedit-ui.h" +#include "gedit-window.h" +#include "gedit-window-private.h" +#include "gedit-app.h" +#include "gedit-notebook.h" +#include "gedit-statusbar.h" +#include "gedit-utils.h" +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-language-manager.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-panel.h" +#include "gedit-documents-panel.h" +#include "gedit-plugins-engine.h" +#include "gedit-enum-types.h" +#include "gedit-dirs.h" +#include "gedit-status-combo-box.h" + +#ifdef OS_OSX +#include "osx/gedit-osx.h" +#endif + +#define LANGUAGE_NONE (const gchar *)"LangNone" +#define GEDIT_UIFILE "gedit-ui.xml" +#define TAB_WIDTH_DATA "GeditWindowTabWidthData" +#define LANGUAGE_DATA "GeditWindowLanguageData" +#define FULLSCREEN_ANIMATION_SPEED 4 + +#define GEDIT_WINDOW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\ +					 GEDIT_TYPE_WINDOW,                    \ +					 GeditWindowPrivate)) + +/* Signals */ +enum +{ +	TAB_ADDED, +	TAB_REMOVED, +	TABS_REORDERED, +	ACTIVE_TAB_CHANGED, +	ACTIVE_TAB_STATE_CHANGED, +	LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum +{ +	PROP_0, +	PROP_STATE +}; + +enum +{ +	TARGET_URI_LIST = 100 +}; + +G_DEFINE_TYPE(GeditWindow, gedit_window, GTK_TYPE_WINDOW) + +static void	recent_manager_changed	(GtkRecentManager *manager, +					 GeditWindow *window); + +static void +gedit_window_get_property (GObject    *object, +			   guint       prop_id, +			   GValue     *value, +			   GParamSpec *pspec) +{ +	GeditWindow *window = GEDIT_WINDOW (object); + +	switch (prop_id) +	{ +		case PROP_STATE: +			g_value_set_enum (value, +					  gedit_window_get_state (window)); +			break;			 +		default: +			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +			break;			 +	} +} + +static void +save_panes_state (GeditWindow *window) +{ +	gint pane_page; + +	gedit_debug (DEBUG_WINDOW); + +	if (gedit_prefs_manager_window_size_can_set ()) +		gedit_prefs_manager_set_window_size (window->priv->width, +						     window->priv->height); + +	if (gedit_prefs_manager_window_state_can_set ()) +		gedit_prefs_manager_set_window_state (window->priv->window_state); + +	if ((window->priv->side_panel_size > 0) && +	    gedit_prefs_manager_side_panel_size_can_set ()) +		gedit_prefs_manager_set_side_panel_size	( +					window->priv->side_panel_size); + +	pane_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (window->priv->side_panel)); +	if (pane_page != 0 && +	    gedit_prefs_manager_side_panel_active_page_can_set ()) +		gedit_prefs_manager_set_side_panel_active_page (pane_page); + +	if ((window->priv->bottom_panel_size > 0) &&  +	    gedit_prefs_manager_bottom_panel_size_can_set ()) +		gedit_prefs_manager_set_bottom_panel_size ( +					window->priv->bottom_panel_size); + +	pane_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (window->priv->bottom_panel)); +	if (pane_page != 0 && +	    gedit_prefs_manager_bottom_panel_active_page_can_set ()) +		gedit_prefs_manager_set_bottom_panel_active_page (pane_page); +} + +#ifdef OS_OSX +static GtkMenuItem * +ui_manager_menu_item (GtkUIManager *uimanager, +                      const gchar  *path) +{ +	return GTK_MENU_ITEM (gtk_ui_manager_get_widget (uimanager, path)); +} + +static void +add_mac_root_menu (GeditWindow *window) +{ +	if (window->priv->mac_menu_group != NULL) +	{ +		return; +	} +	 +	window->priv->mac_menu_group = ige_mac_menu_add_app_menu_group (); + +	ige_mac_menu_add_app_menu_item (window->priv->mac_menu_group, +	                                ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/HelpMenu/HelpAboutMenu"), +	                                NULL); +} + +static void +remove_mac_root_menu (GeditWindow *window) +{ +	if (window->priv->mac_menu_group == NULL) +	{ +		return; +	} +	 +	ige_mac_menu_remove_app_menu_group (window->priv->mac_menu_group); +	window->priv->mac_menu_group = NULL; +} + +static gboolean +gedit_window_focus_in_event (GtkWidget     *widget, +                             GdkEventFocus *event) +{ +	add_mac_root_menu (GEDIT_WINDOW (widget)); +	return GTK_WIDGET_CLASS (gedit_window_parent_class)->focus_in_event (widget, event); +} + +static gboolean +gedit_window_focus_out_event (GtkWidget     *widget, +                              GdkEventFocus *event) +{ +	remove_mac_root_menu (GEDIT_WINDOW (widget)); +	return GTK_WIDGET_CLASS (gedit_window_parent_class)->focus_out_event (widget, event); +} +#endif + +static void +gedit_window_dispose (GObject *object) +{ +	GeditWindow *window; + +	gedit_debug (DEBUG_WINDOW); + +	window = GEDIT_WINDOW (object); + +	/* Stop tracking removal of panes otherwise we always +	 * end up with thinking we had no pane active, since they +	 * should all be removed below */ +	if (window->priv->bottom_panel_item_removed_handler_id != 0) +	{ +		g_signal_handler_disconnect (window->priv->bottom_panel, +					     window->priv->bottom_panel_item_removed_handler_id); +		window->priv->bottom_panel_item_removed_handler_id = 0; +	} + +	/* First of all, force collection so that plugins +	 * really drop some of the references. +	 */ +	gedit_plugins_engine_garbage_collect (gedit_plugins_engine_get_default ()); + +	/* save the panes position and make sure to deactivate plugins +	 * for this window, but only once */ +	if (!window->priv->dispose_has_run) +	{ +		save_panes_state (window); + +		gedit_plugins_engine_deactivate_plugins (gedit_plugins_engine_get_default (), +					                  window); +		window->priv->dispose_has_run = TRUE; +	} + +	if (window->priv->fullscreen_animation_timeout_id != 0) +	{ +		g_source_remove (window->priv->fullscreen_animation_timeout_id); +		window->priv->fullscreen_animation_timeout_id = 0; +	} + +	if (window->priv->fullscreen_controls != NULL) +	{ +		gtk_widget_destroy (window->priv->fullscreen_controls); +		 +		window->priv->fullscreen_controls = NULL; +	} + +	if (window->priv->recents_handler_id != 0) +	{ +		GtkRecentManager *recent_manager; + +		recent_manager =  gtk_recent_manager_get_default (); +		g_signal_handler_disconnect (recent_manager, +					     window->priv->recents_handler_id); +		window->priv->recents_handler_id = 0; +	} + +	if (window->priv->manager != NULL) +	{ +		g_object_unref (window->priv->manager); +		window->priv->manager = NULL; +	} + +	if (window->priv->message_bus != NULL) +	{ +		g_object_unref (window->priv->message_bus); +		window->priv->message_bus = NULL; +	} + +	if (window->priv->window_group != NULL) +	{ +		g_object_unref (window->priv->window_group); +		window->priv->window_group = NULL; +	} +	 +	/* Now that there have broken some reference loops, +	 * force collection again. +	 */ +	gedit_plugins_engine_garbage_collect (gedit_plugins_engine_get_default ()); + +#ifdef OS_OSX +	remove_mac_root_menu (window); +#endif + +	G_OBJECT_CLASS (gedit_window_parent_class)->dispose (object); +} + +static void +gedit_window_finalize (GObject *object) +{ +	GeditWindow *window;  + +	gedit_debug (DEBUG_WINDOW); + +	window = GEDIT_WINDOW (object); + +	if (window->priv->default_location != NULL) +		g_object_unref (window->priv->default_location); + +	G_OBJECT_CLASS (gedit_window_parent_class)->finalize (object); +} + +static gboolean +gedit_window_window_state_event (GtkWidget           *widget, +				 GdkEventWindowState *event) +{ +	GeditWindow *window = GEDIT_WINDOW (widget); + +	window->priv->window_state = event->new_window_state; + +	if (event->changed_mask & +	    (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) +	{ +		gboolean show; + +		show = !(event->new_window_state & +			(GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)); + +		_gedit_statusbar_set_has_resize_grip (GEDIT_STATUSBAR (window->priv->statusbar), +						      show); +	} + +	return FALSE; +} + +static gboolean  +gedit_window_configure_event (GtkWidget         *widget, +			      GdkEventConfigure *event) +{ +	GeditWindow *window = GEDIT_WINDOW (widget); + +	window->priv->width = event->width; +	window->priv->height = event->height; + +	return GTK_WIDGET_CLASS (gedit_window_parent_class)->configure_event (widget, event); +} + +/* + * GtkWindow catches keybindings for the menu items _before_ passing them to + * the focused widget. This is unfortunate and means that pressing ctrl+V + * in an entry on a panel ends up pasting text in the TextView. + * Here we override GtkWindow's handler to do the same things that it + * does, but in the opposite order and then we chain up to the grand + * parent handler, skipping gtk_window_key_press_event. + */ +static gboolean +gedit_window_key_press_event (GtkWidget   *widget, +			      GdkEventKey *event) +{ +	static gpointer grand_parent_class = NULL; +	GtkWindow *window = GTK_WINDOW (widget); +	gboolean handled = FALSE; + +	if (grand_parent_class == NULL) +		grand_parent_class = g_type_class_peek_parent (gedit_window_parent_class); + +	/* handle focus widget key events */ +	if (!handled) +		handled = gtk_window_propagate_key_event (window, event); + +	/* handle mnemonics and accelerators */ +	if (!handled) +		handled = gtk_window_activate_key (window, event); + +	/* Chain up, invokes binding set */ +	if (!handled) +		handled = GTK_WIDGET_CLASS (grand_parent_class)->key_press_event (widget, event); + +	return handled; +} + +static void +gedit_window_tab_removed (GeditWindow *window, +			  GeditTab    *tab)  +{ +	gedit_plugins_engine_garbage_collect (gedit_plugins_engine_get_default ()); +} + +static void +gedit_window_class_init (GeditWindowClass *klass) +{ +	GObjectClass *object_class = G_OBJECT_CLASS (klass); +	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + +	klass->tab_removed = gedit_window_tab_removed; + +	object_class->dispose = gedit_window_dispose; +	object_class->finalize = gedit_window_finalize; +	object_class->get_property = gedit_window_get_property; + +	widget_class->window_state_event = gedit_window_window_state_event; +	widget_class->configure_event = gedit_window_configure_event; +	widget_class->key_press_event = gedit_window_key_press_event; + +#ifdef OS_OSX +	widget_class->focus_in_event = gedit_window_focus_in_event; +	widget_class->focus_out_event = gedit_window_focus_out_event; +#endif + +	signals[TAB_ADDED] = +		g_signal_new ("tab_added", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditWindowClass, tab_added), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[TAB_REMOVED] = +		g_signal_new ("tab_removed", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditWindowClass, tab_removed), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[TABS_REORDERED] = +		g_signal_new ("tabs_reordered", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditWindowClass, tabs_reordered), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, +			      0); +	signals[ACTIVE_TAB_CHANGED] = +		g_signal_new ("active_tab_changed", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditWindowClass, active_tab_changed), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__OBJECT, +			      G_TYPE_NONE, +			      1, +			      GEDIT_TYPE_TAB); +	signals[ACTIVE_TAB_STATE_CHANGED] = +		g_signal_new ("active_tab_state_changed", +			      G_OBJECT_CLASS_TYPE (object_class), +			      G_SIGNAL_RUN_FIRST, +			      G_STRUCT_OFFSET (GeditWindowClass, active_tab_state_changed), +			      NULL, NULL, +			      g_cclosure_marshal_VOID__VOID, +			      G_TYPE_NONE, +			      0);			      			       + +	g_object_class_install_property (object_class, +					 PROP_STATE, +					 g_param_spec_flags ("state", +							     "State", +							     "The window's state", +							     GEDIT_TYPE_WINDOW_STATE, +							     GEDIT_WINDOW_STATE_NORMAL, +							     G_PARAM_READABLE | +							     G_PARAM_STATIC_STRINGS)); + +	g_type_class_add_private (object_class, sizeof(GeditWindowPrivate)); +} + +static void +menu_item_select_cb (GtkMenuItem *proxy, +		     GeditWindow *window) +{ +	GtkAction *action; +	char *message; + +	action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy)); +	g_return_if_fail (action != NULL); + +	g_object_get (G_OBJECT (action), "tooltip", &message, NULL); +	if (message) +	{ +		gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), +				    window->priv->tip_message_cid, message); +		g_free (message); +	} +} + +static void +menu_item_deselect_cb (GtkMenuItem *proxy, +                       GeditWindow *window) +{ +	gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), +			   window->priv->tip_message_cid); +} + +static void +connect_proxy_cb (GtkUIManager *manager, +                  GtkAction *action, +                  GtkWidget *proxy, +                  GeditWindow *window) +{ +	if (GTK_IS_MENU_ITEM (proxy)) +	{ +		g_signal_connect (proxy, "select", +				  G_CALLBACK (menu_item_select_cb), window); +		g_signal_connect (proxy, "deselect", +				  G_CALLBACK (menu_item_deselect_cb), window); +	} +} + +static void +disconnect_proxy_cb (GtkUIManager *manager, +                     GtkAction *action, +                     GtkWidget *proxy, +                     GeditWindow *window) +{ +	if (GTK_IS_MENU_ITEM (proxy)) +	{ +		g_signal_handlers_disconnect_by_func +			(proxy, G_CALLBACK (menu_item_select_cb), window); +		g_signal_handlers_disconnect_by_func +			(proxy, G_CALLBACK (menu_item_deselect_cb), window); +	} +} + +static void +apply_toolbar_style (GeditWindow *window, +		     GtkWidget *toolbar) +{ +	switch (window->priv->toolbar_style) +	{ +		case GEDIT_TOOLBAR_SYSTEM: +			gedit_debug_message (DEBUG_WINDOW, "GEDIT: SYSTEM"); +			gtk_toolbar_unset_style ( +					GTK_TOOLBAR (toolbar)); +			break; + +		case GEDIT_TOOLBAR_ICONS: +			gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS"); +			gtk_toolbar_set_style ( +					GTK_TOOLBAR (toolbar), +					GTK_TOOLBAR_ICONS); +			break; + +		case GEDIT_TOOLBAR_ICONS_AND_TEXT: +			gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS_AND_TEXT"); +			gtk_toolbar_set_style ( +					GTK_TOOLBAR (toolbar), +					GTK_TOOLBAR_BOTH); +			break; + +		case GEDIT_TOOLBAR_ICONS_BOTH_HORIZ: +			gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS_BOTH_HORIZ"); +			gtk_toolbar_set_style ( +					GTK_TOOLBAR (toolbar), +					GTK_TOOLBAR_BOTH_HORIZ); +			break; +	} +} + +/* Returns TRUE if toolbar is visible */ +static gboolean +set_toolbar_style (GeditWindow *window, +		   GeditWindow *origin) +{ +	gboolean visible; +	GeditToolbarSetting style; +	GtkAction *action; +	 +	if (origin == NULL) +		visible = gedit_prefs_manager_get_toolbar_visible (); +	else +		visible = GTK_WIDGET_VISIBLE (origin->priv->toolbar); +	 +	/* Set visibility */ +	if (visible) +		gtk_widget_show (window->priv->toolbar); +	else +		gtk_widget_hide (window->priv->toolbar); + +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewToolbar"); + +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + +	/* Set style */ +	if (origin == NULL) +		style = gedit_prefs_manager_get_toolbar_buttons_style (); +	else +		style = origin->priv->toolbar_style; +	 +	window->priv->toolbar_style = style; +	 +	apply_toolbar_style (window, window->priv->toolbar); +	 +	return visible; +} + +static void +update_next_prev_doc_sensitivity (GeditWindow *window, +				  GeditTab    *tab) +{ +	gint	     tab_number; +	GtkNotebook *notebook; +	GtkAction   *action; +	 +	gedit_debug (DEBUG_WINDOW); +	 +	notebook = GTK_NOTEBOOK (_gedit_window_get_notebook (window)); +	 +	tab_number = gtk_notebook_page_num (notebook, GTK_WIDGET (tab)); +	g_return_if_fail (tab_number >= 0); +	 +	action = gtk_action_group_get_action (window->priv->action_group, +					      "DocumentsPreviousDocument"); +	gtk_action_set_sensitive (action, tab_number != 0); +	 +	action = gtk_action_group_get_action (window->priv->action_group, +					      "DocumentsNextDocument"); +	gtk_action_set_sensitive (action,  +				  tab_number < gtk_notebook_get_n_pages (notebook) - 1); +} + +static void +update_next_prev_doc_sensitivity_per_window (GeditWindow *window) +{ +	GeditTab  *tab; +	GtkAction *action; +	 +	gedit_debug (DEBUG_WINDOW); +	 +	tab = gedit_window_get_active_tab (window); +	if (tab != NULL) +	{ +		update_next_prev_doc_sensitivity (window, tab); +		 +		return; +	} +	 +	action = gtk_action_group_get_action (window->priv->action_group, +					      "DocumentsPreviousDocument"); +	gtk_action_set_sensitive (action, FALSE); +	 +	action = gtk_action_group_get_action (window->priv->action_group, +					      "DocumentsNextDocument"); +	gtk_action_set_sensitive (action, FALSE); +	 +} + +static void +received_clipboard_contents (GtkClipboard     *clipboard, +			     GtkSelectionData *selection_data, +			     GeditWindow      *window) +{ +	gboolean sens; +	GtkAction *action; + +	/* getting clipboard contents is async, so we need to +	 * get the current tab and its state */ + +	if (window->priv->active_tab != NULL) +	{ +		GeditTabState state; +		gboolean state_normal; + +		state = gedit_tab_get_state (window->priv->active_tab); +		state_normal = (state == GEDIT_TAB_STATE_NORMAL); + +		sens = state_normal && +		       gtk_selection_data_targets_include_text (selection_data); +	} +	else +	{ +		sens = FALSE; +	} + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditPaste"); + +	gtk_action_set_sensitive (action, sens); + +	g_object_unref (window); +} + +static void +set_paste_sensitivity_according_to_clipboard (GeditWindow  *window, +					      GtkClipboard *clipboard) +{ +	GdkDisplay *display; + +	display = gtk_clipboard_get_display (clipboard); + +	if (gdk_display_supports_selection_notification (display)) +	{ +		gtk_clipboard_request_contents (clipboard, +						gdk_atom_intern_static_string ("TARGETS"), +						(GtkClipboardReceivedFunc) received_clipboard_contents, +						g_object_ref (window)); +	} +	else +	{ +		GtkAction *action; + +		action = gtk_action_group_get_action (window->priv->action_group, +						      "EditPaste"); + +		/* XFIXES extension not availbale, make +		 * Paste always sensitive */ +		gtk_action_set_sensitive (action, TRUE); +	} +} + +static void +set_sensitivity_according_to_tab (GeditWindow *window, +				  GeditTab    *tab) +{ +	GeditDocument *doc; +	GeditView     *view; +	GtkAction     *action; +	gboolean       b; +	gboolean       state_normal; +	gboolean       editable; +	GeditTabState  state; +	GtkClipboard  *clipboard; +	GeditLockdownMask lockdown; + +	g_return_if_fail (GEDIT_TAB (tab)); + +	gedit_debug (DEBUG_WINDOW); + +	lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); + +	state = gedit_tab_get_state (tab); +	state_normal = (state == GEDIT_TAB_STATE_NORMAL); + +	view = gedit_tab_get_view (tab); +	editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view)); + +	doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); + +	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), +					      GDK_SELECTION_CLIPBOARD); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "FileSave"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || +				   (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && +				  !gedit_document_get_readonly (doc) && +				  !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "FileSaveAs"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   (state == GEDIT_TAB_STATE_SAVING_ERROR) || +				   (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || +				   (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && +				  !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "FileRevert"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && +				  !gedit_document_is_untitled (doc)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "FilePrintPreview"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  !(lockdown & GEDIT_LOCKDOWN_PRINTING)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "FilePrint"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				  (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && +				  !(lockdown & GEDIT_LOCKDOWN_PRINTING)); +				   +	action = gtk_action_group_get_action (window->priv->close_action_group, +					      "FileClose"); + +	gtk_action_set_sensitive (action, +				  (state != GEDIT_TAB_STATE_CLOSING) && +				  (state != GEDIT_TAB_STATE_SAVING) && +				  (state != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) && +				  (state != GEDIT_TAB_STATE_PRINTING) && +				  (state != GEDIT_TAB_STATE_PRINT_PREVIEWING) && +				  (state != GEDIT_TAB_STATE_SAVING_ERROR)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditUndo"); +	gtk_action_set_sensitive (action,  +				  state_normal && +				  gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditRedo"); +	gtk_action_set_sensitive (action,  +				  state_normal && +				  gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditCut"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  editable && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditCopy"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); +				   +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditPaste"); +	if (state_normal && editable) +	{ +		set_paste_sensitivity_according_to_clipboard (window, +							      clipboard); +	} +	else +	{ +		gtk_action_set_sensitive (action, FALSE); +	} + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditDelete"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  editable && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchFind"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchIncrementalSearch"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchReplace"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  editable); + +	b = gedit_document_get_can_search_again (doc); +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchFindNext"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchFindPrevious"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchClearHighlight"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchGoToLine"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); +	 +	action = gtk_action_group_get_action (window->priv->action_group, +					      "ViewHighlightMode"); +	gtk_action_set_sensitive (action,  +				  (state != GEDIT_TAB_STATE_CLOSING) && +				  gedit_prefs_manager_get_enable_syntax_highlighting ()); + +	update_next_prev_doc_sensitivity (window, tab); + +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static void +language_toggled (GtkToggleAction *action, +		  GeditWindow     *window) +{ +	GeditDocument *doc; +	GtkSourceLanguage *lang; +	const gchar *lang_id; + +	if (gtk_toggle_action_get_active (action) == FALSE) +		return; + +	doc = gedit_window_get_active_document (window); +	if (doc == NULL) +		return; + +	lang_id = gtk_action_get_name (GTK_ACTION (action)); +	 +	if (strcmp (lang_id, LANGUAGE_NONE) == 0) +	{ +		/* Normal (no highlighting) */ +		lang = NULL; +	} +	else +	{ +		lang = gtk_source_language_manager_get_language ( +				gedit_get_language_manager (), +				lang_id); +		if (lang == NULL) +		{ +			g_warning ("Could not get language %s\n", lang_id); +		} +	} + +	gedit_document_set_language (doc, lang); +} + +static gchar * +escape_section_name (const gchar *name) +{ +	gchar *ret; + +	ret = g_markup_escape_text (name, -1); + +	/* Replace '/' with '-' to avoid problems in xml paths */ +	g_strdelimit (ret, "/", '-'); + +	return ret; +} + +static void +create_language_menu_item (GtkSourceLanguage *lang, +			   gint               index, +			   guint              ui_id, +			   GeditWindow       *window) +{ +	GtkAction *section_action; +	GtkRadioAction *action; +	GtkAction *normal_action; +	GSList *group; +	const gchar *section; +	gchar *escaped_section; +	const gchar *lang_id; +	const gchar *lang_name; +	gchar *escaped_lang_name; +	gchar *tip; +	gchar *path; + +	section = gtk_source_language_get_section (lang); +	escaped_section = escape_section_name (section); + +	/* check if the section submenu exists or create it */ +	section_action = gtk_action_group_get_action (window->priv->languages_action_group, +						      escaped_section); + +	if (section_action == NULL) +	{ +		gchar *section_name; +		 +		section_name = gedit_utils_escape_underscores (section, -1); +		 +		section_action = gtk_action_new (escaped_section, +						 section_name, +						 NULL, +						 NULL); +						  +		g_free (section_name); + +		gtk_action_group_add_action (window->priv->languages_action_group, +					     section_action); +		g_object_unref (section_action); + +		gtk_ui_manager_add_ui (window->priv->manager, +				       ui_id, +				       "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder", +				       escaped_section, +				       escaped_section, +				       GTK_UI_MANAGER_MENU, +				       FALSE); +	} + +	/* now add the language item to the section */ +	lang_name = gtk_source_language_get_name (lang); +	lang_id = gtk_source_language_get_id (lang); +	 +	escaped_lang_name = gedit_utils_escape_underscores (lang_name, -1); +	 +	tip = g_strdup_printf (_("Use %s highlight mode"), lang_name); +	path = g_strdup_printf ("/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder/%s", +				escaped_section); + +	action = gtk_radio_action_new (lang_id, +				       escaped_lang_name, +				       tip, +				       NULL, +				       index); + +	g_free (escaped_lang_name); + +	/* Action is added with a NULL accel to make the accel overridable */ +	gtk_action_group_add_action_with_accel (window->priv->languages_action_group, +						GTK_ACTION (action), +						NULL); +	g_object_unref (action); + +	/* add the action to the same radio group of the "Normal" action */ +	normal_action = gtk_action_group_get_action (window->priv->languages_action_group, +						     LANGUAGE_NONE); +	group = gtk_radio_action_get_group (GTK_RADIO_ACTION (normal_action)); +	gtk_radio_action_set_group (action, group); + +	g_signal_connect (action, +			  "activate", +			  G_CALLBACK (language_toggled), +			  window); + +	gtk_ui_manager_add_ui (window->priv->manager, +			       ui_id, +			       path, +			       lang_id,  +			       lang_id, +			       GTK_UI_MANAGER_MENUITEM, +			       FALSE); + +	g_free (path); +	g_free (tip); +	g_free (escaped_section); +} + +static void +create_languages_menu (GeditWindow *window) +{ +	GtkRadioAction *action_none; +	GSList *languages; +	GSList *l; +	guint id; +	gint i; + +	gedit_debug (DEBUG_WINDOW); + +	/* add the "Plain Text" item before all the others */ +	 +	/* Translators: "Plain Text" means that no highlight mode is selected in the  +	 * "View->Highlight Mode" submenu and so syntax highlighting is disabled */ +	action_none = gtk_radio_action_new (LANGUAGE_NONE, _("Plain Text"), +					    _("Disable syntax highlighting"), +					    NULL, +					    -1); + +	gtk_action_group_add_action (window->priv->languages_action_group, +				     GTK_ACTION (action_none)); +	g_object_unref (action_none); + +	g_signal_connect (action_none, +			  "activate", +			  G_CALLBACK (language_toggled), +			  window); + +	id = gtk_ui_manager_new_merge_id (window->priv->manager); + +	gtk_ui_manager_add_ui (window->priv->manager, +			       id, +			       "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder", +			       LANGUAGE_NONE,  +			       LANGUAGE_NONE, +			       GTK_UI_MANAGER_MENUITEM, +			       TRUE); + +	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_none), TRUE); + +	/* now add all the known languages */ +	languages = gedit_language_manager_list_languages_sorted ( +						gedit_get_language_manager (), +						FALSE); + +	for (l = languages, i = 0; l != NULL; l = l->next, ++i) +	{ +		create_language_menu_item (l->data, +					   i, +					   id, +					   window); +	} + +	g_slist_free (languages); +} + +static void +update_languages_menu (GeditWindow *window) +{ +	GeditDocument *doc; +	GList *actions; +	GList *l; +	GtkAction *action; +	GtkSourceLanguage *lang; +	const gchar *lang_id; + +	doc = gedit_window_get_active_document (window); +	if (doc == NULL) +		return; + +	lang = gedit_document_get_language (doc); +	if (lang != NULL) +		lang_id = gtk_source_language_get_id (lang); +	else +		lang_id = LANGUAGE_NONE; + +	actions = gtk_action_group_list_actions (window->priv->languages_action_group); + +	/* prevent recursion */ +	for (l = actions; l != NULL; l = l->next) +	{ +		g_signal_handlers_block_by_func (GTK_ACTION (l->data), +						 G_CALLBACK (language_toggled), +						 window); +	} + +	action = gtk_action_group_get_action (window->priv->languages_action_group, +					      lang_id); + +	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + +	for (l = actions; l != NULL; l = l->next) +	{ +		g_signal_handlers_unblock_by_func (GTK_ACTION (l->data), +						   G_CALLBACK (language_toggled), +						   window); +	} + +	g_list_free (actions); +} + +void +_gedit_recent_add (GeditWindow *window, +		   const gchar *uri, +		   const gchar *mime) +{ +	GtkRecentManager *recent_manager; +	GtkRecentData *recent_data; + +	static gchar *groups[2] = { +		"gedit", +		NULL +	}; + +	recent_manager =  gtk_recent_manager_get_default (); + +	recent_data = g_slice_new (GtkRecentData); + +	recent_data->display_name = NULL; +	recent_data->description = NULL; +	recent_data->mime_type = (gchar *) mime; +	recent_data->app_name = (gchar *) g_get_application_name (); +	recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); +	recent_data->groups = groups; +	recent_data->is_private = FALSE; + +	gtk_recent_manager_add_full (recent_manager, +				     uri, +				     recent_data); + +	g_free (recent_data->app_exec); + +	g_slice_free (GtkRecentData, recent_data); +} + +void +_gedit_recent_remove (GeditWindow *window, +		      const gchar *uri) +{ +	GtkRecentManager *recent_manager; + +	recent_manager =  gtk_recent_manager_get_default (); + +	gtk_recent_manager_remove_item (recent_manager, uri, NULL); +} + +static void +open_recent_file (const gchar *uri, +		  GeditWindow *window) +{ +	GSList *uris = NULL; + +	uris = g_slist_prepend (uris, (gpointer) uri); + +	if (gedit_commands_load_uris (window, uris, NULL, 0) != 1) +	{ +		_gedit_recent_remove (window, uri); +	} + +	g_slist_free (uris); +} + +static void +recent_chooser_item_activated (GtkRecentChooser *chooser, +			       GeditWindow      *window) +{ +	gchar *uri; + +	uri = gtk_recent_chooser_get_current_uri (chooser); + +	open_recent_file (uri, window); + +	g_free (uri); +} + +static void +recents_menu_activate (GtkAction   *action, +		       GeditWindow *window) +{ +	GtkRecentInfo *info; +	const gchar *uri; + +	info = g_object_get_data (G_OBJECT (action), "gtk-recent-info"); +	g_return_if_fail (info != NULL); + +	uri = gtk_recent_info_get_uri (info); + +	open_recent_file (uri, window); +} + +static gint +sort_recents_mru (GtkRecentInfo *a, GtkRecentInfo *b) +{ +	return (gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a)); +} + +static void	update_recent_files_menu (GeditWindow *window); + +static void +recent_manager_changed (GtkRecentManager *manager, +			GeditWindow      *window) +{ +	/* regenerate the menu when the model changes */ +	update_recent_files_menu (window); +} + +/* + * Manually construct the inline recents list in the File menu. + * Hopefully gtk 2.12 will add support for it. + */ +static void +update_recent_files_menu (GeditWindow *window) +{ +	GeditWindowPrivate *p = window->priv; +	GtkRecentManager *recent_manager; +	gint max_recents; +	GList *actions, *l, *items; +	GList *filtered_items = NULL; +	gint i; + +	gedit_debug (DEBUG_WINDOW); + +	max_recents = gedit_prefs_manager_get_max_recents (); + +	g_return_if_fail (p->recents_action_group != NULL); + +	if (p->recents_menu_ui_id != 0) +		gtk_ui_manager_remove_ui (p->manager, +					  p->recents_menu_ui_id); + +	actions = gtk_action_group_list_actions (p->recents_action_group); +	for (l = actions; l != NULL; l = l->next) +	{ +		g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data), +						      G_CALLBACK (recents_menu_activate), +						      window); + 		gtk_action_group_remove_action (p->recents_action_group, +						GTK_ACTION (l->data)); +	} +	g_list_free (actions); + +	p->recents_menu_ui_id = gtk_ui_manager_new_merge_id (p->manager); + +	recent_manager =  gtk_recent_manager_get_default (); +	items = gtk_recent_manager_get_items (recent_manager); + +	/* filter */ +	for (l = items; l != NULL; l = l->next) +	{ +		GtkRecentInfo *info = l->data; + +		if (!gtk_recent_info_has_group (info, "gedit")) +			continue; + +		filtered_items = g_list_prepend (filtered_items, info); +	} + +	/* sort */ +	filtered_items = g_list_sort (filtered_items, +				      (GCompareFunc) sort_recents_mru); + +	i = 0; +	for (l = filtered_items; l != NULL; l = l->next) +	{ +		gchar *action_name; +		const gchar *display_name; +		gchar *escaped; +		gchar *label; +		gchar *uri; +		gchar *ruri; +		gchar *tip; +		GtkAction *action; +		GtkRecentInfo *info = l->data; + +		/* clamp */ +		if (i >= max_recents) +			break; + +		i++; + +		action_name = g_strdup_printf ("recent-info-%d", i); + +		display_name = gtk_recent_info_get_display_name (info); +		escaped = gedit_utils_escape_underscores (display_name, -1); +		if (i >= 10) +			label = g_strdup_printf ("%d.  %s", +						 i,  +						 escaped); +		else +			label = g_strdup_printf ("_%d.  %s", +						 i,  +						 escaped); +		g_free (escaped); + +		/* gtk_recent_info_get_uri_display (info) is buggy and +		 * works only for local files */ +		uri = gedit_utils_uri_for_display (gtk_recent_info_get_uri (info)); +		ruri = gedit_utils_replace_home_dir_with_tilde (uri); +		g_free (uri); + +		/* Translators: %s is a URI */ +		tip = g_strdup_printf (_("Open '%s'"), ruri); +		g_free (ruri); + +		action = gtk_action_new (action_name, +					 label, +					 tip, +					 NULL); + +		g_object_set_data_full (G_OBJECT (action), +					"gtk-recent-info", +					gtk_recent_info_ref (info), +					(GDestroyNotify) gtk_recent_info_unref); + +		g_signal_connect (action, +				  "activate", +				  G_CALLBACK (recents_menu_activate), +				  window); + +		gtk_action_group_add_action (p->recents_action_group, +					     action); +		g_object_unref (action); + +		gtk_ui_manager_add_ui (p->manager, +				       p->recents_menu_ui_id, +				       "/MenuBar/FileMenu/FileRecentsPlaceholder", +				       action_name, +				       action_name, +				       GTK_UI_MANAGER_MENUITEM, +				       FALSE); + +		g_free (action_name); +		g_free (label); +		g_free (tip); +	} + +	g_list_free (filtered_items); + +	g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL); +	g_list_free (items); +} + +static void +set_non_homogeneus (GtkWidget *widget, gpointer data) +{ +	gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); +} + +static void +toolbar_visibility_changed (GtkWidget   *toolbar, +			    GeditWindow *window) +{ +	gboolean visible; +	GtkAction *action; + +	visible = GTK_WIDGET_VISIBLE (toolbar); + +	if (gedit_prefs_manager_toolbar_visible_can_set ()) +		gedit_prefs_manager_set_toolbar_visible (visible); + +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewToolbar"); + +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); +} + +static GtkWidget * +setup_toolbar_open_button (GeditWindow *window, +			   GtkWidget *toolbar) +{ +	GtkRecentManager *recent_manager; +	GtkRecentFilter *filter; +	GtkWidget *toolbar_recent_menu; +	GtkToolItem *open_button; +	GtkAction *action; + +	recent_manager = gtk_recent_manager_get_default (); + +	/* recent files menu tool button */ +	toolbar_recent_menu = gtk_recent_chooser_menu_new_for_manager (recent_manager); + +	gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (toolbar_recent_menu), +					   FALSE); +	gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (toolbar_recent_menu), +					  GTK_RECENT_SORT_MRU); +	gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (toolbar_recent_menu), +				      gedit_prefs_manager_get_max_recents ()); + +	filter = gtk_recent_filter_new (); +	gtk_recent_filter_add_group (filter, "gedit"); +	gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (toolbar_recent_menu), +				       filter); + +	g_signal_connect (toolbar_recent_menu, +			  "item_activated", +			  G_CALLBACK (recent_chooser_item_activated), +			  window); +	 +	/* add the custom Open button to the toolbar */ +	open_button = gtk_menu_tool_button_new_from_stock (GTK_STOCK_OPEN); +	gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_button), +				       toolbar_recent_menu); + +	gtk_tool_item_set_tooltip_text (open_button, _("Open a file")); +	gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_button), +						     _("Open a recently used file")); + +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "FileOpen"); +	g_object_set (action, +		      "is_important", TRUE, +		      "short_label", _("Open"), +		      NULL); +	gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_button), +					    action); + +	gtk_toolbar_insert (GTK_TOOLBAR (toolbar), +			    open_button, +			    1); +	 +	return toolbar_recent_menu; +} + +static void +create_menu_bar_and_toolbar (GeditWindow *window,  +			     GtkWidget   *main_box) +{ +	GtkActionGroup *action_group; +	GtkAction *action; +	GtkUIManager *manager; +	GtkRecentManager *recent_manager; +	GError *error = NULL; +	gchar *ui_file; + +	gedit_debug (DEBUG_WINDOW); + +	manager = gtk_ui_manager_new (); +	window->priv->manager = manager; + +	gtk_window_add_accel_group (GTK_WINDOW (window), +				    gtk_ui_manager_get_accel_group (manager)); + +	action_group = gtk_action_group_new ("GeditWindowAlwaysSensitiveActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	gtk_action_group_add_actions (action_group, +				      gedit_always_sensitive_menu_entries, +				      G_N_ELEMENTS (gedit_always_sensitive_menu_entries), +				      window); +	gtk_action_group_add_toggle_actions (action_group, +					     gedit_always_sensitive_toggle_menu_entries, +					     G_N_ELEMENTS (gedit_always_sensitive_toggle_menu_entries), +					     window); + +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	window->priv->always_sensitive_action_group = action_group; + +	action_group = gtk_action_group_new ("GeditWindowActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	gtk_action_group_add_actions (action_group, +				      gedit_menu_entries, +				      G_N_ELEMENTS (gedit_menu_entries), +				      window); +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	window->priv->action_group = action_group; + +	/* set short labels to use in the toolbar */ +	action = gtk_action_group_get_action (action_group, "FileSave"); +	g_object_set (action, "short_label", _("Save"), NULL); +	action = gtk_action_group_get_action (action_group, "FilePrint"); +	g_object_set (action, "short_label", _("Print"), NULL); +	action = gtk_action_group_get_action (action_group, "SearchFind"); +	g_object_set (action, "short_label", _("Find"), NULL); +	action = gtk_action_group_get_action (action_group, "SearchReplace"); +	g_object_set (action, "short_label", _("Replace"), NULL); + +	/* set which actions should have priority on the toolbar */ +	action = gtk_action_group_get_action (action_group, "FileSave"); +	g_object_set (action, "is_important", TRUE, NULL); +	action = gtk_action_group_get_action (action_group, "EditUndo"); +	g_object_set (action, "is_important", TRUE, NULL); + +	action_group = gtk_action_group_new ("GeditQuitWindowActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	gtk_action_group_add_actions (action_group, +				      gedit_quit_menu_entries, +				      G_N_ELEMENTS (gedit_quit_menu_entries), +				      window); + +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	window->priv->quit_action_group = action_group; + +	action_group = gtk_action_group_new ("GeditCloseWindowActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	gtk_action_group_add_actions (action_group, +	                              gedit_close_menu_entries, +	                              G_N_ELEMENTS (gedit_close_menu_entries), +	                              window); + +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	window->priv->close_action_group = action_group; + +	action_group = gtk_action_group_new ("GeditWindowPanesActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	gtk_action_group_add_toggle_actions (action_group, +					     gedit_panes_toggle_menu_entries, +					     G_N_ELEMENTS (gedit_panes_toggle_menu_entries), +					     window); + +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	window->priv->panes_action_group = action_group; + +	/* now load the UI definition */ +	ui_file = gedit_dirs_get_ui_file (GEDIT_UIFILE); +	gtk_ui_manager_add_ui_from_file (manager, ui_file, &error); +	if (error != NULL) +	{ +		g_warning ("Could not merge %s: %s", ui_file, error->message); +		g_error_free (error); +	} +	g_free (ui_file); + +#if !GTK_CHECK_VERSION (2, 17, 4) +	/* merge page setup menu manually since we cannot have conditional +	 * sections in gedit-ui.xml */ +	{ +		guint merge_id; +		GeditLockdownMask lockdown; + +		merge_id = gtk_ui_manager_new_merge_id (manager); +		gtk_ui_manager_add_ui (manager, +				       merge_id, +				       "/MenuBar/FileMenu/FileOps_5", +				       "FilePageSetupMenu", +				       "FilePageSetup", +				       GTK_UI_MANAGER_MENUITEM, +				       FALSE); + +		lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); +		action = gtk_action_group_get_action (window->priv->action_group, +						      "FilePageSetup"); +		gtk_action_set_sensitive (action,  +					  !(lockdown & GEDIT_LOCKDOWN_PRINT_SETUP)); +	} +#endif + +	/* show tooltips in the statusbar */ +	g_signal_connect (manager, +			  "connect_proxy", +			  G_CALLBACK (connect_proxy_cb), +			  window); +	g_signal_connect (manager, +			  "disconnect_proxy", +			  G_CALLBACK (disconnect_proxy_cb), +			  window); + +	/* recent files menu */ +	action_group = gtk_action_group_new ("RecentFilesActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	window->priv->recents_action_group = action_group; +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); + +	recent_manager = gtk_recent_manager_get_default (); +	window->priv->recents_handler_id = g_signal_connect (recent_manager, +							     "changed", +							     G_CALLBACK (recent_manager_changed), +							     window); +	update_recent_files_menu (window); + +	/* languages menu */ +	action_group = gtk_action_group_new ("LanguagesActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	window->priv->languages_action_group = action_group; +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); +	create_languages_menu (window); + +	/* list of open documents menu */ +	action_group = gtk_action_group_new ("DocumentsListActions"); +	gtk_action_group_set_translation_domain (action_group, NULL); +	window->priv->documents_list_action_group = action_group; +	gtk_ui_manager_insert_action_group (manager, action_group, 0); +	g_object_unref (action_group); + +	window->priv->menubar = gtk_ui_manager_get_widget (manager, "/MenuBar"); +	gtk_box_pack_start (GTK_BOX (main_box),  +			    window->priv->menubar, +			    FALSE,  +			    FALSE,  +			    0); + +	window->priv->toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar"); +	gtk_box_pack_start (GTK_BOX (main_box), +			    window->priv->toolbar, +			    FALSE, +			    FALSE, +			    0); + +	set_toolbar_style (window, NULL); +	 +	window->priv->toolbar_recent_menu = setup_toolbar_open_button (window, +								       window->priv->toolbar); + +	gtk_container_foreach (GTK_CONTAINER (window->priv->toolbar), +			       (GtkCallback)set_non_homogeneus, +			       NULL); + +	g_signal_connect_after (G_OBJECT (window->priv->toolbar), +				"show", +				G_CALLBACK (toolbar_visibility_changed), +				window); +	g_signal_connect_after (G_OBJECT (window->priv->toolbar), +				"hide", +				G_CALLBACK (toolbar_visibility_changed), +				window); +} + +static void +documents_list_menu_activate (GtkToggleAction *action, +			      GeditWindow     *window) +{ +	gint n; + +	if (gtk_toggle_action_get_active (action) == FALSE) +		return; + +	n = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action)); +	gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), n); +} + +static gchar * +get_menu_tip_for_tab (GeditTab *tab) +{ +	GeditDocument *doc; +	gchar *uri; +	gchar *ruri; +	gchar *tip; + +	doc = gedit_tab_get_document (tab); + +	uri = gedit_document_get_uri_for_display (doc); +	ruri = gedit_utils_replace_home_dir_with_tilde (uri); +	g_free (uri); + +	/* Translators: %s is a URI */ +	tip =  g_strdup_printf (_("Activate '%s'"), ruri); +	g_free (ruri); +	 +	return tip; +} + +static void +update_documents_list_menu (GeditWindow *window) +{ +	GeditWindowPrivate *p = window->priv; +	GList *actions, *l; +	gint n, i; +	guint id; +	GSList *group = NULL; + +	gedit_debug (DEBUG_WINDOW); + +	g_return_if_fail (p->documents_list_action_group != NULL); + +	if (p->documents_list_menu_ui_id != 0) +		gtk_ui_manager_remove_ui (p->manager, +					  p->documents_list_menu_ui_id); + +	actions = gtk_action_group_list_actions (p->documents_list_action_group); +	for (l = actions; l != NULL; l = l->next) +	{ +		g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data), +						      G_CALLBACK (documents_list_menu_activate), +						      window); + 		gtk_action_group_remove_action (p->documents_list_action_group, +						GTK_ACTION (l->data)); +	} +	g_list_free (actions); + +	n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (p->notebook)); + +	id = (n > 0) ? gtk_ui_manager_new_merge_id (p->manager) : 0; + +	for (i = 0; i < n; i++) +	{ +		GtkWidget *tab; +		GtkRadioAction *action; +		gchar *action_name; +		gchar *tab_name; +		gchar *name; +		gchar *tip; +		gchar *accel; + +		tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (p->notebook), i); + +		/* NOTE: the action is associated to the position of the tab in +		 * the notebook not to the tab itself! This is needed to work +		 * around the gtk+ bug #170727: gtk leaves around the accels +		 * of the action. Since the accel depends on the tab position +		 * the problem is worked around, action with the same name always +		 * get the same accel. +		 */ +		action_name = g_strdup_printf ("Tab_%d", i); +		tab_name = _gedit_tab_get_name (GEDIT_TAB (tab)); +		name = gedit_utils_escape_underscores (tab_name, -1); +		tip =  get_menu_tip_for_tab (GEDIT_TAB (tab)); + +		/* alt + 1, 2, 3... 0 to switch to the first ten tabs */ +		accel = (i < 10) ? g_strdup_printf ("<alt>%d", (i + 1) % 10) : NULL; + +		action = gtk_radio_action_new (action_name, +					       name, +					       tip, +					       NULL, +					       i); + +		if (group != NULL) +			gtk_radio_action_set_group (action, group); + +		/* note that group changes each time we add an action, so it must be updated */ +		group = gtk_radio_action_get_group (action); + +		gtk_action_group_add_action_with_accel (p->documents_list_action_group, +							GTK_ACTION (action), +							accel); + +		g_signal_connect (action, +				  "activate", +				  G_CALLBACK (documents_list_menu_activate), +				  window); + +		gtk_ui_manager_add_ui (p->manager, +				       id, +				       "/MenuBar/DocumentsMenu/DocumentsListPlaceholder", +				       action_name, action_name, +				       GTK_UI_MANAGER_MENUITEM, +				       FALSE); + +		if (GEDIT_TAB (tab) == p->active_tab) +			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + +		g_object_unref (action); + +		g_free (action_name); +		g_free (tab_name); +		g_free (name); +		g_free (tip); +		g_free (accel); +	} + +	p->documents_list_menu_ui_id = id; +} + +/* Returns TRUE if status bar is visible */ +static gboolean +set_statusbar_style (GeditWindow *window, +		     GeditWindow *origin) +{ +	GtkAction *action; +	 +	gboolean visible; + +	if (origin == NULL) +		visible = gedit_prefs_manager_get_statusbar_visible (); +	else +		visible = GTK_WIDGET_VISIBLE (origin->priv->statusbar); + +	if (visible) +		gtk_widget_show (window->priv->statusbar); +	else +		gtk_widget_hide (window->priv->statusbar); + +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewStatusbar"); +					       +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); +		 +	return visible; +} + +static void +statusbar_visibility_changed (GtkWidget   *statusbar, +			      GeditWindow *window) +{ +	gboolean visible; +	GtkAction *action; + +	visible = GTK_WIDGET_VISIBLE (statusbar); + +	if (gedit_prefs_manager_statusbar_visible_can_set ()) +		gedit_prefs_manager_set_statusbar_visible (visible); + +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewStatusbar"); + +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); +} + +static void +tab_width_combo_changed (GeditStatusComboBox *combo, +			 GtkMenuItem         *item, +			 GeditWindow         *window) +{ +	GeditView *view; +	guint width_data = 0; +	 +	view = gedit_window_get_active_view (window); +	 +	if (!view) +		return; +	 +	width_data = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), TAB_WIDTH_DATA)); +	 +	if (width_data == 0) +		return; +	 +	g_signal_handler_block (view, window->priv->tab_width_id); +	gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (view), width_data); +	g_signal_handler_unblock (view, window->priv->tab_width_id); +} + +static void +use_spaces_toggled (GtkCheckMenuItem *item, +		    GeditWindow      *window) +{ +	GeditView *view; +	 +	view = gedit_window_get_active_view (window); +	 +	g_signal_handler_block (view, window->priv->spaces_instead_of_tabs_id); +	gtk_source_view_set_insert_spaces_instead_of_tabs ( +			GTK_SOURCE_VIEW (view), +			gtk_check_menu_item_get_active (item)); +	g_signal_handler_unblock (view, window->priv->spaces_instead_of_tabs_id); +} + +static void +language_combo_changed (GeditStatusComboBox *combo, +			GtkMenuItem         *item, +			GeditWindow         *window) +{ +	GeditDocument *doc; +	GtkSourceLanguage *language; +	 +	doc = gedit_window_get_active_document (window); +	 +	if (!doc) +		return; +	 +	language = GTK_SOURCE_LANGUAGE (g_object_get_data (G_OBJECT (item), LANGUAGE_DATA)); +	 +	g_signal_handler_block (doc, window->priv->language_changed_id); +	gedit_document_set_language (doc, language); +	g_signal_handler_unblock (doc, window->priv->language_changed_id); +} + +typedef struct +{ +	const gchar *label; +	guint width; +} TabWidthDefinition; +	  +static void +fill_tab_width_combo (GeditWindow *window) +{ +	static TabWidthDefinition defs[] = { +		{"2", 2}, +		{"4", 4}, +		{"8", 8}, +		{"", 0}, /* custom size */ +		{NULL, 0} +	}; +	 +	GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo); +	guint i = 0; +	GtkWidget *item; +	 +	while (defs[i].label != NULL) +	{ +		item = gtk_menu_item_new_with_label (defs[i].label); +		g_object_set_data (G_OBJECT (item), TAB_WIDTH_DATA, GINT_TO_POINTER (defs[i].width)); +		 +		gedit_status_combo_box_add_item (combo, +						 GTK_MENU_ITEM (item), +						 defs[i].label); + +		if (defs[i].width != 0) +			gtk_widget_show (item); + +		++i; +	} +	 +	item = gtk_separator_menu_item_new (); +	gedit_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL); +	gtk_widget_show (item); +	 +	item = gtk_check_menu_item_new_with_label (_("Use Spaces")); +	gedit_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL); +	gtk_widget_show (item); +	 +	g_signal_connect (item,  +			  "toggled",  +			  G_CALLBACK (use_spaces_toggled),  +			  window); +} + +static void +fill_language_combo (GeditWindow *window) +{ +	GtkSourceLanguageManager *manager; +	GSList *languages; +	GSList *item; +	GtkWidget *menu_item; +	const gchar *name; +	 +	manager = gedit_get_language_manager (); +	languages = gedit_language_manager_list_languages_sorted (manager, FALSE); + +	name = _("Plain Text"); +	menu_item = gtk_menu_item_new_with_label (name); +	gtk_widget_show (menu_item); +	 +	g_object_set_data (G_OBJECT (menu_item), LANGUAGE_DATA, NULL); +	gedit_status_combo_box_add_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), +					 GTK_MENU_ITEM (menu_item), +					 name); + +	for (item = languages; item; item = item->next) +	{ +		GtkSourceLanguage *lang = GTK_SOURCE_LANGUAGE (item->data); +		 +		name = gtk_source_language_get_name (lang); +		menu_item = gtk_menu_item_new_with_label (name); +		gtk_widget_show (menu_item); +		 +		g_object_set_data_full (G_OBJECT (menu_item), +				        LANGUAGE_DATA,		 +					g_object_ref (lang), +					(GDestroyNotify)g_object_unref); + +		gedit_status_combo_box_add_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), +						 GTK_MENU_ITEM (menu_item), +						 name); +	} +	 +	g_slist_free (languages); +} + +static void +create_statusbar (GeditWindow *window,  +		  GtkWidget   *main_box) +{ +	gedit_debug (DEBUG_WINDOW); + +	window->priv->statusbar = gedit_statusbar_new (); + +	window->priv->generic_message_cid = gtk_statusbar_get_context_id +		(GTK_STATUSBAR (window->priv->statusbar), "generic_message"); +	window->priv->tip_message_cid = gtk_statusbar_get_context_id +		(GTK_STATUSBAR (window->priv->statusbar), "tip_message"); + +	gtk_box_pack_end (GTK_BOX (main_box), +			  window->priv->statusbar, +			  FALSE,  +			  TRUE,  +			  0); + +	window->priv->tab_width_combo = gedit_status_combo_box_new (_("Tab Width")); +	gtk_widget_show (window->priv->tab_width_combo); +	gtk_box_pack_end (GTK_BOX (window->priv->statusbar), +			  window->priv->tab_width_combo, +			  FALSE, +			  TRUE, +			  0); + +	fill_tab_width_combo (window); + +	g_signal_connect (G_OBJECT (window->priv->tab_width_combo), +			  "changed", +			  G_CALLBACK (tab_width_combo_changed), +			  window); +			   +	window->priv->language_combo = gedit_status_combo_box_new (NULL); +	gtk_widget_show (window->priv->language_combo); +	gtk_box_pack_end (GTK_BOX (window->priv->statusbar), +			  window->priv->language_combo, +			  FALSE, +			  TRUE, +			  0); + +	fill_language_combo (window); + +	g_signal_connect (G_OBJECT (window->priv->language_combo), +			  "changed", +			  G_CALLBACK (language_combo_changed), +			  window); + +	g_signal_connect_after (G_OBJECT (window->priv->statusbar), +				"show", +				G_CALLBACK (statusbar_visibility_changed), +				window); +	g_signal_connect_after (G_OBJECT (window->priv->statusbar), +				"hide", +				G_CALLBACK (statusbar_visibility_changed), +				window); + +	set_statusbar_style (window, NULL); +} + +static GeditWindow * +clone_window (GeditWindow *origin) +{ +	GeditWindow *window; +	GdkScreen *screen; +	GeditApp  *app; +	gint panel_page; + +	gedit_debug (DEBUG_WINDOW);	 + +	app = gedit_app_get_default (); + +	screen = gtk_window_get_screen (GTK_WINDOW (origin)); +	window = gedit_app_create_window (app, screen); + +	if ((origin->priv->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) +	{ +		gint w, h; + +		gedit_prefs_manager_get_default_window_size (&w, &h); +		gtk_window_set_default_size (GTK_WINDOW (window), w, h); +		gtk_window_maximize (GTK_WINDOW (window)); +	} +	else +	{ +		gtk_window_set_default_size (GTK_WINDOW (window), +					     origin->priv->width, +					     origin->priv->height); + +		gtk_window_unmaximize (GTK_WINDOW (window)); +	}		 + +	if ((origin->priv->window_state & GDK_WINDOW_STATE_STICKY ) != 0) +		gtk_window_stick (GTK_WINDOW (window)); +	else +		gtk_window_unstick (GTK_WINDOW (window)); + +	/* set the panes size, the paned position will be set when +	 * they are mapped */  +	window->priv->side_panel_size = origin->priv->side_panel_size; +	window->priv->bottom_panel_size = origin->priv->bottom_panel_size; + +	panel_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (origin->priv->side_panel)); +	_gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->side_panel), +					    panel_page); + +	panel_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (origin->priv->bottom_panel)); +	_gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->bottom_panel), +					    panel_page); + +	if (GTK_WIDGET_VISIBLE (origin->priv->side_panel)) +		gtk_widget_show (window->priv->side_panel); +	else +		gtk_widget_hide (window->priv->side_panel); + +	if (GTK_WIDGET_VISIBLE (origin->priv->bottom_panel)) +		gtk_widget_show (window->priv->bottom_panel); +	else +		gtk_widget_hide (window->priv->bottom_panel); + +	set_statusbar_style (window, origin); +	set_toolbar_style (window, origin); + +	return window; +} + +static void +update_cursor_position_statusbar (GtkTextBuffer *buffer,  +				  GeditWindow   *window) +{ +	gint row, col; +	GtkTextIter iter; +	GtkTextIter start; +	guint tab_size; +	GeditView *view; + +	gedit_debug (DEBUG_WINDOW); +   + 	if (buffer != GTK_TEXT_BUFFER (gedit_window_get_active_document (window))) + 		return; + 		 + 	view = gedit_window_get_active_view (window); + 	 +	gtk_text_buffer_get_iter_at_mark (buffer, +					  &iter, +					  gtk_text_buffer_get_insert (buffer)); +	 +	row = gtk_text_iter_get_line (&iter); +	 +	start = iter; +	gtk_text_iter_set_line_offset (&start, 0); +	col = 0; + +	tab_size = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view)); + +	while (!gtk_text_iter_equal (&start, &iter)) +	{ +		/* FIXME: Are we Unicode compliant here? */ +		if (gtk_text_iter_get_char (&start) == '\t') +					 +			col += (tab_size - (col  % tab_size)); +		else +			++col; + +		gtk_text_iter_forward_char (&start); +	} +	 +	gedit_statusbar_set_cursor_position ( +				GEDIT_STATUSBAR (window->priv->statusbar), +				row + 1, +				col + 1); +} + +static void +update_overwrite_mode_statusbar (GtkTextView *view,  +				 GeditWindow *window) +{ +	if (view != GTK_TEXT_VIEW (gedit_window_get_active_view (window))) +		return; +		 +	/* Note that we have to use !gtk_text_view_get_overwrite since we +	   are in the in the signal handler of "toggle overwrite" that is +	   G_SIGNAL_RUN_LAST +	*/ +	gedit_statusbar_set_overwrite ( +			GEDIT_STATUSBAR (window->priv->statusbar), +			!gtk_text_view_get_overwrite (view)); +} + +#define MAX_TITLE_LENGTH 100 + +static void  +set_title (GeditWindow *window) +{ +	GeditDocument *doc = NULL; +	gchar *name; +	gchar *dirname = NULL; +	gchar *title = NULL; +	gint len; + +	if (window->priv->active_tab == NULL) +	{ +#ifdef OS_OSX +		gedit_osx_set_window_title (window, "gedit", NULL); +#else +		gtk_window_set_title (GTK_WINDOW (window), "gedit"); +#endif +		return; +	} + +	doc = gedit_tab_get_document (window->priv->active_tab); +	g_return_if_fail (doc != NULL); + +	name = gedit_document_get_short_name_for_display (doc); + +	len = g_utf8_strlen (name, -1); + +	/* if the name is awfully long, truncate it and be done with it, +	 * otherwise also show the directory (ellipsized if needed) +	 */ +	if (len > MAX_TITLE_LENGTH) +	{ +		gchar *tmp; + +		tmp = gedit_utils_str_middle_truncate (name, +						       MAX_TITLE_LENGTH); +		g_free (name); +		name = tmp; +	} +	else +	{ +		GFile *file; + +		file = gedit_document_get_location (doc); +		if (file != NULL) +		{ +			gchar *str; + +			str = gedit_utils_location_get_dirname_for_display (file); +			g_object_unref (file); + +			/* use the remaining space for the dir, but use a min of 20 chars +			 * so that we do not end up with a dirname like "(a...b)". +			 * This means that in the worst case when the filename is long 99 +			 * we have a title long 99 + 20, but I think it's a rare enough +			 * case to be acceptable. It's justa darn title afterall :) +			 */ +			dirname = gedit_utils_str_middle_truncate (str,  +								   MAX (20, MAX_TITLE_LENGTH - len)); +			g_free (str); +		} +	} + +	if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) +	{ +		gchar *tmp_name; +		 +		tmp_name = g_strdup_printf ("*%s", name); +		g_free (name); +		 +		name = tmp_name; +	}		 + +	if (gedit_document_get_readonly (doc))  +	{ +		if (dirname != NULL) +			title = g_strdup_printf ("%s [%s] (%s) - gedit",  +						 name,  +						 _("Read-Only"),  +						 dirname); +		else +			title = g_strdup_printf ("%s [%s] - gedit",  +						 name,  +						 _("Read-Only")); +	}  +	else  +	{ +		if (dirname != NULL) +			title = g_strdup_printf ("%s (%s) - gedit",  +						 name,  +						 dirname); +		else +			title = g_strdup_printf ("%s - gedit",  +						 name); +	} + +#ifdef OS_OSX +	gedit_osx_set_window_title (window, title, doc); +#else +	gtk_window_set_title (GTK_WINDOW (window), title); +#endif + +	g_free (dirname); +	g_free (name); +	g_free (title); +} + +#undef MAX_TITLE_LENGTH + +static void +set_tab_width_item_blocked (GeditWindow *window, +			    GtkMenuItem *item) +{ +	g_signal_handlers_block_by_func (window->priv->tab_width_combo,  +					 tab_width_combo_changed,  +					 window); +	 +	gedit_status_combo_box_set_item (GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo), +					 item); +	 +	g_signal_handlers_unblock_by_func (window->priv->tab_width_combo,  +					   tab_width_combo_changed,  +					   window); +} + +static void +spaces_instead_of_tabs_changed (GObject     *object, +		   		GParamSpec  *pspec, +		 		GeditWindow *window) +{ +	GeditView *view = GEDIT_VIEW (object); +	gboolean active = gtk_source_view_get_insert_spaces_instead_of_tabs ( +			GTK_SOURCE_VIEW (view)); +	GList *children = gedit_status_combo_box_get_items ( +			GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo)); +	GtkCheckMenuItem *item; +	 +	item = GTK_CHECK_MENU_ITEM (g_list_last (children)->data); +	 +	gtk_check_menu_item_set_active (item, active); +	 +	g_list_free (children);	 +} + +static void +tab_width_changed (GObject     *object, +		   GParamSpec  *pspec, +		   GeditWindow *window) +{ +	GList *items; +	GList *item; +	GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo); +	guint new_tab_width; +	gboolean found = FALSE; + +	items = gedit_status_combo_box_get_items (combo); + +	new_tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (object)); +	 +	for (item = items; item; item = item->next) +	{ +		guint tab_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item->data), TAB_WIDTH_DATA)); +		 +		if (tab_width == new_tab_width) +		{ +			set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data)); +			found = TRUE; +		} +		 +		if (GTK_IS_SEPARATOR_MENU_ITEM (item->next->data)) +		{ +			if (!found) +			{ +				/* Set for the last item the custom thing */ +				gchar *text; +			 +				text = g_strdup_printf ("%u", new_tab_width); +				gedit_status_combo_box_set_item_text (combo,  +								      GTK_MENU_ITEM (item->data), +								      text); + +				gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->data))), +						    text); +			 +				set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data)); +				gtk_widget_show (GTK_WIDGET (item->data)); +			} +			else +			{ +				gtk_widget_hide (GTK_WIDGET (item->data)); +			} +			 +			break; +		} +	} +	 +	g_list_free (items); +} + +static void +language_changed (GObject     *object, +		  GParamSpec  *pspec, +		  GeditWindow *window) +{ +	GList *items; +	GList *item; +	GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->language_combo); +	GtkSourceLanguage *new_language; +	const gchar *new_id; +	 +	items = gedit_status_combo_box_get_items (combo); + +	new_language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (object)); +	 +	if (new_language) +		new_id = gtk_source_language_get_id (new_language); +	else +		new_id = NULL; +	 +	for (item = items; item; item = item->next) +	{ +		GtkSourceLanguage *lang = g_object_get_data (G_OBJECT (item->data), LANGUAGE_DATA); +		 +		if ((new_id == NULL && lang == NULL) ||  +		    (new_id != NULL && lang != NULL && strcmp (gtk_source_language_get_id (lang), +		    					       new_id) == 0)) +		{ +			g_signal_handlers_block_by_func (window->priv->language_combo,  +							 language_combo_changed,  +					 		 window); +			 +			gedit_status_combo_box_set_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), +					 		 GTK_MENU_ITEM (item->data)); + +			g_signal_handlers_unblock_by_func (window->priv->language_combo,  +							   language_combo_changed,  +					 		   window); +		} +	} + +	g_list_free (items); +} + +static void  +notebook_switch_page (GtkNotebook     *book,  +		      GtkNotebookPage *pg, +		      gint             page_num,  +		      GeditWindow     *window) +{ +	GeditView *view; +	GeditTab *tab; +	GtkAction *action; +	gchar *action_name; +	 +	/* CHECK: I don't know why but it seems notebook_switch_page is called +	two times every time the user change the active tab */ +	 +	tab = GEDIT_TAB (gtk_notebook_get_nth_page (book, page_num)); +	if (tab == window->priv->active_tab) +		return; +	 +	if (window->priv->active_tab) +	{ +		if (window->priv->tab_width_id) +		{ +			g_signal_handler_disconnect (gedit_tab_get_view (window->priv->active_tab),  +						     window->priv->tab_width_id); +		 +			window->priv->tab_width_id = 0; +		} +		 +		if (window->priv->spaces_instead_of_tabs_id) +		{ +			g_signal_handler_disconnect (gedit_tab_get_view (window->priv->active_tab),  +						     window->priv->spaces_instead_of_tabs_id); +		 +			window->priv->spaces_instead_of_tabs_id = 0; +		} +	} +	 +	/* set the active tab */		 +	window->priv->active_tab = tab; + +	set_title (window); +	set_sensitivity_according_to_tab (window, tab); + +	/* activate the right item in the documents menu */ +	action_name = g_strdup_printf ("Tab_%d", page_num); +	action = gtk_action_group_get_action (window->priv->documents_list_action_group, +					      action_name); + +	/* sometimes the action doesn't exist yet, and the proper action +	 * is set active during the documents list menu creation +	 * CHECK: would it be nicer if active_tab was a property and we monitored the notify signal? +	 */ +	if (action != NULL) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + +	g_free (action_name); + +	/* update the syntax menu */ +	update_languages_menu (window); + +	view = gedit_tab_get_view (tab); + +	/* sync the statusbar */ +	update_cursor_position_statusbar (GTK_TEXT_BUFFER (gedit_tab_get_document (tab)), +					  window); +	gedit_statusbar_set_overwrite (GEDIT_STATUSBAR (window->priv->statusbar), +				       gtk_text_view_get_overwrite (GTK_TEXT_VIEW (view))); + +	gtk_widget_show (window->priv->tab_width_combo); +	gtk_widget_show (window->priv->language_combo); + +	window->priv->tab_width_id = g_signal_connect (view,  +						       "notify::tab-width",  +						       G_CALLBACK (tab_width_changed), +						       window); +	window->priv->spaces_instead_of_tabs_id = g_signal_connect (view,  +								    "notify::insert-spaces-instead-of-tabs",  +								    G_CALLBACK (spaces_instead_of_tabs_changed), +								    window); + +	window->priv->language_changed_id = g_signal_connect (gedit_tab_get_document (tab), +							      "notify::language", +							      G_CALLBACK (language_changed), +							      window); + +	/* call it for the first time */ +	tab_width_changed (G_OBJECT (view), NULL, window); +	spaces_instead_of_tabs_changed (G_OBJECT (view), NULL, window); +	language_changed (G_OBJECT (gedit_tab_get_document (tab)), NULL, window); + +	g_signal_emit (G_OBJECT (window),  +		       signals[ACTIVE_TAB_CHANGED],  +		       0,  +		       window->priv->active_tab);				        +} + +static void +set_sensitivity_according_to_window_state (GeditWindow *window) +{ +	GtkAction *action; +	GeditLockdownMask lockdown; + +	lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); + +	/* We disable File->Quit/SaveAll/CloseAll while printing to avoid to have two +	   operations (save and print/print preview) that uses the message area at +	   the same time (may be we can remove this limitation in the future) */ +	/* We disable File->Quit/CloseAll if state is saving since saving cannot be +	   cancelled (may be we can remove this limitation in the future) */ +	gtk_action_group_set_sensitive (window->priv->quit_action_group, +				  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && +				  !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); + +	action = gtk_action_group_get_action (window->priv->action_group, +				              "FileCloseAll"); +	gtk_action_set_sensitive (action,  +				  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && +				  !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); + +	action = gtk_action_group_get_action (window->priv->action_group, +				              "FileSaveAll"); +	gtk_action_set_sensitive (action,  +				  !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && +				  !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); +			 +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "FileNew"); +	gtk_action_set_sensitive (action,  +				  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); +				   +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "FileOpen"); +	gtk_action_set_sensitive (action,  +				  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + +	gtk_action_group_set_sensitive (window->priv->recents_action_group, +					!(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + +	gedit_notebook_set_close_buttons_sensitive (GEDIT_NOTEBOOK (window->priv->notebook), +						    !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); +						     +	gedit_notebook_set_tab_drag_and_drop_enabled (GEDIT_NOTEBOOK (window->priv->notebook), +						      !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + +	if ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) != 0) +	{ +		/* TODO: If we really care, Find could be active +		 * when in SAVING_SESSION state */ + +		if (gtk_action_group_get_sensitive (window->priv->action_group)) +			gtk_action_group_set_sensitive (window->priv->action_group, +							FALSE); +		if (gtk_action_group_get_sensitive (window->priv->quit_action_group)) +			gtk_action_group_set_sensitive (window->priv->quit_action_group, +							FALSE); +		if (gtk_action_group_get_sensitive (window->priv->close_action_group)) +			gtk_action_group_set_sensitive (window->priv->close_action_group, +							FALSE); +	} +	else +	{ +		if (!gtk_action_group_get_sensitive (window->priv->action_group)) +			gtk_action_group_set_sensitive (window->priv->action_group, +							window->priv->num_tabs > 0); +		if (!gtk_action_group_get_sensitive (window->priv->quit_action_group)) +			gtk_action_group_set_sensitive (window->priv->quit_action_group, +							window->priv->num_tabs > 0); +		if (!gtk_action_group_get_sensitive (window->priv->close_action_group)) +		{ +#ifdef OS_OSX +			/* On OS X, File Close is always sensitive */ +			gtk_action_group_set_sensitive (window->priv->close_action_group, +							TRUE); +#else +			gtk_action_group_set_sensitive (window->priv->close_action_group, +							window->priv->num_tabs > 0); +#endif +		} +	} +} + +static void +update_tab_autosave (GtkWidget *widget, +		     gpointer   data) +{ +	GeditTab *tab = GEDIT_TAB (widget); +	gboolean *enabled = (gboolean *) data; + +	gedit_tab_set_auto_save_enabled (tab, *enabled); +} + +void +_gedit_window_set_lockdown (GeditWindow       *window, +			    GeditLockdownMask  lockdown) +{ +	GeditTab *tab; +	GtkAction *action; +	gboolean autosave; + +	/* start/stop autosave in each existing tab */ +	autosave = gedit_prefs_manager_get_auto_save (); +	gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), +			       update_tab_autosave, +			       &autosave); + +	/* update menues wrt the current active tab */	 +	tab = gedit_window_get_active_tab (window); + +	set_sensitivity_according_to_tab (window, tab); + +	action = gtk_action_group_get_action (window->priv->action_group, +				              "FileSaveAll"); +	gtk_action_set_sensitive (action,  +				  !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && +				  !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + +#if !GTK_CHECK_VERSION (2, 17, 4) +	action = gtk_action_group_get_action (window->priv->action_group, +				              "FilePageSetup"); +	gtk_action_set_sensitive (action,  +				  !(lockdown & GEDIT_LOCKDOWN_PRINT_SETUP)); +#endif +} + +static void +analyze_tab_state (GeditTab    *tab,  +		   GeditWindow *window) +{ +	GeditTabState ts; +	 +	ts = gedit_tab_get_state (tab); +	 +	switch (ts) +	{ +		case GEDIT_TAB_STATE_LOADING: +		case GEDIT_TAB_STATE_REVERTING: +			window->priv->state |= GEDIT_WINDOW_STATE_LOADING; +			break; +		 +		case GEDIT_TAB_STATE_SAVING: +			window->priv->state |= GEDIT_WINDOW_STATE_SAVING; +			break; +			 +		case GEDIT_TAB_STATE_PRINTING: +		case GEDIT_TAB_STATE_PRINT_PREVIEWING: +			window->priv->state |= GEDIT_WINDOW_STATE_PRINTING; +			break; +	 +		case GEDIT_TAB_STATE_LOADING_ERROR: +		case GEDIT_TAB_STATE_REVERTING_ERROR: +		case GEDIT_TAB_STATE_SAVING_ERROR: +		case GEDIT_TAB_STATE_GENERIC_ERROR: +			window->priv->state |= GEDIT_WINDOW_STATE_ERROR; +			++window->priv->num_tabs_with_error; +		default: +			/* NOP */ +			break;		 +	} +} + +static void +update_window_state (GeditWindow *window) +{ +	GeditWindowState old_ws; +	gint old_num_of_errors; +	 +	gedit_debug_message (DEBUG_WINDOW, "Old state: %x", window->priv->state); +	 +	old_ws = window->priv->state; +	old_num_of_errors = window->priv->num_tabs_with_error; +	 +	window->priv->state = old_ws & GEDIT_WINDOW_STATE_SAVING_SESSION; +	 +	window->priv->num_tabs_with_error = 0; + +	gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), +	       		       (GtkCallback)analyze_tab_state, +	       		       window); +		 +	gedit_debug_message (DEBUG_WINDOW, "New state: %x", window->priv->state);		 +		 +	if (old_ws != window->priv->state) +	{ +		set_sensitivity_according_to_window_state (window); + +		gedit_statusbar_set_window_state (GEDIT_STATUSBAR (window->priv->statusbar), +						  window->priv->state, +						  window->priv->num_tabs_with_error); +						   +		g_object_notify (G_OBJECT (window), "state"); +	} +	else if (old_num_of_errors != window->priv->num_tabs_with_error) +	{ +		gedit_statusbar_set_window_state (GEDIT_STATUSBAR (window->priv->statusbar), +						  window->priv->state, +						  window->priv->num_tabs_with_error);	 +	} +} + +static void +sync_state (GeditTab    *tab, +	    GParamSpec  *pspec, +	    GeditWindow *window) +{ +	gedit_debug (DEBUG_WINDOW); +	 +	update_window_state (window); +	 +	if (tab != window->priv->active_tab) +		return; + +	set_sensitivity_according_to_tab (window, tab); + +	g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_STATE_CHANGED], 0);		 +} + +static void +sync_name (GeditTab    *tab, +	   GParamSpec  *pspec, +	   GeditWindow *window) +{ +	GtkAction *action; +	gchar *action_name; +	gchar *tab_name; +	gchar *escaped_name; +	gchar *tip; +	gint n; +	GeditDocument *doc; + +	if (tab == window->priv->active_tab) +	{ +		set_title (window); + +		doc = gedit_tab_get_document (tab); +		action = gtk_action_group_get_action (window->priv->action_group, +						      "FileRevert"); +		gtk_action_set_sensitive (action, +					  !gedit_document_is_untitled (doc)); +	} + +	/* sync the item in the documents list menu */ +	n = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), +				   GTK_WIDGET (tab)); +	action_name = g_strdup_printf ("Tab_%d", n); +	action = gtk_action_group_get_action (window->priv->documents_list_action_group, +					      action_name); +	g_return_if_fail (action != NULL); + +	tab_name = _gedit_tab_get_name (tab); +	escaped_name = gedit_utils_escape_underscores (tab_name, -1); +	tip =  get_menu_tip_for_tab (tab); + +	g_object_set (action, "label", escaped_name, NULL); +	g_object_set (action, "tooltip", tip, NULL); + +	g_free (action_name); +	g_free (tab_name); +	g_free (escaped_name); +	g_free (tip); + +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static GeditWindow * +get_drop_window (GtkWidget *widget) +{ +	GtkWidget *target_window; + +	target_window = gtk_widget_get_toplevel (widget); +	g_return_val_if_fail (GEDIT_IS_WINDOW (target_window), NULL); + +	if ((GEDIT_WINDOW(target_window)->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) != 0) +		return NULL; +	 +	return GEDIT_WINDOW (target_window); +} + +static void +load_uris_from_drop (GeditWindow  *window, +		     gchar       **uri_list) +{ +	GSList *uris = NULL; +	gint i; +	 +	if (uri_list == NULL) +		return; +	 +	for (i = 0; uri_list[i] != NULL; ++i) +	{ +		uris = g_slist_prepend (uris, uri_list[i]); +	} + +	uris = g_slist_reverse (uris); +	gedit_commands_load_uris (window, +				  uris, +				  NULL, +				  0); + +	g_slist_free (uris); +} + +/* Handle drops on the GeditWindow */ +static void +drag_data_received_cb (GtkWidget        *widget, +		       GdkDragContext   *context, +		       gint              x, +		       gint              y, +		       GtkSelectionData *selection_data, +		       guint             info, +		       guint             timestamp, +		       gpointer          data) +{ +	GeditWindow *window; +	gchar **uri_list; + +	window = get_drop_window (widget); +	 +	if (window == NULL) +		return; + +	if (info == TARGET_URI_LIST) +	{ +		uri_list = gedit_utils_drop_get_uris(selection_data); +		load_uris_from_drop (window, uri_list); +		g_strfreev (uri_list); +	} +} + +/* Handle drops on the GeditView */ +static void +drop_uris_cb (GtkWidget    *widget, +	      gchar       **uri_list) +{ +	GeditWindow *window; + +	window = get_drop_window (widget); +	 +	if (window == NULL) +		return; + +	load_uris_from_drop (window, uri_list); +} + +static void +fullscreen_controls_show (GeditWindow *window) +{ +	GdkScreen *screen; +	GdkRectangle fs_rect; +	gint w, h; + +	screen = gtk_window_get_screen (GTK_WINDOW (window)); +	gdk_screen_get_monitor_geometry (screen, +					 gdk_screen_get_monitor_at_window (screen, +									   gtk_widget_get_window (GTK_WIDGET (window))), +					 &fs_rect); + +	gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h); + +	gtk_window_resize (GTK_WINDOW (window->priv->fullscreen_controls), +			   fs_rect.width, h); + +	gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), +			 fs_rect.x, fs_rect.y - h + 1); + +	gtk_widget_show_all (window->priv->fullscreen_controls); +} + +static gboolean +run_fullscreen_animation (gpointer data) +{ +	GeditWindow *window = GEDIT_WINDOW (data); +	GdkScreen *screen; +	GdkRectangle fs_rect; +	gint x, y; +	 +	screen = gtk_window_get_screen (GTK_WINDOW (window)); +	gdk_screen_get_monitor_geometry (screen, +					 gdk_screen_get_monitor_at_window (screen, +									   gtk_widget_get_window (GTK_WIDGET (window))), +					 &fs_rect); +					  +	gtk_window_get_position (GTK_WINDOW (window->priv->fullscreen_controls), +				 &x, &y); +	 +	if (window->priv->fullscreen_animation_enter) +	{ +		if (y == fs_rect.y) +		{ +			window->priv->fullscreen_animation_timeout_id = 0; +			return FALSE; +		} +		else +		{ +			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), +					 x, y + 1); +			return TRUE; +		} +	} +	else +	{ +		gint w, h; +	 +		gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), +				     &w, &h); +	 +		if (y == fs_rect.y - h + 1) +		{ +			window->priv->fullscreen_animation_timeout_id = 0; +			return FALSE; +		} +		else +		{ +			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), +					 x, y - 1); +			return TRUE; +		} +	} +} + +static void +show_hide_fullscreen_toolbar (GeditWindow *window, +			      gboolean     show, +			      gint         height) +{ +	GtkSettings *settings; +	gboolean enable_animations; + +	settings = gtk_widget_get_settings (GTK_WIDGET (window)); +	g_object_get (G_OBJECT (settings), +		      "gtk-enable-animations", +		      &enable_animations, +		      NULL); + +	if (enable_animations) +	{ +		window->priv->fullscreen_animation_enter = show; + +		if (window->priv->fullscreen_animation_timeout_id == 0) +		{ +			window->priv->fullscreen_animation_timeout_id = +				g_timeout_add (FULLSCREEN_ANIMATION_SPEED, +					       (GSourceFunc) run_fullscreen_animation, +					       window); +		} +	} +	else +	{ +		GdkRectangle fs_rect; +		GdkScreen *screen; + +		screen = gtk_window_get_screen (GTK_WINDOW (window)); +		gdk_screen_get_monitor_geometry (screen, +						 gdk_screen_get_monitor_at_window (screen, +										   gtk_widget_get_window (GTK_WIDGET (window))), +						 &fs_rect); + +		if (show) +			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), +				 fs_rect.x, fs_rect.y); +		else +			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), +					 fs_rect.x, fs_rect.y - height + 1); +	} + +} + +static gboolean +on_fullscreen_controls_enter_notify_event (GtkWidget        *widget, +					   GdkEventCrossing *event, +					   GeditWindow      *window) +{ +	show_hide_fullscreen_toolbar (window, TRUE, 0); + +	return FALSE; +} + +static gboolean +on_fullscreen_controls_leave_notify_event (GtkWidget        *widget, +					   GdkEventCrossing *event, +					   GeditWindow      *window) +{ +	GdkDisplay *display; +	GdkScreen *screen; +	gint w, h; +	gint x, y; + +	display = gdk_display_get_default (); +	screen = gtk_window_get_screen (GTK_WINDOW (window)); + +	gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h); +	gdk_display_get_pointer (display, &screen, &x, &y, NULL); +	 +	/* gtk seems to emit leave notify when clicking on tool items, +	 * work around it by checking the coordinates +	 */ +	if (y >= h) +	{ +		show_hide_fullscreen_toolbar (window, FALSE, h); +	} + +	return FALSE; +} + +static void +fullscreen_controls_build (GeditWindow *window) +{ +	GeditWindowPrivate *priv = window->priv; +	GtkWidget *toolbar; +	GtkWidget *toolbar_recent_menu; +	GtkAction *action; + +	if (priv->fullscreen_controls != NULL) +		return; +	 +	priv->fullscreen_controls = gtk_window_new (GTK_WINDOW_POPUP); + +	gtk_window_set_transient_for (GTK_WINDOW (priv->fullscreen_controls), +				      &window->window); +	 +	/* popup toolbar */ +	toolbar = gtk_ui_manager_get_widget (priv->manager, "/FullscreenToolBar"); +	gtk_container_add (GTK_CONTAINER (priv->fullscreen_controls), +			   toolbar); +	 +	action = gtk_action_group_get_action (priv->always_sensitive_action_group, +					      "LeaveFullscreen"); +	g_object_set (action, "is-important", TRUE, NULL); +	 +	toolbar_recent_menu = setup_toolbar_open_button (window, toolbar); + +	gtk_container_foreach (GTK_CONTAINER (toolbar), +			       (GtkCallback)set_non_homogeneus, +			       NULL); + +	/* Set the toolbar style */ +	gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), +			       GTK_TOOLBAR_BOTH_HORIZ); + +	g_signal_connect (priv->fullscreen_controls, "enter-notify-event", +			  G_CALLBACK (on_fullscreen_controls_enter_notify_event), +			  window); +	g_signal_connect (priv->fullscreen_controls, "leave-notify-event", +			  G_CALLBACK (on_fullscreen_controls_leave_notify_event), +			  window); +} + +static void +can_search_again (GeditDocument *doc, +		  GParamSpec    *pspec, +		  GeditWindow   *window) +{ +	gboolean sensitive; +	GtkAction *action; + +	if (doc != gedit_window_get_active_document (window)) +		return; + +	sensitive = gedit_document_get_can_search_again (doc); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchFindNext"); +	gtk_action_set_sensitive (action, sensitive); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchFindPrevious"); +	gtk_action_set_sensitive (action, sensitive); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "SearchClearHighlight"); +	gtk_action_set_sensitive (action, sensitive); +} + +static void +can_undo (GeditDocument *doc, +	  GParamSpec    *pspec, +	  GeditWindow   *window) +{ +	GtkAction *action; +	gboolean sensitive; + +	sensitive = gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc)); + +	if (doc != gedit_window_get_active_document (window)) +		return; + +	action = gtk_action_group_get_action (window->priv->action_group, +					     "EditUndo"); +	gtk_action_set_sensitive (action, sensitive); +} + +static void +can_redo (GeditDocument *doc, +	  GParamSpec    *pspec, +	  GeditWindow   *window) +{ +	GtkAction *action; +	gboolean sensitive; + +	sensitive = gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc)); + +	if (doc != gedit_window_get_active_document (window)) +		return; + +	action = gtk_action_group_get_action (window->priv->action_group, +					     "EditRedo"); +	gtk_action_set_sensitive (action, sensitive); +} + +static void +selection_changed (GeditDocument *doc, +		   GParamSpec    *pspec, +		   GeditWindow   *window) +{ +	GeditTab *tab; +	GeditView *view; +	GtkAction *action; +	GeditTabState state; +	gboolean state_normal; +	gboolean editable; + +	gedit_debug (DEBUG_WINDOW); + +	if (doc != gedit_window_get_active_document (window)) +		return; + +	tab = gedit_tab_get_from_document (doc); +	state = gedit_tab_get_state (tab); +	state_normal = (state == GEDIT_TAB_STATE_NORMAL); + +	view = gedit_tab_get_view (tab); +	editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view)); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditCut"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  editable && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditCopy"); +	gtk_action_set_sensitive (action, +				  (state_normal || +				   state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + +	action = gtk_action_group_get_action (window->priv->action_group, +					      "EditDelete"); +	gtk_action_set_sensitive (action, +				  state_normal && +				  editable && +				  gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static void +sync_languages_menu (GeditDocument *doc, +		     GParamSpec    *pspec, +		     GeditWindow   *window) +{ +	update_languages_menu (window); +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static void +readonly_changed (GeditDocument *doc, +		  GParamSpec    *pspec, +		  GeditWindow   *window) +{ +	set_sensitivity_according_to_tab (window, window->priv->active_tab); + +	sync_name (window->priv->active_tab, NULL, window); + +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static void +editable_changed (GeditView  *view, +                  GParamSpec  *arg1, +                  GeditWindow *window) +{ +	gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +						 window); +} + +static void +update_sensitivity_according_to_open_tabs (GeditWindow *window) +{ +	GtkAction *action; + +	/* Set sensitivity */ +	gtk_action_group_set_sensitive (window->priv->action_group, +					window->priv->num_tabs != 0); + +	action = gtk_action_group_get_action (window->priv->action_group, +					     "DocumentsMoveToNewWindow"); +	gtk_action_set_sensitive (action, +				  window->priv->num_tabs > 1); + +	/* Do not set close action insensitive on OS X */ +#ifndef OS_OSX +	gtk_action_group_set_sensitive (window->priv->close_action_group, +	                                window->priv->num_tabs != 0); +#endif +} + +static void +notebook_tab_added (GeditNotebook *notebook, +		    GeditTab      *tab, +		    GeditWindow   *window) +{ +	GeditView *view; +	GeditDocument *doc; + +	gedit_debug (DEBUG_WINDOW); + +	g_return_if_fail ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) == 0); +	 +	++window->priv->num_tabs; + +	update_sensitivity_according_to_open_tabs (window); + +	view = gedit_tab_get_view (tab); +	doc = gedit_tab_get_document (tab); + +	/* IMPORTANT: remember to disconnect the signal in notebook_tab_removed +	 * if a new signal is connected here */ + +	g_signal_connect (tab,  +			 "notify::name", +			  G_CALLBACK (sync_name),  +			  window); +	g_signal_connect (tab,  +			 "notify::state", +			  G_CALLBACK (sync_state),  +			  window); + +	g_signal_connect (doc, +			  "cursor-moved", +			  G_CALLBACK (update_cursor_position_statusbar), +			  window); +	g_signal_connect (doc, +			  "notify::can-search-again", +			  G_CALLBACK (can_search_again), +			  window); +	g_signal_connect (doc, +			  "notify::can-undo", +			  G_CALLBACK (can_undo), +			  window); +	g_signal_connect (doc, +			  "notify::can-redo", +			  G_CALLBACK (can_redo), +			  window); +	g_signal_connect (doc, +			  "notify::has-selection", +			  G_CALLBACK (selection_changed), +			  window); +	g_signal_connect (doc, +			  "notify::language", +			  G_CALLBACK (sync_languages_menu), +			  window); +	g_signal_connect (doc, +			  "notify::read-only", +			  G_CALLBACK (readonly_changed), +			  window); +	g_signal_connect (view, +			  "toggle_overwrite", +			  G_CALLBACK (update_overwrite_mode_statusbar), +			  window); +	g_signal_connect (view, +			  "notify::editable", +			  G_CALLBACK (editable_changed), +			  window); + +	update_documents_list_menu (window); +	 +	g_signal_connect (view, +			  "drop_uris", +			  G_CALLBACK (drop_uris_cb),  +			  NULL); + +	update_window_state (window); + +	g_signal_emit (G_OBJECT (window), signals[TAB_ADDED], 0, tab); +} + +static void +notebook_tab_removed (GeditNotebook *notebook, +		      GeditTab      *tab, +		      GeditWindow   *window) +{ +	GeditView     *view; +	GeditDocument *doc; + +	gedit_debug (DEBUG_WINDOW); + +	g_return_if_fail ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) == 0); + +	--window->priv->num_tabs; + +	view = gedit_tab_get_view (tab); +	doc = gedit_tab_get_document (tab); + +	g_signal_handlers_disconnect_by_func (tab, +					      G_CALLBACK (sync_name),  +					      window); +	g_signal_handlers_disconnect_by_func (tab, +					      G_CALLBACK (sync_state),  +					      window); +	g_signal_handlers_disconnect_by_func (doc, +					      G_CALLBACK (update_cursor_position_statusbar),  +					      window); +	g_signal_handlers_disconnect_by_func (doc,  +					      G_CALLBACK (can_search_again), +					      window); +	g_signal_handlers_disconnect_by_func (doc,  +					      G_CALLBACK (can_undo), +					      window); +	g_signal_handlers_disconnect_by_func (doc,  +					      G_CALLBACK (can_redo), +					      window); +	g_signal_handlers_disconnect_by_func (doc, +					      G_CALLBACK (selection_changed), +					      window); +	g_signal_handlers_disconnect_by_func (doc, +					      G_CALLBACK (sync_languages_menu), +					      window); +	g_signal_handlers_disconnect_by_func (doc, +					      G_CALLBACK (readonly_changed), +					      window); +	g_signal_handlers_disconnect_by_func (view,  +					      G_CALLBACK (update_overwrite_mode_statusbar), +					      window); +	g_signal_handlers_disconnect_by_func (view,  +					      G_CALLBACK (editable_changed), +					      window); +	g_signal_handlers_disconnect_by_func (view,  +					      G_CALLBACK (drop_uris_cb), +					      NULL); + +	if (window->priv->tab_width_id && tab == gedit_window_get_active_tab (window)) +	{ +		g_signal_handler_disconnect (view, window->priv->tab_width_id); +		window->priv->tab_width_id = 0; +	} +	 +	if (window->priv->spaces_instead_of_tabs_id && tab == gedit_window_get_active_tab (window)) +	{ +		g_signal_handler_disconnect (view, window->priv->spaces_instead_of_tabs_id); +		window->priv->spaces_instead_of_tabs_id = 0; +	} +	 +	if (window->priv->language_changed_id && tab == gedit_window_get_active_tab (window)) +	{ +		g_signal_handler_disconnect (doc, window->priv->language_changed_id); +		window->priv->language_changed_id = 0; +	} + +	g_return_if_fail (window->priv->num_tabs >= 0); +	if (window->priv->num_tabs == 0) +	{ +		window->priv->active_tab = NULL; +			        +		set_title (window); +			 +		/* Remove line and col info */ +		gedit_statusbar_set_cursor_position ( +				GEDIT_STATUSBAR (window->priv->statusbar), +				-1, +				-1); +				 +		gedit_statusbar_clear_overwrite ( +				GEDIT_STATUSBAR (window->priv->statusbar)); + +		/* hide the combos */ +		gtk_widget_hide (window->priv->tab_width_combo); +		gtk_widget_hide (window->priv->language_combo); +	} + +	if (!window->priv->removing_tabs) +	{ +		update_documents_list_menu (window); +		update_next_prev_doc_sensitivity_per_window (window); +	} +	else +	{ +		if (window->priv->num_tabs == 0) +		{ +			update_documents_list_menu (window); +			update_next_prev_doc_sensitivity_per_window (window); +		} +	} + +	update_sensitivity_according_to_open_tabs (window); + +	if (window->priv->num_tabs == 0) +	{ +		gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), +							 window); +	} + +	update_window_state (window); + +	g_signal_emit (G_OBJECT (window), signals[TAB_REMOVED], 0, tab);	 +} + +static void +notebook_tabs_reordered (GeditNotebook *notebook, +			 GeditWindow   *window) +{ +	update_documents_list_menu (window); +	update_next_prev_doc_sensitivity_per_window (window); +	 +	g_signal_emit (G_OBJECT (window), signals[TABS_REORDERED], 0); +} + +static void +notebook_tab_detached (GeditNotebook *notebook, +		       GeditTab      *tab, +		       GeditWindow   *window) +{ +	GeditWindow *new_window; +	 +	new_window = clone_window (window); +		 +	gedit_notebook_move_tab (notebook, +				 GEDIT_NOTEBOOK (_gedit_window_get_notebook (new_window)), +				 tab, 0); +				  +	gtk_window_set_position (GTK_WINDOW (new_window),  +				 GTK_WIN_POS_MOUSE); +					  +	gtk_widget_show (GTK_WIDGET (new_window)); +}		       + +static void  +notebook_tab_close_request (GeditNotebook *notebook, +			    GeditTab      *tab, +			    GtkWindow     *window) +{ +	/* Note: we are destroying the tab before the default handler +	 * seems to be ok, but we need to keep an eye on this. */ +	_gedit_cmd_file_close_tab (tab, GEDIT_WINDOW (window)); +} + +static gboolean +show_notebook_popup_menu (GtkNotebook    *notebook, +			  GeditWindow    *window, +			  GdkEventButton *event) +{ +	GtkWidget *menu; +//	GtkAction *action; + +	menu = gtk_ui_manager_get_widget (window->priv->manager, "/NotebookPopup"); +	g_return_val_if_fail (menu != NULL, FALSE); + +// CHECK do we need this? +#if 0 +	/* allow extensions to sync when showing the popup */ +	action = gtk_action_group_get_action (window->priv->action_group, +					      "NotebookPopupAction"); +	g_return_val_if_fail (action != NULL, FALSE); +	gtk_action_activate (action); +#endif +	if (event != NULL) +	{ +		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, +				NULL, NULL, +				event->button, event->time); +	} +	else +	{ +		GtkWidget *tab; +		GtkWidget *tab_label; + +		tab = GTK_WIDGET (gedit_window_get_active_tab (window)); +		g_return_val_if_fail (tab != NULL, FALSE); + +		tab_label = gtk_notebook_get_tab_label (notebook, tab); + +		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, +				gedit_utils_menu_position_under_widget, tab_label, +				0, gtk_get_current_event_time ()); + +		gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE); +	} + +	return TRUE; +} + +static gboolean +notebook_button_press_event (GtkNotebook    *notebook, +			     GdkEventButton *event, +			     GeditWindow    *window) +{ +	if (GDK_BUTTON_PRESS == event->type && 3 == event->button) +	{ +		return show_notebook_popup_menu (notebook, window, event); +	} + +	return FALSE; +} + +static gboolean +notebook_popup_menu (GtkNotebook *notebook, +		     GeditWindow *window) +{ +	/* Only respond if the notebook is the actual focus */ +	if (GEDIT_IS_NOTEBOOK (gtk_window_get_focus (GTK_WINDOW (window)))) +	{ +		return show_notebook_popup_menu (notebook, window, NULL); +	} + +	return FALSE; +} + +static void +side_panel_size_allocate (GtkWidget     *widget, +			  GtkAllocation *allocation, +			  GeditWindow   *window) +{ +	window->priv->side_panel_size = allocation->width; +} + +static void +bottom_panel_size_allocate (GtkWidget     *widget, +			    GtkAllocation *allocation, +			    GeditWindow   *window) +{ +	window->priv->bottom_panel_size = allocation->height; +} + +static void +hpaned_restore_position (GtkWidget   *widget, +			 GeditWindow *window) +{ +	gint pos; + +	gedit_debug_message (DEBUG_WINDOW, +			     "Restoring hpaned position: side panel size %d", +			     window->priv->side_panel_size); + +	pos = MAX (100, window->priv->side_panel_size); +	gtk_paned_set_position (GTK_PANED (window->priv->hpaned), pos); + +	/* start monitoring the size */ +	g_signal_connect (window->priv->side_panel, +			  "size-allocate", +			  G_CALLBACK (side_panel_size_allocate), +			  window); + +	/* run this only once */ +	g_signal_handlers_disconnect_by_func (widget, hpaned_restore_position, window); +} + +static void +vpaned_restore_position (GtkWidget   *widget, +			 GeditWindow *window) +{ +	gint pos; + +	gedit_debug_message (DEBUG_WINDOW, +			     "Restoring vpaned position: bottom panel size %d", +			     window->priv->bottom_panel_size); + +	pos = widget->allocation.height - +	      MAX (50, window->priv->bottom_panel_size); +	gtk_paned_set_position (GTK_PANED (window->priv->vpaned), pos); + +	/* start monitoring the size */ +	g_signal_connect (window->priv->bottom_panel, +			  "size-allocate", +			  G_CALLBACK (bottom_panel_size_allocate), +			  window); + +	/* run this only once */ +	g_signal_handlers_disconnect_by_func (widget, vpaned_restore_position, window); +} + +static void +side_panel_visibility_changed (GtkWidget   *side_panel, +			       GeditWindow *window) +{ +	gboolean visible; +	GtkAction *action; + +	visible = GTK_WIDGET_VISIBLE (side_panel); + +	if (gedit_prefs_manager_side_pane_visible_can_set ()) +		gedit_prefs_manager_set_side_pane_visible (visible); + +	action = gtk_action_group_get_action (window->priv->panes_action_group, +	                                      "ViewSidePane"); + +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + +	/* focus the document */ +	if (!visible && window->priv->active_tab != NULL) +		gtk_widget_grab_focus (GTK_WIDGET ( +				gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)))); +} + +static void +create_side_panel (GeditWindow *window) +{ +	GtkWidget *documents_panel; + +	gedit_debug (DEBUG_WINDOW); + +	window->priv->side_panel = gedit_panel_new (GTK_ORIENTATION_VERTICAL); + +	gtk_paned_pack1 (GTK_PANED (window->priv->hpaned),  +			 window->priv->side_panel,  +			 FALSE,  +			 FALSE); + +	g_signal_connect_after (window->priv->side_panel, +				"show", +				G_CALLBACK (side_panel_visibility_changed), +				window); +	g_signal_connect_after (window->priv->side_panel, +				"hide", +				G_CALLBACK (side_panel_visibility_changed), +				window); + +	documents_panel = gedit_documents_panel_new (window); +	gedit_panel_add_item_with_stock_icon (GEDIT_PANEL (window->priv->side_panel), +					      documents_panel, +					      _("Documents"), +					      GTK_STOCK_FILE); +} + +static void +bottom_panel_visibility_changed (GeditPanel  *bottom_panel, +				 GeditWindow *window) +{ +	gboolean visible; +	GtkAction *action; + +	visible = GTK_WIDGET_VISIBLE (bottom_panel); + +	if (gedit_prefs_manager_bottom_panel_visible_can_set ()) +		gedit_prefs_manager_set_bottom_panel_visible (visible); + +	action = gtk_action_group_get_action (window->priv->panes_action_group, +					      "ViewBottomPane"); + +	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) +		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + +	/* focus the document */ +	if (!visible && window->priv->active_tab != NULL) +		gtk_widget_grab_focus (GTK_WIDGET ( +				gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)))); +} + +static void +bottom_panel_item_removed (GeditPanel  *panel, +			   GtkWidget   *item, +			   GeditWindow *window) +{ +	if (gedit_panel_get_n_items (panel) == 0) +	{ +		GtkAction *action; + +		gtk_widget_hide (GTK_WIDGET (panel)); + +		action = gtk_action_group_get_action (window->priv->panes_action_group, +						      "ViewBottomPane"); +		gtk_action_set_sensitive (action, FALSE); +	} +} + +static void +bottom_panel_item_added (GeditPanel  *panel, +			 GtkWidget   *item, +			 GeditWindow *window) +{ +	/* if it's the first item added, set the menu item +	 * sensitive and if needed show the panel */ +	if (gedit_panel_get_n_items (panel) == 1) +	{ +		GtkAction *action; +		gboolean show; + +		action = gtk_action_group_get_action (window->priv->panes_action_group, +						      "ViewBottomPane"); +		gtk_action_set_sensitive (action, TRUE); + +		show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); +		if (show) +			gtk_widget_show (GTK_WIDGET (panel)); +	} +} + +static void +create_bottom_panel (GeditWindow *window)  +{ +	gedit_debug (DEBUG_WINDOW); + +	window->priv->bottom_panel = gedit_panel_new (GTK_ORIENTATION_HORIZONTAL); + +	gtk_paned_pack2 (GTK_PANED (window->priv->vpaned),  +			 window->priv->bottom_panel,  +			 FALSE, +			 FALSE); + +	g_signal_connect_after (window->priv->bottom_panel, +				"show", +				G_CALLBACK (bottom_panel_visibility_changed), +				window); +	g_signal_connect_after (window->priv->bottom_panel, +				"hide", +				G_CALLBACK (bottom_panel_visibility_changed), +				window); +} + +static void +init_panels_visibility (GeditWindow *window) +{ +	gint active_page; + +	gedit_debug (DEBUG_WINDOW); + +	/* side pane */ +	active_page = gedit_prefs_manager_get_side_panel_active_page (); +	_gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->side_panel), +					    active_page); + +	if (gedit_prefs_manager_get_side_pane_visible ()) +	{ +		gtk_widget_show (window->priv->side_panel); +	} + +	/* bottom pane, it can be empty */ +	if (gedit_panel_get_n_items (GEDIT_PANEL (window->priv->bottom_panel)) > 0) +	{ +		active_page = gedit_prefs_manager_get_bottom_panel_active_page (); +		_gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->bottom_panel), +						    active_page); + +		if (gedit_prefs_manager_get_bottom_panel_visible ()) +		{ +			gtk_widget_show (window->priv->bottom_panel); +		} +	} +	else +	{ +		GtkAction *action; +		action = gtk_action_group_get_action (window->priv->panes_action_group, +						      "ViewBottomPane"); +		gtk_action_set_sensitive (action, FALSE); +	} + +	/* start track sensitivity after the initial state is set */ +	window->priv->bottom_panel_item_removed_handler_id = +		g_signal_connect (window->priv->bottom_panel, +				  "item_removed", +				  G_CALLBACK (bottom_panel_item_removed), +				  window); + +	g_signal_connect (window->priv->bottom_panel, +			  "item_added", +			  G_CALLBACK (bottom_panel_item_added), +			  window); +} + +static void +clipboard_owner_change (GtkClipboard        *clipboard, +			GdkEventOwnerChange *event, +			GeditWindow         *window) +{ +	set_paste_sensitivity_according_to_clipboard (window, +						      clipboard); +} + +static void +window_realized (GtkWidget *window, +		 gpointer  *data) +{ +	GtkClipboard *clipboard; + +	clipboard = gtk_widget_get_clipboard (window, +					      GDK_SELECTION_CLIPBOARD); + +	g_signal_connect (clipboard, +			  "owner_change", +			  G_CALLBACK (clipboard_owner_change), +			  window); +} + +static void +window_unrealized (GtkWidget *window, +		   gpointer  *data) +{ +	GtkClipboard *clipboard; + +	clipboard = gtk_widget_get_clipboard (window, +					      GDK_SELECTION_CLIPBOARD); + +	g_signal_handlers_disconnect_by_func (clipboard, +					      G_CALLBACK (clipboard_owner_change), +					      window); +} + +static void +check_window_is_active (GeditWindow *window, +			GParamSpec *property, +			gpointer useless) +{ +	if (window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN) +	{ +		if (gtk_window_is_active (GTK_WINDOW (window))) +		{ +			gtk_widget_show (window->priv->fullscreen_controls); +		} +		else +		{ +			gtk_widget_hide (window->priv->fullscreen_controls); +		} +	} +} + +#ifdef OS_OSX +static void +setup_mac_menu (GeditWindow *window) +{ +	GtkAction *action; + +	gtk_widget_hide (window->priv->menubar); +	action = gtk_ui_manager_get_action (window->priv->manager, "/ui/MenuBar/HelpMenu/HelpAboutMenu"); + +	gtk_action_set_label (action, _("About gedit")); + +	ige_mac_menu_set_menu_bar (GTK_MENU_SHELL (window->priv->menubar)); +	ige_mac_menu_set_quit_menu_item (ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/FileMenu/FileQuitMenu")); + +	ige_mac_menu_set_preferences_menu_item (ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/EditMenu/EditPreferencesMenu")); +	 +	add_mac_root_menu (window); +	ige_mac_menu_connect_window_key_handler (GTK_WINDOW (window)); +} +#endif + +static void +connect_notebook_signals (GeditWindow *window, +			  GtkWidget   *notebook) +{ +	g_signal_connect (notebook, +			  "switch-page", +			  G_CALLBACK (notebook_switch_page), +			  window); +	g_signal_connect (notebook, +			  "tab-added", +			  G_CALLBACK (notebook_tab_added), +			  window); +	g_signal_connect (notebook, +			  "tab-removed", +			  G_CALLBACK (notebook_tab_removed), +			  window); +	g_signal_connect (notebook, +			  "tabs-reordered", +			  G_CALLBACK (notebook_tabs_reordered), +			  window); +	g_signal_connect (notebook, +			  "tab-detached", +			  G_CALLBACK (notebook_tab_detached), +			  window); +	g_signal_connect (notebook, +			  "tab-close-request", +			  G_CALLBACK (notebook_tab_close_request), +			  window); +	g_signal_connect (notebook, +			  "button-press-event", +			  G_CALLBACK (notebook_button_press_event), +			  window); +	g_signal_connect (notebook, +			  "popup-menu", +			  G_CALLBACK (notebook_popup_menu), +			  window); +} + +static void +add_notebook (GeditWindow *window, +	      GtkWidget   *notebook) +{ +	gtk_paned_pack1 (GTK_PANED (window->priv->vpaned), +	                 notebook, +	                 TRUE, +	                 TRUE); + +	gtk_widget_show (notebook); + +	connect_notebook_signals (window, notebook); +} + +static void +gedit_window_init (GeditWindow *window) +{ +	GtkWidget *main_box; +	GtkTargetList *tl; + +	gedit_debug (DEBUG_WINDOW); + +	window->priv = GEDIT_WINDOW_GET_PRIVATE (window); +	window->priv->active_tab = NULL; +	window->priv->num_tabs = 0; +	window->priv->removing_tabs = FALSE; +	window->priv->state = GEDIT_WINDOW_STATE_NORMAL; +	window->priv->dispose_has_run = FALSE; +	window->priv->fullscreen_controls = NULL; +	window->priv->fullscreen_animation_timeout_id = 0; + +	window->priv->message_bus = gedit_message_bus_new (); + +	window->priv->window_group = gtk_window_group_new (); +	gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window)); + +	main_box = gtk_vbox_new (FALSE, 0); +	gtk_container_add (GTK_CONTAINER (window), main_box); +	gtk_widget_show (main_box); + +	/* Add menu bar and toolbar bar */ +	create_menu_bar_and_toolbar (window, main_box); + +	/* Add status bar */ +	create_statusbar (window, main_box); + +	/* Add the main area */ +	gedit_debug_message (DEBUG_WINDOW, "Add main area");		 +	window->priv->hpaned = gtk_hpaned_new (); +  	gtk_box_pack_start (GTK_BOX (main_box),  +  			    window->priv->hpaned,  +  			    TRUE,  +  			    TRUE,  +  			    0); + +	window->priv->vpaned = gtk_vpaned_new (); +  	gtk_paned_pack2 (GTK_PANED (window->priv->hpaned),  +  			 window->priv->vpaned,  +  			 TRUE,  +  			 FALSE); +  	 +	gedit_debug_message (DEBUG_WINDOW, "Create gedit notebook"); +	window->priv->notebook = gedit_notebook_new (); +	add_notebook (window, window->priv->notebook); + +	/* side and bottom panels */ +  	create_side_panel (window); +	create_bottom_panel (window); + +	/* panes' state must be restored after panels have been mapped, +	 * since the bottom pane position depends on the size of the vpaned. */ +	window->priv->side_panel_size = gedit_prefs_manager_get_side_panel_size (); +	window->priv->bottom_panel_size = gedit_prefs_manager_get_bottom_panel_size (); + +	g_signal_connect_after (window->priv->hpaned, +				"map", +				G_CALLBACK (hpaned_restore_position), +				window); +	g_signal_connect_after (window->priv->vpaned, +				"map", +				G_CALLBACK (vpaned_restore_position), +				window); + +	gtk_widget_show (window->priv->hpaned); +	gtk_widget_show (window->priv->vpaned); + +	/* Drag and drop support, set targets to NULL because we add the +	   default uri_targets below */ +	gtk_drag_dest_set (GTK_WIDGET (window), +			   GTK_DEST_DEFAULT_MOTION | +			   GTK_DEST_DEFAULT_HIGHLIGHT | +			   GTK_DEST_DEFAULT_DROP, +			   NULL, +			   0, +			   GDK_ACTION_COPY); + +	/* Add uri targets */ +	tl = gtk_drag_dest_get_target_list (GTK_WIDGET (window)); +	 +	if (tl == NULL) +	{ +		tl = gtk_target_list_new (NULL, 0); +		gtk_drag_dest_set_target_list (GTK_WIDGET (window), tl); +		gtk_target_list_unref (tl); +	} +	 +	gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST); + +	/* connect instead of override, so that we can +	 * share the cb code with the view */ +	g_signal_connect (window, +			  "drag_data_received", +	                  G_CALLBACK (drag_data_received_cb),  +	                  NULL); + +	/* we can get the clipboard only after the widget +	 * is realized */ +	g_signal_connect (window, +			  "realize", +			  G_CALLBACK (window_realized), +			  NULL); +	g_signal_connect (window, +			  "unrealize", +			  G_CALLBACK (window_unrealized), +			  NULL); + +	/* Check if the window is active for fullscreen */ +	g_signal_connect (window, +			  "notify::is-active", +			  G_CALLBACK (check_window_is_active), +			  NULL); + +	gedit_debug_message (DEBUG_WINDOW, "Update plugins ui"); +	 +	gedit_plugins_engine_activate_plugins (gedit_plugins_engine_get_default (), +					        window); + +	/* set visibility of panes. +	 * This needs to be done after plugins activatation */ +	init_panels_visibility (window); + +	update_sensitivity_according_to_open_tabs (window); + +#ifdef OS_OSX +	setup_mac_menu (window); +#endif + +	gedit_debug_message (DEBUG_WINDOW, "END"); +} + +/** + * gedit_window_get_active_view: + * @window: a #GeditWindow + * + * Gets the active #GeditView. + * + * Returns: the active #GeditView + */ +GeditView * +gedit_window_get_active_view (GeditWindow *window) +{ +	GeditView *view; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	if (window->priv->active_tab == NULL) +		return NULL; + +	view = gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)); + +	return view; +} + +/** + * gedit_window_get_active_document: + * @window: a #GeditWindow + * + * Gets the active #GeditDocument. + *  + * Returns: the active #GeditDocument + */ +GeditDocument * +gedit_window_get_active_document (GeditWindow *window) +{ +	GeditView *view; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	view = gedit_window_get_active_view (window); +	if (view == NULL) +		return NULL; + +	return GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); +} + +GtkWidget * +_gedit_window_get_notebook (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return window->priv->notebook; +} + +/** + * gedit_window_create_tab: + * @window: a #GeditWindow + * @jump_to: %TRUE to set the new #GeditTab as active + * + * Creates a new #GeditTab and adds the new tab to the #GeditNotebook. + * In case @jump_to is %TRUE the #GeditNotebook switches to that new #GeditTab. + * + * Returns: a new #GeditTab + */ +GeditTab * +gedit_window_create_tab (GeditWindow *window, +			 gboolean     jump_to) +{ +	GeditTab *tab; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	tab = GEDIT_TAB (_gedit_tab_new ());	 +	gtk_widget_show (GTK_WIDGET (tab));	 + +	gedit_notebook_add_tab (GEDIT_NOTEBOOK (window->priv->notebook), +				tab, +				-1, +				jump_to); + +	if (!GTK_WIDGET_VISIBLE (window)) +	{ +		gtk_window_present (GTK_WINDOW (window)); +	} + +	return tab; +} + +/** + * gedit_window_create_tab_from_uri: + * @window: a #GeditWindow + * @uri: the uri of the document + * @encoding: a #GeditEncoding + * @line_pos: the line position to visualize + * @create: %TRUE to create a new document in case @uri does exist + * @jump_to: %TRUE to set the new #GeditTab as active + * + * Creates a new #GeditTab loading the document specified by @uri. + * In case @jump_to is %TRUE the #GeditNotebook swithes to that new #GeditTab. + * Whether @create is %TRUE, creates a new empty document if location does  + * not refer to an existing file + * + * Returns: a new #GeditTab + */ +GeditTab * +gedit_window_create_tab_from_uri (GeditWindow         *window, +				  const gchar         *uri, +				  const GeditEncoding *encoding, +				  gint                 line_pos, +				  gboolean             create, +				  gboolean             jump_to) +{ +	GtkWidget *tab; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	g_return_val_if_fail (uri != NULL, NULL); + +	tab = _gedit_tab_new_from_uri (uri, +				       encoding, +				       line_pos, +				       create);	 +	if (tab == NULL) +		return NULL; + +	gtk_widget_show (tab);	 +	 +	gedit_notebook_add_tab (GEDIT_NOTEBOOK (window->priv->notebook), +				GEDIT_TAB (tab), +				-1, +				jump_to); + + +	if (!GTK_WIDGET_VISIBLE (window)) +	{ +		gtk_window_present (GTK_WINDOW (window)); +	} + +	return GEDIT_TAB (tab); +}				   + +/** + * gedit_window_get_active_tab: + * @window: a GeditWindow + * + * Gets the active #GeditTab in the @window. + * + * Returns: the active #GeditTab in the @window. + */ +GeditTab * +gedit_window_get_active_tab (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	return (window->priv->active_tab == NULL) ?  +				NULL : GEDIT_TAB (window->priv->active_tab); +} + +static void +add_document (GeditTab *tab, GList **res) +{ +	GeditDocument *doc; +	 +	doc = gedit_tab_get_document (tab); +	 +	*res = g_list_prepend (*res, doc); +} + +/** + * gedit_window_get_documents: + * @window: a #GeditWindow + * + * Gets a newly allocated list with all the documents in the window. + * This list must be freed. + * + * Returns: a newly allocated list with all the documents in the window + */ +GList * +gedit_window_get_documents (GeditWindow *window) +{ +	GList *res = NULL; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), +			       (GtkCallback)add_document, +			       &res); +			        +	res = g_list_reverse (res); +	 +	return res; +} + +static void +add_view (GeditTab *tab, GList **res) +{ +	GeditView *view; +	 +	view = gedit_tab_get_view (tab); +	 +	*res = g_list_prepend (*res, view); +} + +/** + * gedit_window_get_views: + * @window: a #GeditWindow + * + * Gets a list with all the views in the window. This list must be freed. + * + * Returns: a newly allocated list with all the views in the window + */ +GList * +gedit_window_get_views (GeditWindow *window) +{ +	GList *res = NULL; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), +			       (GtkCallback)add_view, +			       &res); +			        +	res = g_list_reverse (res); +	 +	return res; +} + +/** + * gedit_window_close_tab: + * @window: a #GeditWindow + * @tab: the #GeditTab to close + * + * Closes the @tab. + */ +void +gedit_window_close_tab (GeditWindow *window, +			GeditTab    *tab) +{ +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	g_return_if_fail ((gedit_tab_get_state (tab) != GEDIT_TAB_STATE_SAVING) && +			  (gedit_tab_get_state (tab) != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)); +	 +	gedit_notebook_remove_tab (GEDIT_NOTEBOOK (window->priv->notebook), +				   tab); +} + +/** + * gedit_window_close_all_tabs: + * @window: a #GeditWindow + * + * Closes all opened tabs. + */ +void +gedit_window_close_all_tabs (GeditWindow *window) +{ +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (!(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && +			  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + +	window->priv->removing_tabs = TRUE; + +	gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (window->priv->notebook)); + +	window->priv->removing_tabs = FALSE; +} + +/** + * gedit_window_close_tabs: + * @window: a #GeditWindow + * @tabs: a list of #GeditTab + * + * Closes all tabs specified by @tabs. + */ +void +gedit_window_close_tabs (GeditWindow *window, +			 const GList *tabs) +{ +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (!(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && +			  !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + +	if (tabs == NULL) +		return; + +	window->priv->removing_tabs = TRUE; + +	while (tabs != NULL) +	{ +		if (tabs->next == NULL) +			window->priv->removing_tabs = FALSE; + +		gedit_notebook_remove_tab (GEDIT_NOTEBOOK (window->priv->notebook), +				   	   GEDIT_TAB (tabs->data)); + +		tabs = g_list_next (tabs); +	} + +	g_return_if_fail (window->priv->removing_tabs == FALSE); +} + +GeditWindow * +_gedit_window_move_tab_to_new_window (GeditWindow *window, +				      GeditTab    *tab) +{ +	GeditWindow *new_window; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); +	g_return_val_if_fail (gtk_notebook_get_n_pages ( +				GTK_NOTEBOOK (window->priv->notebook)) > 1,  +			      NULL); +			       +	new_window = clone_window (window); + +	gedit_notebook_move_tab (GEDIT_NOTEBOOK (window->priv->notebook), +				 GEDIT_NOTEBOOK (new_window->priv->notebook), +				 tab, +				 -1); +				  +	gtk_widget_show (GTK_WIDGET (new_window)); +	 +	return new_window; +}				       + +/** + * gedit_window_set_active_tab: + * @window: a #GeditWindow + * @tab: a #GeditTab + * + * Switches to the tab that matches with @tab. + */ +void +gedit_window_set_active_tab (GeditWindow *window, +			     GeditTab    *tab) +{ +	gint page_num; +	 +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (GEDIT_IS_TAB (tab)); +	 +	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), +					  GTK_WIDGET (tab)); +	g_return_if_fail (page_num != -1); +	 +	gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), +				       page_num); +} + +/** + * gedit_window_get_group: + * @window: a #GeditWindow + * + * Gets the #GtkWindowGroup in which @window resides. + * + * Returns: the #GtkWindowGroup + */ +GtkWindowGroup * +gedit_window_get_group (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	return window->priv->window_group; +} + +gboolean +_gedit_window_is_removing_tabs (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); +	 +	return window->priv->removing_tabs; +} + +/** + * gedit_window_get_ui_manager: + * @window: a #GeditWindow + * + * Gets the #GtkUIManager associated with the @window. + * + * Returns: the #GtkUIManager of the @window. + */ +GtkUIManager * +gedit_window_get_ui_manager (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return window->priv->manager; +} + +/** + * gedit_window_get_side_panel: + * @window: a #GeditWindow + * + * Gets the side #GeditPanel of the @window. + * + * Returns: the side #GeditPanel. + */ +GeditPanel * +gedit_window_get_side_panel (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return GEDIT_PANEL (window->priv->side_panel); +} + +/** + * gedit_window_get_bottom_panel: + * @window: a #GeditWindow + * + * Gets the bottom #GeditPanel of the @window. + * + * Returns: the bottom #GeditPanel. + */ +GeditPanel * +gedit_window_get_bottom_panel (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return GEDIT_PANEL (window->priv->bottom_panel); +} + +/** + * gedit_window_get_statusbar: + * @window: a #GeditWindow + * + * Gets the #GeditStatusbar of the @window. + * + * Returns: the #GeditStatusbar of the @window. + */ +GtkWidget * +gedit_window_get_statusbar (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), 0); + +	return window->priv->statusbar; +} + +/** + * gedit_window_get_state: + * @window: a #GeditWindow + * + * Retrieves the state of the @window. + * + * Returns: the current #GeditWindowState of the @window. + */ +GeditWindowState +gedit_window_get_state (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), GEDIT_WINDOW_STATE_NORMAL); + +	return window->priv->state; +} + +GFile * +_gedit_window_get_default_location (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + +	return window->priv->default_location != NULL ? +		g_object_ref (window->priv->default_location) : NULL; +} + +void +_gedit_window_set_default_location (GeditWindow *window, +				    GFile       *location) +{ +	GFile *dir; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	g_return_if_fail (G_IS_FILE (location)); + +	dir = g_file_get_parent (location); +	g_return_if_fail (dir != NULL); + +	if (window->priv->default_location != NULL) +		g_object_unref (window->priv->default_location); + +	window->priv->default_location = dir; +} + +/** + * gedit_window_get_unsaved_documents: + * @window: a #GeditWindow + * + * Gets the list of documents that need to be saved before closing the window. + * + * Returns: a list of #GeditDocument that need to be saved before closing the window + */ +GList * +gedit_window_get_unsaved_documents (GeditWindow *window) +{ +	GList *unsaved_docs = NULL; +	GList *tabs; +	GList *l; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	tabs = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook)); +	 +	l = tabs; +	while (l != NULL) +	{ +		GeditTab *tab; + +		tab = GEDIT_TAB (l->data); +		 +		if (!_gedit_tab_can_close (tab)) +		{ +			GeditDocument *doc; +			 +			doc = gedit_tab_get_document (tab); +			unsaved_docs = g_list_prepend (unsaved_docs, doc); +		}	 +		 +		l = g_list_next (l); +	} +	 +	g_list_free (tabs); + +	return g_list_reverse (unsaved_docs); +} + +void  +_gedit_window_set_saving_session_state (GeditWindow *window, +					gboolean     saving_session) +{ +	GeditWindowState old_state; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); +	 +	old_state = window->priv->state; + +	if (saving_session) +		window->priv->state |= GEDIT_WINDOW_STATE_SAVING_SESSION; +	else +		window->priv->state &= ~GEDIT_WINDOW_STATE_SAVING_SESSION; + +	if (old_state != window->priv->state) +	{ +		set_sensitivity_according_to_window_state (window); + +		g_object_notify (G_OBJECT (window), "state"); +	} +} + +static void +hide_notebook_tabs_on_fullscreen (GtkNotebook	*notebook,  +				  GParamSpec	*pspec, +				  GeditWindow	*window) +{ +	gtk_notebook_set_show_tabs (notebook, FALSE); +} + +void +_gedit_window_fullscreen (GeditWindow *window) +{ +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	if (_gedit_window_is_fullscreen (window)) +		return; + +	/* Go to fullscreen mode and hide bars */ +	gtk_window_fullscreen (&window->window); +	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), FALSE); +	g_signal_connect (window->priv->notebook, "notify::show-tabs", +			  G_CALLBACK (hide_notebook_tabs_on_fullscreen), window); +	 +	gtk_widget_hide (window->priv->menubar); +	 +	g_signal_handlers_block_by_func (window->priv->toolbar, +					 toolbar_visibility_changed, +					 window); +	gtk_widget_hide (window->priv->toolbar); +	 +	g_signal_handlers_block_by_func (window->priv->statusbar, +					 statusbar_visibility_changed, +					 window); +	gtk_widget_hide (window->priv->statusbar); + +	fullscreen_controls_build (window); +	fullscreen_controls_show (window); +} + +void +_gedit_window_unfullscreen (GeditWindow *window) +{ +	gboolean visible; +	GtkAction *action; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	if (!_gedit_window_is_fullscreen (window)) +		return; + +	/* Unfullscreen and show bars */ +	gtk_window_unfullscreen (&window->window); +	g_signal_handlers_disconnect_by_func (window->priv->notebook, +					      hide_notebook_tabs_on_fullscreen, +					      window); +	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), TRUE); +	gtk_widget_show (window->priv->menubar); +	 +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewToolbar"); +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); +	if (visible) +		gtk_widget_show (window->priv->toolbar); +	g_signal_handlers_unblock_by_func (window->priv->toolbar, +					   toolbar_visibility_changed, +					   window); +	 +	action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, +					      "ViewStatusbar"); +	visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); +	if (visible) +		gtk_widget_show (window->priv->statusbar); +	g_signal_handlers_unblock_by_func (window->priv->statusbar, +					   statusbar_visibility_changed, +					   window); + +	gtk_widget_hide (window->priv->fullscreen_controls); +} + +gboolean +_gedit_window_is_fullscreen (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); + +	return window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN; +} + +/** + * gedit_window_get_tab_from_location: + * @window: a #GeditWindow + * @location: a #GFile + * + * Gets the #GeditTab that matches with the given @location. + * + * Returns: the #GeditTab that matches with the given @location. + */ +GeditTab * +gedit_window_get_tab_from_location (GeditWindow *window, +				    GFile       *location) +{ +	GList *tabs; +	GList *l; +	GeditTab *ret = NULL; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	g_return_val_if_fail (G_IS_FILE (location), NULL); + +	tabs = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook)); +	 +	for (l = tabs; l != NULL; l = g_list_next (l)) +	{ +		GeditDocument *d; +		GeditTab *t; +		GFile *f; + +		t = GEDIT_TAB (l->data); +		d = gedit_tab_get_document (t); + +		f = gedit_document_get_location (d); + +		if ((f != NULL)) +		{ +			gboolean found = g_file_equal (location, f); + +			g_object_unref (f); + +			if (found) +			{ +				ret = t; +				break; +			} +		} +	} +	 +	g_list_free (tabs); +	 +	return ret; +} + +/** + * gedit_window_get_message_bus: + * @window: a #GeditWindow + * + * Gets the #GeditMessageBus associated with @window. The returned reference + * is owned by the window and should not be unreffed. + * + * Return value: the #GeditMessageBus associated with @window + */ +GeditMessageBus	* +gedit_window_get_message_bus (GeditWindow *window) +{ +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	 +	return window->priv->message_bus; +} + +/** + * gedit_window_get_tab_from_uri: + * @window: a #GeditWindow + * @uri: the uri to get the #GeditTab + * + * Gets the #GeditTab that matches @uri. + * + * Returns: the #GeditTab associated with @uri. + * + * Deprecated: 2.24: Use gedit_window_get_tab_from_location() instead. + */ +GeditTab * +gedit_window_get_tab_from_uri (GeditWindow *window, +			       const gchar *uri) +{ +	GFile *f; +	GeditTab *tab; + +	g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); +	g_return_val_if_fail (uri != NULL, NULL); + +	f = g_file_new_for_uri (uri); +	tab = gedit_window_get_tab_from_location (window, f); +	g_object_unref (f); + +	return tab; +} diff --git a/gedit/gedit-window.h b/gedit/gedit-window.h new file mode 100755 index 00000000..e8c7eef3 --- /dev/null +++ b/gedit/gedit-window.h @@ -0,0 +1,195 @@ +/* + * gedit-window.h + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi  + * + * 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 + * MERCHANWINDOWILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330,  + * Boston, MA 02111-1307, USA. + */ +  +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a  + * list of people on the gedit Team.   + * See the ChangeLog files for a list of changes.  + * + * $Id$ + */ + +#ifndef __GEDIT_WINDOW_H__ +#define __GEDIT_WINDOW_H__ + +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include <gedit/gedit-tab.h> +#include <gedit/gedit-panel.h> +#include <gedit/gedit-message-bus.h> + +G_BEGIN_DECLS + +typedef enum +{ +	GEDIT_WINDOW_STATE_NORMAL		= 0, +	GEDIT_WINDOW_STATE_SAVING		= 1 << 1, +	GEDIT_WINDOW_STATE_PRINTING		= 1 << 2, +	GEDIT_WINDOW_STATE_LOADING		= 1 << 3, +	GEDIT_WINDOW_STATE_ERROR		= 1 << 4, +	GEDIT_WINDOW_STATE_SAVING_SESSION	= 1 << 5 +} GeditWindowState; +	 +/* + * Type checking and casting macros + */ +#define GEDIT_TYPE_WINDOW              (gedit_window_get_type()) +#define GEDIT_WINDOW(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), GEDIT_TYPE_WINDOW, GeditWindow)) +#define GEDIT_WINDOW_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), GEDIT_TYPE_WINDOW, GeditWindowClass)) +#define GEDIT_IS_WINDOW(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), GEDIT_TYPE_WINDOW)) +#define GEDIT_IS_WINDOW_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_WINDOW)) +#define GEDIT_WINDOW_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GEDIT_TYPE_WINDOW, GeditWindowClass)) + +/* Private structure type */ +typedef struct _GeditWindowPrivate GeditWindowPrivate; + +/* + * Main object structure + */ +typedef struct _GeditWindow GeditWindow; + +struct _GeditWindow  +{ +	GtkWindow window; + +	/*< private > */ +	GeditWindowPrivate *priv; +}; + +/* + * Class definition + */ +typedef struct _GeditWindowClass GeditWindowClass; + +struct _GeditWindowClass  +{ +	GtkWindowClass parent_class; +	 +	/* Signals */ +	void	 (* tab_added)      	(GeditWindow *window, +				     	 GeditTab    *tab); +	void	 (* tab_removed)    	(GeditWindow *window, +				     	 GeditTab    *tab); +	void	 (* tabs_reordered) 	(GeditWindow *window); +	void	 (* active_tab_changed)	(GeditWindow *window, +				     	 GeditTab    *tab); +	void	 (* active_tab_state_changed)	 +					(GeditWindow *window); +}; + +/* + * Public methods + */ +GType 		 gedit_window_get_type 			(void) G_GNUC_CONST; + +GeditTab	*gedit_window_create_tab		(GeditWindow         *window, +							 gboolean             jump_to); +							  +GeditTab	*gedit_window_create_tab_from_uri	(GeditWindow         *window, +							 const gchar         *uri, +							 const GeditEncoding *encoding, +							 gint                 line_pos, +							 gboolean             create, +							 gboolean             jump_to); +							  +void		 gedit_window_close_tab			(GeditWindow         *window, +							 GeditTab            *tab); +							  +void		 gedit_window_close_all_tabs		(GeditWindow         *window); + +void		 gedit_window_close_tabs		(GeditWindow         *window, +							 const GList         *tabs); +							  +GeditTab	*gedit_window_get_active_tab		(GeditWindow         *window); + +void		 gedit_window_set_active_tab		(GeditWindow         *window, +							 GeditTab            *tab); + +/* Helper functions */ +GeditView	*gedit_window_get_active_view		(GeditWindow         *window); +GeditDocument	*gedit_window_get_active_document	(GeditWindow         *window); + +/* Returns a newly allocated list with all the documents in the window */ +GList		*gedit_window_get_documents		(GeditWindow         *window); + +/* Returns a newly allocated list with all the documents that need to be  +   saved before closing the window */ +GList		*gedit_window_get_unsaved_documents 	(GeditWindow         *window); + +/* Returns a newly allocated list with all the views in the window */ +GList		*gedit_window_get_views			(GeditWindow         *window); + +GtkWindowGroup  *gedit_window_get_group			(GeditWindow         *window); + +GeditPanel	*gedit_window_get_side_panel		(GeditWindow         *window); + +GeditPanel	*gedit_window_get_bottom_panel		(GeditWindow         *window); + +GtkWidget	*gedit_window_get_statusbar		(GeditWindow         *window); + +GtkUIManager	*gedit_window_get_ui_manager		(GeditWindow         *window); + +GeditWindowState gedit_window_get_state 		(GeditWindow         *window); + +GeditTab        *gedit_window_get_tab_from_location	(GeditWindow         *window, +							 GFile               *location); + +GeditTab        *gedit_window_get_tab_from_uri		(GeditWindow         *window, +							 const gchar         *uri); + +/* Message bus */ +GeditMessageBus	*gedit_window_get_message_bus		(GeditWindow         *window); + +/* + * Non exported functions + */ +GtkWidget	*_gedit_window_get_notebook		(GeditWindow         *window); + +GeditWindow	*_gedit_window_move_tab_to_new_window	(GeditWindow         *window, +							 GeditTab            *tab); +gboolean	 _gedit_window_is_removing_tabs		(GeditWindow         *window); + +GFile		*_gedit_window_get_default_location 	(GeditWindow         *window); + +void		 _gedit_window_set_default_location 	(GeditWindow         *window, +							 GFile               *location); + +void		 _gedit_window_set_saving_session_state	(GeditWindow         *window, +							 gboolean             saving_session); + +void		 _gedit_window_fullscreen		(GeditWindow         *window); + +void		 _gedit_window_unfullscreen		(GeditWindow         *window); + +gboolean	 _gedit_window_is_fullscreen		(GeditWindow         *window); + +/* these are in gedit-window because of screen safety */ +void		 _gedit_recent_add			(GeditWindow	     *window, +							 const gchar         *uri, +							 const gchar         *mime); +void		 _gedit_recent_remove			(GeditWindow         *window, +							 const gchar         *uri); + +G_END_DECLS + +#endif  /* __GEDIT_WINDOW_H__  */ diff --git a/gedit/gedit.c b/gedit/gedit.c new file mode 100755 index 00000000..fa53b407 --- /dev/null +++ b/gedit/gedit.c @@ -0,0 +1,766 @@ +/* + * gedit.c + * This file is part of gedit + * + * Copyright (C) 2005 - Paolo Maggi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +#include "gedit-app.h" +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-dirs.h" +#include "gedit-encodings.h" +#include "gedit-plugins-engine.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-session.h" +#include "gedit-utils.h" +#include "gedit-window.h" + +#include "eggsmclient.h" +#include "eggdesktopfile.h" + +#ifdef G_OS_WIN32 +#define SAVE_DATADIR DATADIR +#undef DATADIR +#include <io.h> +#include <conio.h> +#define _WIN32_WINNT 0x0500 +#include <windows.h> +#define DATADIR SAVE_DATADIR +#undef SAVE_DATADIR +#endif + +#ifdef OS_OSX +#include <ige-mac-dock.h> +#include <ige-mac-integration.h> +#include "osx/gedit-osx.h" +#endif + +#ifndef ENABLE_GVFS_METADATA +#include "gedit-metadata-manager.h" +#endif + +static guint32 startup_timestamp = 0; + +#ifndef G_OS_WIN32 +#include "bacon-message-connection.h" + +static BaconMessageConnection *connection; +#endif + +/* command line */ +static gint line_position = 0; +static gchar *encoding_charset = NULL; +static gboolean new_window_option = FALSE; +static gboolean new_document_option = FALSE; +static gchar **remaining_args = NULL; +static GSList *file_list = NULL; + +static void +show_version_and_quit (void) +{ +	g_print ("%s - Version %s\n", g_get_application_name (), VERSION); + +	exit (0); +} + +static void +list_encodings_and_quit (void) +{ +	gint i = 0; +	const GeditEncoding *enc; + +	while ((enc = gedit_encoding_get_from_index (i)) != NULL) +	{ +		g_print ("%s\n", gedit_encoding_get_charset (enc)); + +		++i; +	} + +	exit (0); +} + +static const GOptionEntry options [] = +{ +	{ "version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, +	  show_version_and_quit, N_("Show the application's version"), NULL }, + +	{ "encoding", '\0', 0, G_OPTION_ARG_STRING, &encoding_charset, +	  N_("Set the character encoding to be used to open the files listed on the command line"), N_("ENCODING")}, + +	{ "list-encodings", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, +	  list_encodings_and_quit, N_("Display list of possible values for the encoding option"), NULL}, + +	{ "new-window", '\0', 0, G_OPTION_ARG_NONE, &new_window_option, +	  N_("Create a new top-level window in an existing instance of gedit"), NULL }, + +	{ "new-document", '\0', 0, G_OPTION_ARG_NONE, &new_document_option, +	  N_("Create a new document in an existing instance of gedit"), NULL }, + +	{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &remaining_args, +	  NULL, N_("[FILE...]") }, /* collects file arguments */ + +	{NULL} +}; + +static void +free_command_line_data (void) +{ +	g_slist_foreach (file_list, (GFunc) g_object_unref, NULL); +	g_slist_free (file_list); +	file_list = NULL; + +	g_strfreev (remaining_args); +	remaining_args = NULL; + +	g_free (encoding_charset); +	encoding_charset = NULL; + +	new_window_option = FALSE; +	new_document_option = FALSE; +	line_position = 0; +} + +static void +gedit_get_command_line_data (void) +{ +	if (remaining_args) +	{ +		gint i; + +		for (i = 0; remaining_args[i]; i++) +		{ +			if (*remaining_args[i] == '+') +			{ +				if (*(remaining_args[i] + 1) == '\0') +					/* goto the last line of the document */ +					line_position = G_MAXINT; +				else +					line_position = atoi (remaining_args[i] + 1); +			} +			else +			{ +				GFile *file; + +				file = g_file_new_for_commandline_arg (remaining_args[i]); +				file_list = g_slist_prepend (file_list, file); +			} +		} + +		file_list = g_slist_reverse (file_list); +	} + +	if (encoding_charset && +	    (gedit_encoding_get_from_charset (encoding_charset) == NULL)) +	{ +		g_print (_("%s: invalid encoding.\n"), +			 encoding_charset); +	} +} + +static guint32 +get_startup_timestamp (void) +{ +	const gchar *startup_id_env; +	gchar *startup_id = NULL; +	gchar *time_str; +	gchar *end; +	gulong retval = 0; + +	/* we don't unset the env, since startup-notification +	 * may still need it */ +	startup_id_env = g_getenv ("DESKTOP_STARTUP_ID"); +	if (startup_id_env == NULL) +		goto out; + +	startup_id = g_strdup (startup_id_env); + +	time_str = g_strrstr (startup_id, "_TIME"); +	if (time_str == NULL) +		goto out; + +	errno = 0; + +	/* Skip past the "_TIME" part */ +	time_str += 5; + +	retval = strtoul (time_str, &end, 0); +	if (end == time_str || errno != 0) +		retval = 0; + + out: +	g_free (startup_id); + +	return (retval > 0) ? retval : 0; +} + +#ifndef G_OS_WIN32 +static GdkDisplay * +display_open_if_needed (const gchar *name) +{ +	GSList *displays; +	GSList *l; +	GdkDisplay *display = NULL; + +	displays = gdk_display_manager_list_displays (gdk_display_manager_get ()); + +	for (l = displays; l != NULL; l = l->next) +	{ +		if (strcmp (gdk_display_get_name ((GdkDisplay *) l->data), name) == 0) +		{ +			display = l->data; +			break; +		} +	} + +	g_slist_free (displays); + +	return display != NULL ? display : gdk_display_open (name); +} + +/* serverside */ +static void +on_message_received (const char *message, +		     gpointer    data) +{ +	const GeditEncoding *encoding = NULL; +	gchar **commands; +	gchar **params; +	gint workspace; +	gint viewport_x; +	gint viewport_y; +	gchar *display_name; +	gint screen_number; +	gint i; +	GeditApp *app; +	GeditWindow *window; +	GdkDisplay *display; +	GdkScreen *screen; + +	g_return_if_fail (message != NULL); + +	gedit_debug_message (DEBUG_APP, "Received message:\n%s\n", message); + +	commands = g_strsplit (message, "\v", -1); + +	/* header */ +	params = g_strsplit (commands[0], "\t", 6); +	startup_timestamp = atoi (params[0]); +	display_name = params[1]; +	screen_number = atoi (params[2]); +	workspace = atoi (params[3]); +	viewport_x = atoi (params[4]); +	viewport_y = atoi (params[5]); + +	display = display_open_if_needed (display_name); +	if (display == NULL) +	{ +		g_warning ("Could not open display %s\n", display_name); +		g_strfreev (params); +		goto out; +	} + +	screen = gdk_display_get_screen (display, screen_number); + +	g_strfreev (params); + +	/* body */ +	for (i = 1; commands[i] != NULL; i++) +	{ +		params = g_strsplit (commands[i], "\t", -1); + +		if (strcmp (params[0], "NEW-WINDOW") == 0) +		{ +			new_window_option = TRUE; +		} +		else if (strcmp (params[0], "NEW-DOCUMENT") == 0) +		{ +			new_document_option = TRUE; +		} +		else if (strcmp (params[0], "OPEN-URIS") == 0) +		{ +			gint n_uris, j; +			gchar **uris; + +			line_position = atoi (params[1]); + +			if (params[2] != '\0') +				encoding = gedit_encoding_get_from_charset (params[2]); + +			n_uris = atoi (params[3]); +			uris = g_strsplit (params[4], " ", n_uris); + +			for (j = 0; j < n_uris; j++) +			{ +				GFile *file; + +				file = g_file_new_for_uri (uris[j]); +				file_list = g_slist_prepend (file_list, file); +			} + +			file_list = g_slist_reverse (file_list); + +			/* the list takes ownerhip of the strings, +			 * only free the array */ +			g_free (uris); +		} +		else +		{ +			g_warning ("Unexpected bacon command"); +		} + +		g_strfreev (params); +	} + +	/* execute the commands */ + +	app = gedit_app_get_default (); + +	if (new_window_option) +	{ +		window = gedit_app_create_window (app, screen); +	} +	else +	{ +		/* get a window in the current workspace (if exists) and raise it */ +		window = _gedit_app_get_window_in_viewport (app, +							    screen, +							    workspace, +							    viewport_x, +							    viewport_y); +	} + +	if (file_list != NULL) +	{ +		_gedit_cmd_load_files_from_prompt (window, +						   file_list, +						   encoding, +						   line_position); + +		if (new_document_option) +			gedit_window_create_tab (window, TRUE); +	} +	else +	{ +		GeditDocument *doc; +		doc = gedit_window_get_active_document (window); + +		if (doc == NULL || +		    !gedit_document_is_untouched (doc) || +		    new_document_option) +			gedit_window_create_tab (window, TRUE); +	} + +	/* set the proper interaction time on the window. +	 * Fall back to roundtripping to the X server when we +	 * don't have the timestamp, e.g. when launched from +	 * terminal. We also need to make sure that the window +	 * has been realized otherwise it will not work. lame. +	 */ +	if (!GTK_WIDGET_REALIZED (window)) +		gtk_widget_realize (GTK_WIDGET (window)); + +#ifdef GDK_WINDOWING_X11 +	if (startup_timestamp <= 0) +		startup_timestamp = gdk_x11_get_server_time (gtk_widget_get_window (GTK_WIDGET (window))); + +	gdk_x11_window_set_user_time (gtk_widget_get_window (GTK_WIDGET (window)), +				      startup_timestamp); +#endif + +	gtk_window_present (GTK_WINDOW (window)); + + out: +	g_strfreev (commands); + +	free_command_line_data (); +} + +/* clientside */ +static void +send_bacon_message (void) +{ +	GdkScreen *screen; +	GdkDisplay *display; +	const gchar *display_name; +	gint screen_number; +	gint ws; +	gint viewport_x; +	gint viewport_y; +	GString *command; + +	/* the messages have the following format: +	 * <---                                  header                                     ---> <----            body             -----> +	 * timestamp \t display_name \t screen_number \t workspace \t viewport_x \t viewport_y \v OP1 \t arg \t arg \v OP2 \t arg \t arg|... +	 * +	 * when the arg is a list of uri, they are separated by a space. +	 * So the delimiters are \v for the commands, \t for the tokens in +	 * a command and ' ' for the uris: note that such delimiters cannot +	 * be part of an uri, this way parsing is easier. +	 */ + +	gedit_debug (DEBUG_APP); + +	screen = gdk_screen_get_default (); +	display = gdk_screen_get_display (screen); + +	display_name = gdk_display_get_name (display); +	screen_number = gdk_screen_get_number (screen); + +	gedit_debug_message (DEBUG_APP, "Display: %s", display_name); +	gedit_debug_message (DEBUG_APP, "Screen: %d", screen_number); + +	ws = gedit_utils_get_current_workspace (screen); +	gedit_utils_get_current_viewport (screen, &viewport_x, &viewport_y); + +	command = g_string_new (NULL); + +	/* header */ +	g_string_append_printf (command, +				"%" G_GUINT32_FORMAT "\t%s\t%d\t%d\t%d\t%d", +				startup_timestamp, +				display_name, +				screen_number, +				ws, +				viewport_x, +				viewport_y); + +	/* NEW-WINDOW command */ +	if (new_window_option) +	{ +		command = g_string_append_c (command, '\v'); +		command = g_string_append (command, "NEW-WINDOW"); +	} + +	/* NEW-DOCUMENT command */ +	if (new_document_option) +	{ +		command = g_string_append_c (command, '\v'); +		command = g_string_append (command, "NEW-DOCUMENT"); +	} + +	/* OPEN_URIS command, optionally specify line_num and encoding */ +	if (file_list) +	{ +		GSList *l; + +		command = g_string_append_c (command, '\v'); +		command = g_string_append (command, "OPEN-URIS"); + +		g_string_append_printf (command, +					"\t%d\t%s\t%u\t", +					line_position, +					encoding_charset ? encoding_charset : "", +					g_slist_length (file_list)); + +		for (l = file_list; l != NULL; l = l->next) +		{ +			gchar *uri; + +			uri = g_file_get_uri (G_FILE (l->data)); +			command = g_string_append (command, uri); +			if (l->next != NULL) +				command = g_string_append_c (command, ' '); + +			g_free (uri); +		} +	} + +	gedit_debug_message (DEBUG_APP, "Bacon Message: %s", command->str); + +	bacon_message_connection_send (connection, +				       command->str); + +	g_string_free (command, TRUE); +} +#endif /* G_OS_WIN32 */ + +#ifdef G_OS_WIN32 +static void +setup_path (void) +{ +	gchar *path; +	gchar *installdir; +	gchar *bin; + +	installdir = g_win32_get_package_installation_directory_of_module (NULL); + +	bin = g_build_filename (installdir, +				"bin", NULL); +	g_free (installdir); + +	/* Set PATH to include the gedit executable's folder */ +	path = g_build_path (";", +			     bin, +			     g_getenv ("PATH"), +			     NULL); +	g_free (bin); + +	if (!g_setenv ("PATH", path, TRUE)) +		g_warning ("Could not set PATH for gedit"); + +	g_free (path); +} +#endif + +int +main (int argc, char *argv[]) +{ +	GOptionContext *context; +	GeditPluginsEngine *engine; +	GeditWindow *window; +	GeditApp *app; +	gboolean restored = FALSE; +	GError *error = NULL; +	gchar *dir; +	gchar *icon_dir; + +	/* Init type system as soon as possible */ +	g_type_init (); + +	/* Init glib threads asap */ +	g_thread_init (NULL); + +	/* Setup debugging */ +	gedit_debug_init (); +	gedit_debug_message (DEBUG_APP, "Startup"); + +	setlocale (LC_ALL, ""); + +	dir = gedit_dirs_get_gedit_locale_dir (); +	bindtextdomain (GETTEXT_PACKAGE, dir); +	g_free (dir); +	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +	textdomain (GETTEXT_PACKAGE); + +	startup_timestamp = get_startup_timestamp(); + +	/* Setup command line options */ +	context = g_option_context_new (_("- Edit text files")); +	g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE); +	g_option_context_add_group (context, gtk_get_option_group (FALSE)); +	g_option_context_add_group (context, egg_sm_client_get_option_group ()); + +#ifdef G_OS_WIN32 +	setup_path (); + +	/* If we open gedit from a console get the stdout printing */ +	if (fileno (stdout) != -1 && +		_get_osfhandle (fileno (stdout)) != -1) +	{ +		/* stdout is fine, presumably redirected to a file or pipe */ +	} +	else +	{ +		typedef BOOL (* WINAPI AttachConsole_t) (DWORD); + +		AttachConsole_t p_AttachConsole = +			(AttachConsole_t) GetProcAddress (GetModuleHandle ("kernel32.dll"), +							  "AttachConsole"); + +		if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS)) +		{ +			freopen ("CONOUT$", "w", stdout); +			dup2 (fileno (stdout), 1); +			freopen ("CONOUT$", "w", stderr); +			dup2 (fileno (stderr), 2); +		} +	} +#endif + +	gtk_init (&argc, &argv); + +	if (!g_option_context_parse (context, &argc, &argv, &error)) +	{ +	        g_print(_("%s\nRun '%s --help' to see a full list of available command line options.\n"), +			error->message, argv[0]); +		g_error_free (error); +		return 1; +	} + +	g_option_context_free (context); + +#ifndef G_OS_WIN32 +	gedit_debug_message (DEBUG_APP, "Create bacon connection"); + +	connection = bacon_message_connection_new ("gedit"); + +	if (connection != NULL) +	{ +		if (!bacon_message_connection_get_is_server (connection)) +		{ +			gedit_debug_message (DEBUG_APP, "I'm a client"); + +			gedit_get_command_line_data (); + +			send_bacon_message (); + +			free_command_line_data (); + +			/* we never popup a window... tell startup-notification +			 * that we are done. +			 */ +			gdk_notify_startup_complete (); + +			bacon_message_connection_free (connection); + +			exit (0); +		} +		else +		{ +		  	gedit_debug_message (DEBUG_APP, "I'm a server"); + +			bacon_message_connection_set_callback (connection, +							       on_message_received, +							       NULL); +		} +	} +	else +	{ +		g_warning ("Cannot create the 'gedit' connection."); +	} +#endif + +	gedit_debug_message (DEBUG_APP, "Set icon"); + +	dir = gedit_dirs_get_gedit_data_dir (); +	icon_dir = g_build_filename (dir, +				     "icons", +				     NULL); +	g_free (dir); + +	gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), +					   icon_dir); +	g_free (icon_dir); + +#ifdef GDK_WINDOWING_X11 +	/* Set the associated .desktop file */ +	egg_set_desktop_file (DATADIR "/applications/gedit.desktop"); +#else +	/* manually set name and icon */ +	g_set_application_name("gedit"); +	gtk_window_set_default_icon_name ("accessories-text-editor"); +#endif + +	/* Load user preferences */ +	gedit_debug_message (DEBUG_APP, "Init prefs manager"); +	gedit_prefs_manager_app_init (); + +	/* Init plugins engine */ +	gedit_debug_message (DEBUG_APP, "Init plugins"); +	engine = gedit_plugins_engine_get_default (); + +	#if !GTK_CHECK_VERSION(3, 0, 0) +		gtk_about_dialog_set_url_hook(gedit_utils_activate_url, NULL, NULL); +	#endif +	/* Initialize session management */ +	gedit_debug_message (DEBUG_APP, "Init session manager"); +	gedit_session_init (); + +#ifdef OS_OSX +	ige_mac_menu_set_global_key_handler_enabled (FALSE); +#endif + +	if (gedit_session_is_restored ()) +		restored = gedit_session_load (); + +	if (!restored) +	{ +		gedit_debug_message (DEBUG_APP, "Analyze command line data"); +		gedit_get_command_line_data (); + +		gedit_debug_message (DEBUG_APP, "Get default app"); +		app = gedit_app_get_default (); + +		gedit_debug_message (DEBUG_APP, "Create main window"); +		window = gedit_app_create_window (app, NULL); + +		if (file_list != NULL) +		{ +			const GeditEncoding *encoding = NULL; + +			if (encoding_charset) +				encoding = gedit_encoding_get_from_charset (encoding_charset); + +			gedit_debug_message (DEBUG_APP, "Load files"); +			_gedit_cmd_load_files_from_prompt (window, +							   file_list, +							   encoding, +							   line_position); +		} +		else +		{ +			gedit_debug_message (DEBUG_APP, "Create tab"); +			gedit_window_create_tab (window, TRUE); +		} + +		gedit_debug_message (DEBUG_APP, "Show window"); +		gtk_widget_show (GTK_WIDGET (window)); + +		free_command_line_data (); +	} + +	gedit_debug_message (DEBUG_APP, "Start gtk-main"); + +#ifdef OS_OSX +	gedit_osx_init(gedit_app_get_default ()); +#endif +	gtk_main(); + +#ifndef G_OS_WIN32 +	bacon_message_connection_free (connection); +#endif + +	/* We kept the original engine reference here. So let's unref it to +	 * finalize it properly. +	 */ +	g_object_unref (engine); +	gedit_prefs_manager_app_shutdown (); + +#ifndef ENABLE_GVFS_METADATA +	gedit_metadata_manager_shutdown (); +#endif + +	return 0; +} + diff --git a/gedit/gedit.rc b/gedit/gedit.rc new file mode 100755 index 00000000..17d8eb13 --- /dev/null +++ b/gedit/gedit.rc @@ -0,0 +1 @@ +A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "../pixmaps/gedit.ico"
 diff --git a/gedit/gedittextregion.c b/gedit/gedittextregion.c new file mode 100755 index 00000000..f6790489 --- /dev/null +++ b/gedit/gedittextregion.c @@ -0,0 +1,647 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-  + * + * gedittextregion.h - GtkTextMark based region utility functions + * + * This file is part of the GtkSourceView widget + * + * Copyright (C) 2002 Gustavo Gir�ldez <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA.   + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> + +#include "gedittextregion.h" + + +#undef ENABLE_DEBUG +/* +#define ENABLE_DEBUG +*/ + +#ifdef ENABLE_DEBUG +#define DEBUG(x) (x) +#else +#define DEBUG(x) +#endif + +typedef struct _Subregion { +	GtkTextMark *start; +	GtkTextMark *end; +} Subregion; + +struct _GeditTextRegion { +	GtkTextBuffer *buffer; +	GList         *subregions; +	guint32        time_stamp; +}; + +typedef struct _GeditTextRegionIteratorReal GeditTextRegionIteratorReal; + +struct _GeditTextRegionIteratorReal { +	GeditTextRegion *region; +	guint32        region_time_stamp; +	 +	GList         *subregions; +}; + + +/* ---------------------------------------------------------------------- +   Private interface +   ---------------------------------------------------------------------- */ + +/* Find and return a subregion node which contains the given text +   iter.  If left_side is TRUE, return the subregion which contains +   the text iter or which is the leftmost; else return the rightmost +   subregion */ +static GList *  +find_nearest_subregion (GeditTextRegion     *region, +			const GtkTextIter *iter, +			GList             *begin, +			gboolean           leftmost, +			gboolean           include_edges) +{ +	GList *l, *retval; +	 +	g_return_val_if_fail (region != NULL && iter != NULL, NULL); + +	if (!begin) +		begin = region->subregions; +	 +	if (begin) +		retval = begin->prev; +	else +		retval = NULL; +	 +	for (l = begin; l; l = l->next) { +		GtkTextIter sr_iter; +		Subregion *sr = l->data; +		gint cmp; + +		if (!leftmost) { +			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->end); +			cmp = gtk_text_iter_compare (iter, &sr_iter); +			if (cmp < 0 || (cmp == 0 && include_edges)) { +				retval = l; +				break; +			} + +		} else { +			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->start); +			cmp = gtk_text_iter_compare (iter, &sr_iter); +			if (cmp > 0 || (cmp == 0 && include_edges)) +				retval = l; +			else +				break; +		} +	} +	return retval; +} + +/* ---------------------------------------------------------------------- +   Public interface +   ---------------------------------------------------------------------- */ + +GeditTextRegion * +gedit_text_region_new (GtkTextBuffer *buffer) +{ +	GeditTextRegion *region; + +	g_return_val_if_fail (buffer != NULL, NULL); +	 +	region = g_new (GeditTextRegion, 1); +	region->buffer = buffer; +	region->subregions = NULL; +	region->time_stamp = 0; +	 +	return region; +} + +void  +gedit_text_region_destroy (GeditTextRegion *region, gboolean delete_marks) +{ +	g_return_if_fail (region != NULL); + +	while (region->subregions) { +		Subregion *sr = region->subregions->data; +		if (delete_marks) { +			gtk_text_buffer_delete_mark (region->buffer, sr->start); +			gtk_text_buffer_delete_mark (region->buffer, sr->end); +		} +		g_free (sr); +		region->subregions = g_list_delete_link (region->subregions, +							 region->subregions); +	} +	region->buffer = NULL; +	region->time_stamp = 0; +	 +	g_free (region); +} + +GtkTextBuffer * +gedit_text_region_get_buffer (GeditTextRegion *region) +{ +	g_return_val_if_fail (region != NULL, NULL); +	 +	return region->buffer; +} + +static void +gedit_text_region_clear_zero_length_subregions (GeditTextRegion *region) +{ +	GtkTextIter start, end; +	GList *node; +	 +	g_return_if_fail (region != NULL); + +	for (node = region->subregions; node; ) { +		Subregion *sr = node->data; +		gtk_text_buffer_get_iter_at_mark (region->buffer, &start, sr->start); +		gtk_text_buffer_get_iter_at_mark (region->buffer, &end, sr->end); +		if (gtk_text_iter_equal (&start, &end)) { +			gtk_text_buffer_delete_mark (region->buffer, sr->start); +			gtk_text_buffer_delete_mark (region->buffer, sr->end); +			g_free (sr); +			if (node == region->subregions) +				region->subregions = node = g_list_delete_link (node, node); +			else +				node = g_list_delete_link (node, node); + +			++region->time_stamp; +			 +		} else { +			node = node->next; +		} +	} +} + +void  +gedit_text_region_add (GeditTextRegion     *region, +		     const GtkTextIter *_start, +		     const GtkTextIter *_end) +{ +	GList *start_node, *end_node; +	GtkTextIter start, end; +	 +	g_return_if_fail (region != NULL && _start != NULL && _end != NULL); +	 +	start = *_start; +	end = *_end; +	 +	DEBUG (g_print ("---\n")); +	DEBUG (gedit_text_region_debug_print (region)); +	DEBUG (g_message ("region_add (%d, %d)", +			  gtk_text_iter_get_offset (&start), +			  gtk_text_iter_get_offset (&end))); + +	gtk_text_iter_order (&start, &end); +	 +	/* don't add zero-length regions */ +	if (gtk_text_iter_equal (&start, &end)) +		return; + +	/* find bounding subregions */ +	start_node = find_nearest_subregion (region, &start, NULL, FALSE, TRUE); +	end_node = find_nearest_subregion (region, &end, start_node, TRUE, TRUE); + +	if (start_node == NULL || end_node == NULL || end_node == start_node->prev) { +		/* create the new subregion */ +		Subregion *sr = g_new0 (Subregion, 1); +		sr->start = gtk_text_buffer_create_mark (region->buffer, NULL, &start, TRUE); +		sr->end = gtk_text_buffer_create_mark (region->buffer, NULL, &end, FALSE); +		 +		if (start_node == NULL) { +			/* append the new region */ +			region->subregions = g_list_append (region->subregions, sr); +			 +		} else if (end_node == NULL) { +			/* prepend the new region */ +			region->subregions = g_list_prepend (region->subregions, sr); + +		} else { +			/* we are in the middle of two subregions */ +			region->subregions = g_list_insert_before (region->subregions, +								   start_node, sr); +		} +	} +	else { +		GtkTextIter iter; +		Subregion *sr = start_node->data; +		if (start_node != end_node) { +			/* we need to merge some subregions */ +			GList *l = start_node->next; +			Subregion *q; +			 +			gtk_text_buffer_delete_mark (region->buffer, sr->end); +			while (l != end_node) { +				q = l->data; +				gtk_text_buffer_delete_mark (region->buffer, q->start); +				gtk_text_buffer_delete_mark (region->buffer, q->end); +				g_free (q); +				l = g_list_delete_link (l, l); +			} +			q = l->data; +			gtk_text_buffer_delete_mark (region->buffer, q->start); +			sr->end = q->end; +			g_free (q); +			l = g_list_delete_link (l, l); +		} +		/* now move marks if that action expands the region */ +		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->start); +		if (gtk_text_iter_compare (&iter, &start) > 0) +			gtk_text_buffer_move_mark (region->buffer, sr->start, &start); +		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->end); +		if (gtk_text_iter_compare (&iter, &end) < 0) +			gtk_text_buffer_move_mark (region->buffer, sr->end, &end); +	} + +	++region->time_stamp; + +	DEBUG (gedit_text_region_debug_print (region)); +} + +void  +gedit_text_region_subtract (GeditTextRegion     *region, +			  const GtkTextIter *_start, +			  const GtkTextIter *_end) +{ +	GList *start_node, *end_node, *node; +	GtkTextIter sr_start_iter, sr_end_iter; +	gboolean done; +	gboolean start_is_outside, end_is_outside; +	Subregion *sr; +	GtkTextIter start, end; + +	g_return_if_fail (region != NULL && _start != NULL && _end != NULL); +	 +	start = *_start; +	end = *_end; +	 +	DEBUG (g_print ("---\n")); +	DEBUG (gedit_text_region_debug_print (region)); +	DEBUG (g_message ("region_substract (%d, %d)", +			  gtk_text_iter_get_offset (&start), +			  gtk_text_iter_get_offset (&end))); +	 +	gtk_text_iter_order (&start, &end); +	 +	/* find bounding subregions */ +	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE); +	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE); + +	/* easy case first */ +	if (start_node == NULL || end_node == NULL || end_node == start_node->prev) +		return; +	 +	/* deal with the start point */ +	start_is_outside = end_is_outside = FALSE; +	 +	sr = start_node->data; +	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start); +	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end); + +	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter) && +	    !gtk_text_iter_equal (&start, &sr_start_iter)) { +		/* the starting point is inside the first subregion */ +		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) && +		    !gtk_text_iter_equal (&end, &sr_end_iter)) { +			/* the ending point is also inside the first +                           subregion: we need to split */ +			Subregion *new_sr = g_new0 (Subregion, 1); +			new_sr->end = sr->end; +			new_sr->start = gtk_text_buffer_create_mark (region->buffer, +								     NULL, &end, TRUE); +			start_node = g_list_insert_before (start_node, start_node->next, new_sr); + +			sr->end = gtk_text_buffer_create_mark (region->buffer, +							       NULL, &start, FALSE); + +			/* no further processing needed */ +			DEBUG (g_message ("subregion splitted")); +			 +			return; +		} else { +			/* the ending point is outside, so just move +                           the end of the subregion to the starting point */ +			gtk_text_buffer_move_mark (region->buffer, sr->end, &start); +		} +	} else { +		/* the starting point is outside (and so to the left) +                   of the first subregion */ +		DEBUG (g_message ("start is outside")); +			 +		start_is_outside = TRUE; +	} +	 +	/* deal with the end point */ +	if (start_node != end_node) { +		sr = end_node->data; +		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start); +		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end); +	} +	 +	if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) && +	    !gtk_text_iter_equal (&end, &sr_end_iter)) { +		/* ending point is inside, move the start mark */ +		gtk_text_buffer_move_mark (region->buffer, sr->start, &end); +	} else { +		end_is_outside = TRUE; +		DEBUG (g_message ("end is outside")); +		 +	} +	 +	/* finally remove any intermediate subregions */ +	done = FALSE; +	node = start_node; +	 +	while (!done) { +		if (node == end_node) +			/* we are done, exit in the next iteration */ +			done = TRUE; +		 +		if ((node == start_node && !start_is_outside) || +		    (node == end_node && !end_is_outside)) { +			/* skip starting or ending node */ +			node = node->next; +		} else { +			GList *l = node->next; +			sr = node->data; +			gtk_text_buffer_delete_mark (region->buffer, sr->start); +			gtk_text_buffer_delete_mark (region->buffer, sr->end); +			g_free (sr); +			region->subregions = g_list_delete_link (region->subregions, +								 node); +			node = l; +		} +	} + +	++region->time_stamp; + +	DEBUG (gedit_text_region_debug_print (region)); + +	/* now get rid of empty subregions */ +	gedit_text_region_clear_zero_length_subregions (region); + +	DEBUG (gedit_text_region_debug_print (region)); +} + +gint  +gedit_text_region_subregions (GeditTextRegion *region) +{ +	g_return_val_if_fail (region != NULL, 0); + +	return g_list_length (region->subregions); +} + +gboolean  +gedit_text_region_nth_subregion (GeditTextRegion *region, +			       guint          subregion, +			       GtkTextIter   *start, +			       GtkTextIter   *end) +{ +	Subregion *sr; +	 +	g_return_val_if_fail (region != NULL, FALSE); + +	sr = g_list_nth_data (region->subregions, subregion); +	if (sr == NULL) +		return FALSE; + +	if (start) +		gtk_text_buffer_get_iter_at_mark (region->buffer, start, sr->start); +	if (end) +		gtk_text_buffer_get_iter_at_mark (region->buffer, end, sr->end); + +	return TRUE; +} + +GeditTextRegion *  +gedit_text_region_intersect (GeditTextRegion     *region, +			   const GtkTextIter *_start, +			   const GtkTextIter *_end) +{ +	GList *start_node, *end_node, *node; +	GtkTextIter sr_start_iter, sr_end_iter; +	Subregion *sr, *new_sr; +	gboolean done; +	GeditTextRegion *new_region; +	GtkTextIter start, end; +	 +	g_return_val_if_fail (region != NULL && _start != NULL && _end != NULL, NULL); +	 +	start = *_start; +	end = *_end; +	 +	gtk_text_iter_order (&start, &end); +	 +	/* find bounding subregions */ +	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE); +	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE); + +	/* easy case first */ +	if (start_node == NULL || end_node == NULL || end_node == start_node->prev) +		return NULL; +	 +	new_region = gedit_text_region_new (region->buffer); +	done = FALSE; +	 +	sr = start_node->data; +	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start); +	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end); + +	/* starting node */ +	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter)) { +		new_sr = g_new0 (Subregion, 1); +		new_region->subregions = g_list_prepend (new_region->subregions, new_sr); + +		new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL, +							     &start, TRUE); +		if (start_node == end_node) { +			/* things will finish shortly */ +			done = TRUE; +			if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter)) +				new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, +									   NULL, &end, FALSE); +			else +				new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, +									   NULL, &sr_end_iter, +									   FALSE); +		} else { +			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL, +								   &sr_end_iter, FALSE); +		} +		node = start_node->next; +	} else { +		/* start should be the same as the subregion, so copy it in the loop */ +		node = start_node; +	} + +	if (!done) { +		while (node != end_node) { +			/* copy intermediate subregions verbatim */ +			sr = node->data; +			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, +							  sr->start); +			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end); +			 +			new_sr = g_new0 (Subregion, 1); +			new_region->subregions = g_list_prepend (new_region->subregions, new_sr); +			new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL, +								     &sr_start_iter, TRUE); +			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL, +								   &sr_end_iter, FALSE); +			/* next node */ +			node = node->next; +		} + +		/* ending node */ +		sr = node->data; +		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start); +		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end); +		 +		new_sr = g_new0 (Subregion, 1); +		new_region->subregions = g_list_prepend (new_region->subregions, new_sr); +		 +		new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL, +							     &sr_start_iter, TRUE); + +		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter)) +			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL, +								   &end, FALSE); +		else +			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL, +								   &sr_end_iter, FALSE); +	} + +	new_region->subregions = g_list_reverse (new_region->subregions); +	return new_region; +} + +static gboolean  +check_iterator (GeditTextRegionIteratorReal *real) +{ +	if ((real->region == NULL) || +	    (real->region_time_stamp != real->region->time_stamp)) +	{ +		g_warning("Invalid iterator: either the iterator " +                	  "is uninitialized, or the region " +                 	  "has been modified since the iterator " +                 	  "was created."); +                 	   +                return FALSE; +	} + +	return TRUE; +} + +void        +gedit_text_region_get_iterator (GeditTextRegion         *region, +                              GeditTextRegionIterator *iter, +                              guint                  start) +{ +	GeditTextRegionIteratorReal *real; + +	g_return_if_fail (region != NULL); +	g_return_if_fail (iter != NULL);	 + +	real = (GeditTextRegionIteratorReal *)iter; + +	/* region->subregions may be NULL, -> end iter */ + +	real->region = region; +	real->subregions = g_list_nth (region->subregions, start); +	real->region_time_stamp = region->time_stamp; +} + +gboolean +gedit_text_region_iterator_is_end (GeditTextRegionIterator *iter) +{ +	GeditTextRegionIteratorReal *real; + +	g_return_val_if_fail (iter != NULL, FALSE);	 + +	real = (GeditTextRegionIteratorReal *)iter; +	g_return_val_if_fail (check_iterator (real), FALSE); + +	return (real->subregions == NULL); +} + +gboolean +gedit_text_region_iterator_next (GeditTextRegionIterator *iter) +{ +	GeditTextRegionIteratorReal *real; + +	g_return_val_if_fail (iter != NULL, FALSE);	 + +	real = (GeditTextRegionIteratorReal *)iter; +	g_return_val_if_fail (check_iterator (real), FALSE); + +	if (real->subregions != NULL) { +		real->subregions = g_list_next (real->subregions); +		return TRUE; +	} +	else +		return FALSE; +} + +void        +gedit_text_region_iterator_get_subregion (GeditTextRegionIterator *iter, +					GtkTextIter           *start, +					GtkTextIter           *end) +{ +	GeditTextRegionIteratorReal *real; +	Subregion *sr; + +	g_return_if_fail (iter != NULL); + +	real = (GeditTextRegionIteratorReal *)iter; +	g_return_if_fail (check_iterator (real)); +	g_return_if_fail (real->subregions != NULL); + +	sr = (Subregion*)real->subregions->data; +	g_return_if_fail (sr != NULL); + +	if (start) +		gtk_text_buffer_get_iter_at_mark (real->region->buffer, start, sr->start); +	if (end) +		gtk_text_buffer_get_iter_at_mark (real->region->buffer, end, sr->end); +} + +void  +gedit_text_region_debug_print (GeditTextRegion *region) +{ +	GList *l; +	 +	g_return_if_fail (region != NULL); + +	g_print ("Subregions: "); +	l = region->subregions; +	while (l) { +		Subregion *sr = l->data; +		GtkTextIter iter1, iter2; +		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter1, sr->start); +		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter2, sr->end); +		g_print ("%d-%d ", gtk_text_iter_get_offset (&iter1), +			 gtk_text_iter_get_offset (&iter2)); +		l = l->next; +	} +	g_print ("\n"); +} + diff --git a/gedit/gedittextregion.h b/gedit/gedittextregion.h new file mode 100755 index 00000000..594a4950 --- /dev/null +++ b/gedit/gedittextregion.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-  + * + * gedittextregion.h - GtkTextMark based region utility functions + * + * This file is part of the GtkSourceView widget + * + * Copyright (C) 2002 Gustavo Gir�ldez <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA.   + */ + +#ifndef __GEDIT_TEXT_REGION_H__ +#define __GEDIT_TEXT_REGION_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _GeditTextRegion		GeditTextRegion; +typedef struct _GeditTextRegionIterator	GeditTextRegionIterator; + +struct _GeditTextRegionIterator { +	/* GeditTextRegionIterator is an opaque datatype; ignore all these fields. +	 * Initialize the iter with gedit_text_region_get_iterator +	 * function +	 */ +	/*< private >*/ +	gpointer dummy1; +	guint32  dummy2; +	gpointer dummy3;	 +}; + +GeditTextRegion *gedit_text_region_new                          (GtkTextBuffer *buffer); +void           gedit_text_region_destroy                      (GeditTextRegion *region, +							     gboolean       delete_marks); + +GtkTextBuffer *gedit_text_region_get_buffer                   (GeditTextRegion *region); + +void           gedit_text_region_add                          (GeditTextRegion     *region, +							     const GtkTextIter *_start, +							     const GtkTextIter *_end); + +void           gedit_text_region_subtract                     (GeditTextRegion     *region, +							     const GtkTextIter *_start, +							     const GtkTextIter *_end); + +gint           gedit_text_region_subregions                   (GeditTextRegion *region); + +gboolean       gedit_text_region_nth_subregion                (GeditTextRegion *region, +							     guint          subregion, +							     GtkTextIter   *start, +							     GtkTextIter   *end); + +GeditTextRegion *gedit_text_region_intersect                    (GeditTextRegion     *region, +							     const GtkTextIter *_start, +							     const GtkTextIter *_end); + +void           gedit_text_region_get_iterator                 (GeditTextRegion         *region, +                                                             GeditTextRegionIterator *iter, +                                                             guint                  start); + +gboolean       gedit_text_region_iterator_is_end              (GeditTextRegionIterator *iter); + +/* Returns FALSE if iterator is the end iterator */ +gboolean       gedit_text_region_iterator_next	            (GeditTextRegionIterator *iter); + +void           gedit_text_region_iterator_get_subregion       (GeditTextRegionIterator *iter, +							     GtkTextIter           *start, +							     GtkTextIter           *end); + +void           gedit_text_region_debug_print                  (GeditTextRegion *region); + +G_END_DECLS + +#endif /* __GEDIT_TEXT_REGION_H__ */ diff --git a/gedit/osx/Makefile.am b/gedit/osx/Makefile.am new file mode 100755 index 00000000..4d734169 --- /dev/null +++ b/gedit/osx/Makefile.am @@ -0,0 +1,23 @@ +INCLUDES = 							\ +	-I$(top_srcdir)						\ +	-I$(top_builddir)					\ +	-I$(top_srcdir)/gedit 					\ +	-I$(top_builddir)/gedit					\ +	$(GEDIT_CFLAGS) 					\ +	$(IGE_MAC_CFLAGS)					\ +	$(WARN_CFLAGS)						\ +	$(DISABLE_DEPRECATED_CFLAGS) + +noinst_LTLIBRARIES = libosx.la + +libosx_la_LDFLAGS = -framework Carbon -framework ApplicationServices -framework Cocoa +libosx_la_LIBADD = -lobjc +libosx_la_CFLAGS = -xobjective-c + +libosx_la_SOURCES = 		\ +	gedit-osx.c 		\ +	gedit-osx.h		\ +	gedit-osx-delegate.m 	\ +	gedit-osx-delegate.h + +-include $(top_srcdir)/git.mk diff --git a/gedit/osx/gedit-osx-delegate.h b/gedit/osx/gedit-osx-delegate.h new file mode 100755 index 00000000..0b4411e8 --- /dev/null +++ b/gedit/osx/gedit-osx-delegate.h @@ -0,0 +1,16 @@ +#ifndef GEDIT_OSX_DELEGATE_H_ +#define GEDIT_OSX_DELEGATE_H_ + +#import <Foundation/NSAppleEventManager.h> + +@interface GeditOSXDelegate : NSObject +{ +} + +-(id) init; +-(void) openFiles:(NSAppleEventDescriptor*)event +        withReply:(NSAppleEventDescriptor*)reply; + +@end + +#endif /* GEDIT_OSX_DELEGATE_H_ */ diff --git a/gedit/osx/gedit-osx-delegate.m b/gedit/osx/gedit-osx-delegate.m new file mode 100755 index 00000000..41b0b262 --- /dev/null +++ b/gedit/osx/gedit-osx-delegate.m @@ -0,0 +1,84 @@ +#import "gedit-osx-delegate.h" +#import <Foundation/NSAppleEventManager.h> +#import <Foundation/NSAppleEventDescriptor.h> +#import <Foundation/NSData.h> +#include <glib.h> +#include <gedit/gedit-app.h> +#include <gedit/gedit-commands.h> + +@implementation GeditOSXDelegate +-(id)init +{ +	if ((self = [super init])) +	{ +		NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; + +	    [em setEventHandler:self +	            andSelector:@selector(openFiles:withReply:) +	          forEventClass:kCoreEventClass +	             andEventID:kAEOpenDocuments]; +	} +	 +	return self; +} + +static GeditWindow * +get_window(NSAppleEventDescriptor *event) +{ +	GeditApp *app = gedit_app_get_default (); +	return gedit_app_get_active_window (app); +} + +- (void)openFiles:(NSAppleEventDescriptor*)event +        withReply:(NSAppleEventDescriptor*)reply +{ +	NSAppleEventDescriptor *fileList = [event paramDescriptorForKeyword:keyDirectObject]; +	NSInteger i; +	GSList *uris = NULL; +	 +	if (!fileList) +	{ +		return; +	} +	 +	for (i = 1; i <= [fileList numberOfItems]; ++i) +	{ +		NSAppleEventDescriptor *fileAliasDesc = [fileList descriptorAtIndex:i]; +		NSAppleEventDescriptor *fileURLDesc; +		NSData *fileURLData; +		gchar *url; +		 +		if (!fileAliasDesc) +		{ +			continue; +		} +		 +		fileURLDesc = [fileAliasDesc coerceToDescriptorType:typeFileURL]; +		 +		if (!fileURLDesc) +		{ +			continue; +		} +		 +		fileURLData = [fileURLDesc data]; +		 +		if (!fileURLData) +		{ +			continue; +		} +		 +		url = g_strndup([fileURLData bytes], [fileURLData length]); +		uris = g_slist_prepend (uris, url); +	} +	 +	if (uris != NULL) +	{ +		GeditWindow *window = get_window (event); +		gedit_commands_load_uris (window, uris, NULL, 0); + +		g_slist_foreach (uris, (GFunc)g_free, NULL); +		g_slist_free (uris); +	} +} + +@end
\ No newline at end of file diff --git a/gedit/osx/gedit-osx.c b/gedit/osx/gedit-osx.c new file mode 100755 index 00000000..e7a18d42 --- /dev/null +++ b/gedit/osx/gedit-osx.c @@ -0,0 +1,94 @@ +#include "gedit-osx.h" +#include <gdk/gdkquartz.h> +#include <Carbon/Carbon.h> + +#import "gedit-osx-delegate.h" + +void +gedit_osx_set_window_title (GeditWindow   *window,  +			    gchar const   *title, +			    GeditDocument *document) +{ +	NSWindow *native; + +	g_return_if_fail (GEDIT_IS_WINDOW (window)); + +	if (GTK_WIDGET (window)->window == NULL) +	{ +		return; +	} + +	native = gdk_quartz_window_get_nswindow (GTK_WIDGET (window)->window); + +	if (document) +	{ +		bool ismodified; + +		if (gedit_document_is_untitled (document)) +		{ +			[native setRepresentedURL:nil]; +		} +		else +		{ +			const gchar *uri = gedit_document_get_uri (document); +			NSURL *nsurl = [NSURL URLWithString:[NSString stringWithUTF8String:uri]]; +			 +			[native setRepresentedURL:nsurl]; +		} + +		ismodified = !gedit_document_is_untouched (document);  +		[native setDocumentEdited:ismodified]; +	} +	else +	{ +		[native setRepresentedURL:nil]; +		[native setDocumentEdited:false]; +	} + +	gtk_window_set_title (GTK_WINDOW (window), title); +} + +gboolean +gedit_osx_show_url (const gchar *url) +{ + 	return [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]; +} + +gboolean +gedit_osx_show_help (const gchar *link_id) +{ +	gchar *link; +	gboolean ret; + +	if (link_id) +	{ +		link = g_strdup_printf ("http://library.mate.org/users/gedit/stable/%s", +					link_id); +	} +	else +	{ +		link = g_strdup ("http://library.mate.org/users/gedit/stable/"); +	} + +	ret = gedit_osx_show_url (link); +	g_free (link); + +	return ret; +} + +static void +destroy_delegate (GeditOSXDelegate *delegate) +{ +	[delegate dealloc]; +} + +void +gedit_osx_init(GeditApp *app) +{ +	GeditOSXDelegate *delegate = [[GeditOSXDelegate alloc] init]; +	 +	g_object_set_data_full (G_OBJECT (app), +	                        "GeditOSXDelegate", +	                        delegate, +							(GDestroyNotify)destroy_delegate); +}
\ No newline at end of file diff --git a/gedit/osx/gedit-osx.h b/gedit/osx/gedit-osx.h new file mode 100755 index 00000000..82f0120b --- /dev/null +++ b/gedit/osx/gedit-osx.h @@ -0,0 +1,17 @@ +#ifndef __GEDIT_OSX_H__ +#define __GEDIT_OSX_H__ + +#include <gtk/gtk.h> +#include <gedit/gedit-window.h> +#include <gedit/gedit-app.h> + +void	gedit_osx_init (GeditApp *app); + +void 	gedit_osx_set_window_title 	(GeditWindow   *window,  +					 gchar const   *title, +					 GeditDocument *document); + +gboolean gedit_osx_show_url 		(const gchar *url); +gboolean gedit_osx_show_help		(const gchar *link_id); + +#endif /* __GEDIT_OSX_H__ */ diff --git a/gedit/smclient/Makefile.am b/gedit/smclient/Makefile.am new file mode 100755 index 00000000..8ec0ef71 --- /dev/null +++ b/gedit/smclient/Makefile.am @@ -0,0 +1,52 @@ +if OS_WIN32 +platform_sources = eggsmclient-win32.c +platform_logout_test_ldflags = -mwindows +else +if OS_OSX +platform_defines = -xobjective-c +platform_ldflags = -framework Carbon +platform_sources = eggsmclient-osx.c +else +platform_defines = -DEGG_SM_CLIENT_BACKEND_XSMP +platform_libs = libeggdesktopfile.la +platform_ltlibraries = libeggdesktopfile.la +platform_sources = eggsmclient-xsmp.c +endif +endif + +INCLUDES =                               \ +	-DG_LOG_DOMAIN=\""EggSMClient"\" \ +	$(GEDIT_CFLAGS)			 \ +	$(platform_defines)              \ +	$(EGG_SMCLIENT_CFLAGS) + +noinst_LTLIBRARIES =                     \ +	libeggsmclient.la                \ +	$(platform_ltlibraries) + +libeggsmclient_la_LIBADD =               \ +	$(EGG_SMCLIENT_LIBS)             \ +	$(platform_libs) + +libeggsmclient_la_LDFLAGS =              \ +	$(platform_ldflags) + +libeggsmclient_la_SOURCES =              \ +	eggsmclient.c                    \ +	eggsmclient.h                    \ +	eggsmclient-private.h            \ +	$(platform_sources) + +libeggdesktopfile_la_LIBADD =            \ +	$(EGG_LIBS) + +libeggdesktopfile_la_SOURCES =           \ +	eggdesktopfile.c                 \ +	eggdesktopfile.h + +EXTRA_DIST =                             \ +	eggsmclient-osx.c                \ +	eggsmclient-win32.c              \ +	eggsmclient-xsmp.c + +-include $(top_srcdir)/git.mk diff --git a/gedit/smclient/eggdesktopfile.c b/gedit/smclient/eggdesktopfile.c new file mode 100755 index 00000000..5ac79507 --- /dev/null +++ b/gedit/smclient/eggdesktopfile.c @@ -0,0 +1,1510 @@ +/* eggdesktopfile.c - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * Based on mate-desktop-item.c + * Copyright (C) 1999, 2000 Red Hat Inc. + * Copyright (C) 2001 George Lebl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - + * Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eggdesktopfile.h" + +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +struct EggDesktopFile { +  GKeyFile           *key_file; +  char               *source; + +  char               *name, *icon; +  EggDesktopFileType  type; +  char                document_code; +}; + +/** + * egg_desktop_file_new: + * @desktop_file_path: path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Creates a new #EggDesktopFile for @desktop_file. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new (const char *desktop_file_path, GError **error) +{ +  GKeyFile *key_file; + +  key_file = g_key_file_new (); +  if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error)) +    { +      g_key_file_free (key_file); +      return NULL; +    } + +  return egg_desktop_file_new_from_key_file (key_file, desktop_file_path, +					     error); +} + +/** + * egg_desktop_file_new_from_data_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path, +				     GError     **error) +{ +  EggDesktopFile *desktop_file; +  GKeyFile *key_file; +  char *full_path; + +  key_file = g_key_file_new (); +  if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path, +				       &full_path, 0, error)) +    { +      g_key_file_free (key_file); +      return NULL; +    } + +  desktop_file = egg_desktop_file_new_from_key_file (key_file, +						     full_path, +						     error); +  g_free (full_path); +  return desktop_file; +} + +/** + * egg_desktop_file_new_from_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @search_dirs: NULL-terminated array of directories to search + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_dirs (const char  *desktop_file_path, +				const char **search_dirs, +				GError     **error) +{ +  EggDesktopFile *desktop_file; +  GKeyFile *key_file; +  char *full_path; + +  key_file = g_key_file_new (); +  if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs, +				  &full_path, 0, error)) +    { +      g_key_file_free (key_file); +      return NULL; +    } + +  desktop_file = egg_desktop_file_new_from_key_file (key_file, +						     full_path, +						     error); +  g_free (full_path); +  return desktop_file; +} + +/** + * egg_desktop_file_new_from_key_file: + * @key_file: a #GKeyFile representing a desktop file + * @source: the path or URI that @key_file was loaded from, or %NULL + * @error: error pointer + * + * Creates a new #EggDesktopFile for @key_file. Assumes ownership of + * @key_file (on success or failure); you should consider @key_file to + * be freed after calling this function. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_key_file (GKeyFile    *key_file, +				    const char  *source, +				    GError     **error) +{ +  EggDesktopFile *desktop_file; +  char *version, *type; + +  if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP)) +    { +      g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		   EGG_DESKTOP_FILE_ERROR_INVALID, +		   _("File is not a valid .desktop file")); +      g_key_file_free (key_file); +      return NULL; +    } + +  version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP, +				  EGG_DESKTOP_FILE_KEY_VERSION, +				  NULL); +  if (version) +    { +      double version_num; +      char *end; + +      version_num = g_ascii_strtod (version, &end); +      if (*end) +	{ +	  g_warning ("Invalid Version string '%s' in %s", +		     version, source ? source : "(unknown)"); +	} +      else if (version_num > 1.0) +	{ +	  g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		       EGG_DESKTOP_FILE_ERROR_INVALID, +		       _("Unrecognized desktop file Version '%s'"), version); +	  g_free (version); +	  g_key_file_free (key_file); +	  return NULL; +	} +      g_free (version); +    } + +  desktop_file = g_new0 (EggDesktopFile, 1); +  desktop_file->key_file = key_file; + +  if (g_path_is_absolute (source)) +    desktop_file->source = g_filename_to_uri (source, NULL, NULL); +  else +    desktop_file->source = g_strdup (source); + +  desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, +					      EGG_DESKTOP_FILE_KEY_NAME, error); +  if (!desktop_file->name) +    { +      egg_desktop_file_free (desktop_file); +      return NULL; +    } + +  type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, +				EGG_DESKTOP_FILE_KEY_TYPE, error); +  if (!type) +    { +      egg_desktop_file_free (desktop_file); +      return NULL; +    } + +  if (!strcmp (type, "Application")) +    { +      char *exec, *p; + +      desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION; + +      exec = g_key_file_get_string (key_file, +				    EGG_DESKTOP_FILE_GROUP, +				    EGG_DESKTOP_FILE_KEY_EXEC, +				    error); +      if (!exec) +	{ +	  egg_desktop_file_free (desktop_file); +	  g_free (type); +	  return NULL; +	} + +      /* See if it takes paths or URIs or neither */ +      for (p = exec; *p; p++) +	{ +	  if (*p == '%') +	    { +	      if (p[1] == '\0' || strchr ("FfUu", p[1])) +		{ +		  desktop_file->document_code = p[1]; +		  break; +		} +	      p++; +	    } +	} + +      g_free (exec); +    } +  else if (!strcmp (type, "Link")) +    { +      char *url; + +      desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK; + +      url = g_key_file_get_string (key_file, +				   EGG_DESKTOP_FILE_GROUP, +				   EGG_DESKTOP_FILE_KEY_URL, +				   error); +      if (!url) +	{ +	  egg_desktop_file_free (desktop_file); +	  g_free (type); +	  return NULL; +	} +      g_free (url); +    } +  else if (!strcmp (type, "Directory")) +    desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY; +  else +    desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED; + +  g_free (type); + +  /* Check the Icon key */ +  desktop_file->icon = g_key_file_get_string (key_file, +					      EGG_DESKTOP_FILE_GROUP, +					      EGG_DESKTOP_FILE_KEY_ICON, +					      NULL); +  if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon)) +    { +      char *ext; + +      /* Lots of .desktop files still get this wrong */ +      ext = strrchr (desktop_file->icon, '.'); +      if (ext && (!strcmp (ext, ".png") || +		  !strcmp (ext, ".xpm") || +		  !strcmp (ext, ".svg"))) +	{ +	  g_warning ("Desktop file '%s' has malformed Icon key '%s'" +		     "(should not include extension)", +		     source ? source : "(unknown)", +		     desktop_file->icon); +	  *ext = '\0'; +	} +    } + +  return desktop_file; +} + +/** + * egg_desktop_file_free: + * @desktop_file: an #EggDesktopFile + * + * Frees @desktop_file. + **/ +void +egg_desktop_file_free (EggDesktopFile *desktop_file) +{ +  g_key_file_free (desktop_file->key_file); +  g_free (desktop_file->source); +  g_free (desktop_file->name); +  g_free (desktop_file->icon); +  g_free (desktop_file); +} + +/** + * egg_desktop_file_get_source: + * @desktop_file: an #EggDesktopFile + * + * Gets the URI that @desktop_file was loaded from. + * + * Return value: @desktop_file's source URI + **/ +const char * +egg_desktop_file_get_source (EggDesktopFile *desktop_file) +{ +  return desktop_file->source; +} + +/** + * egg_desktop_file_get_desktop_file_type: + * @desktop_file: an #EggDesktopFile + * + * Gets the desktop file type of @desktop_file. + * + * Return value: @desktop_file's type + **/ +EggDesktopFileType +egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file) +{ +  return desktop_file->type; +} + +/** + * egg_desktop_file_get_name: + * @desktop_file: an #EggDesktopFile + * + * Gets the (localized) value of @desktop_file's "Name" key. + * + * Return value: the application/link name + **/ +const char * +egg_desktop_file_get_name (EggDesktopFile *desktop_file) +{ +  return desktop_file->name; +} + +/** + * egg_desktop_file_get_icon: + * @desktop_file: an #EggDesktopFile + * + * Gets the value of @desktop_file's "Icon" key. + * + * If the icon string is a full path (that is, if g_path_is_absolute() + * returns %TRUE when called on it), it points to a file containing an + * unthemed icon. If the icon string is not a full path, it is the + * name of a themed icon, which can be looked up with %GtkIconTheme, + * or passed directly to a theme-aware widget like %GtkImage or + * %GtkCellRendererPixbuf. + * + * Return value: the icon path or name + **/ +const char * +egg_desktop_file_get_icon (EggDesktopFile *desktop_file) +{ +  return desktop_file->icon; +} + +gboolean +egg_desktop_file_has_key (EggDesktopFile  *desktop_file, +			  const char      *key, +			  GError         **error) +{ +  return g_key_file_has_key (desktop_file->key_file, +			     EGG_DESKTOP_FILE_GROUP, key, +			     error); +} + +char * +egg_desktop_file_get_string (EggDesktopFile  *desktop_file, +			     const char      *key, +			     GError         **error) +{ +  return g_key_file_get_string (desktop_file->key_file, +				EGG_DESKTOP_FILE_GROUP, key, +				error); +} + +char * +egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file, +				    const char      *key, +				    const char      *locale, +				    GError         **error) +{ +  return g_key_file_get_locale_string (desktop_file->key_file, +				       EGG_DESKTOP_FILE_GROUP, key, locale, +				       error); +} + +gboolean +egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file, +			      const char      *key, +			      GError         **error) +{ +  return g_key_file_get_boolean (desktop_file->key_file, +				 EGG_DESKTOP_FILE_GROUP, key, +				 error); +} + +double +egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file, +			      const char      *key, +			      GError         **error) +{ +  return g_key_file_get_double (desktop_file->key_file, +				EGG_DESKTOP_FILE_GROUP, key, +				error); +} + +char ** +egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file, +				  const char      *key, +				  gsize           *length, +				  GError         **error) +{ +  return g_key_file_get_string_list (desktop_file->key_file, +				     EGG_DESKTOP_FILE_GROUP, key, length, +				     error); +} + +char ** +egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file, +					 const char      *key, +					 const char      *locale, +					 gsize           *length, +					 GError         **error) +{ +  return g_key_file_get_locale_string_list (desktop_file->key_file, +					    EGG_DESKTOP_FILE_GROUP, key, +					    locale, length, +					    error); +} + +/** + * egg_desktop_file_can_launch: + * @desktop_file: an #EggDesktopFile + * @desktop_environment: the name of the running desktop environment, + * or %NULL + * + * Tests if @desktop_file can/should be launched in the current + * environment. If @desktop_environment is non-%NULL, @desktop_file's + * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that + * this desktop_file is appropriate for the named environment. + * + * Furthermore, if @desktop_file has type + * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is + * also checked, to make sure the binary it points to exists. + * + * egg_desktop_file_can_launch() does NOT check the value of the + * "Hidden" key. + * + * Return value: %TRUE if @desktop_file can be launched + **/ +gboolean +egg_desktop_file_can_launch (EggDesktopFile *desktop_file, +			     const char     *desktop_environment) +{ +  char *try_exec, *found_program; +  char **only_show_in, **not_show_in; +  gboolean found; +  int i; + +  if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION && +      desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK) +    return FALSE; + +  if (desktop_environment) +    { +      only_show_in = g_key_file_get_string_list (desktop_file->key_file, +						 EGG_DESKTOP_FILE_GROUP, +						 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN, +						 NULL, NULL); +      if (only_show_in) +	{ +	  for (i = 0, found = FALSE; only_show_in[i] && !found; i++) +	    { +	      if (!strcmp (only_show_in[i], desktop_environment)) +		found = TRUE; +	    } + +	  g_strfreev (only_show_in); + +	  if (!found) +	    return FALSE; +	} + +      not_show_in = g_key_file_get_string_list (desktop_file->key_file, +						EGG_DESKTOP_FILE_GROUP, +						EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN, +						NULL, NULL); +      if (not_show_in) +	{ +	  for (i = 0, found = FALSE; not_show_in[i] && !found; i++) +	    { +	      if (!strcmp (not_show_in[i], desktop_environment)) +		found = TRUE; +	    } + +	  g_strfreev (not_show_in); + +	  if (found) +	    return FALSE; +	} +    } + +  if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION) +    { +      try_exec = g_key_file_get_string (desktop_file->key_file, +					EGG_DESKTOP_FILE_GROUP, +					EGG_DESKTOP_FILE_KEY_TRY_EXEC, +					NULL); +      if (try_exec) +	{ +	  found_program = g_find_program_in_path (try_exec); +	  g_free (try_exec); + +	  if (!found_program) +	    return FALSE; +	  g_free (found_program); +	} +    } + +  return TRUE; +} + +/** + * egg_desktop_file_accepts_documents: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file represents an application that can accept + * documents on the command line. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file) +{ +  return desktop_file->document_code != 0; +} + +/** + * egg_desktop_file_accepts_multiple: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept multiple documents at once. + * + * If this returns %FALSE, you can still pass multiple documents to + * egg_desktop_file_launch(), but that will result in multiple copies + * of the application being launched. See egg_desktop_file_launch() + * for more details. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file) +{ +  return (desktop_file->document_code == 'F' || +	  desktop_file->document_code == 'U'); +} + +/** + * egg_desktop_file_accepts_uris: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept (non-"file:") URIs as documents to + * open. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file) +{ +  return (desktop_file->document_code == 'U' || +	  desktop_file->document_code == 'u'); +} + +static void +append_quoted_word (GString    *str, +		    const char *s, +		    gboolean    in_single_quotes, +		    gboolean    in_double_quotes) +{ +  const char *p; + +  if (!in_single_quotes && !in_double_quotes) +    g_string_append_c (str, '\''); +  else if (!in_single_quotes && in_double_quotes) +    g_string_append (str, "\"'"); + +  if (!strchr (s, '\'')) +    g_string_append (str, s); +  else +    { +      for (p = s; *p != '\0'; p++) +	{ +	  if (*p == '\'') +	    g_string_append (str, "'\\''"); +	  else +	    g_string_append_c (str, *p); +	} +    } + +  if (!in_single_quotes && !in_double_quotes) +    g_string_append_c (str, '\''); +  else if (!in_single_quotes && in_double_quotes) +    g_string_append (str, "'\""); +} + +static void +do_percent_subst (EggDesktopFile *desktop_file, +		  char            code, +		  GString        *str, +		  GSList        **documents, +		  gboolean        in_single_quotes, +		  gboolean        in_double_quotes) +{ +  GSList *d; +  char *doc; + +  switch (code) +    { +    case '%': +      g_string_append_c (str, '%'); +      break; + +    case 'F': +    case 'U': +      for (d = *documents; d; d = d->next) +	{ +	  doc = d->data; +	  g_string_append (str, " "); +	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes); +	} +      *documents = NULL; +      break; + +    case 'f': +    case 'u': +      if (*documents) +	{ +	  doc = (*documents)->data; +	  g_string_append (str, " "); +	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes); +	  *documents = (*documents)->next; +	} +      break; + +    case 'i': +      if (desktop_file->icon) +	{ +	  g_string_append (str, "--icon "); +	  append_quoted_word (str, desktop_file->icon, +			      in_single_quotes, in_double_quotes); +	} +      break; + +    case 'c': +      if (desktop_file->name) +	{ +	  append_quoted_word (str, desktop_file->name, +			      in_single_quotes, in_double_quotes); +	} +      break; + +    case 'k': +      if (desktop_file->source) +	{ +	  append_quoted_word (str, desktop_file->source, +			      in_single_quotes, in_double_quotes); +	} +      break; + +    case 'D': +    case 'N': +    case 'd': +    case 'n': +    case 'v': +    case 'm': +      /* Deprecated; skip */ +      break; + +    default: +      g_warning ("Unrecognized %%-code '%%%c' in Exec", code); +      break; +    } +} + +static char * +parse_exec (EggDesktopFile  *desktop_file, +	    GSList         **documents, +	    GError         **error) +{ +  char *exec, *p, *command; +  gboolean escape, single_quot, double_quot; +  GString *gs; + +  exec = g_key_file_get_string (desktop_file->key_file, +				EGG_DESKTOP_FILE_GROUP, +				EGG_DESKTOP_FILE_KEY_EXEC, +				error); +  if (!exec) +    return NULL; + +  /* Build the command */ +  gs = g_string_new (NULL); +  escape = single_quot = double_quot = FALSE; + +  for (p = exec; *p != '\0'; p++) +    { +      if (escape) +	{ +	  escape = FALSE; +	  g_string_append_c (gs, *p); +	} +      else if (*p == '\\') +	{ +	  if (!single_quot) +	    escape = TRUE; +	  g_string_append_c (gs, *p); +	} +      else if (*p == '\'') +	{ +	  g_string_append_c (gs, *p); +	  if (!single_quot && !double_quot) +	    single_quot = TRUE; +	  else if (single_quot) +	    single_quot = FALSE; +	} +      else if (*p == '"') +	{ +	  g_string_append_c (gs, *p); +	  if (!single_quot && !double_quot) +	    double_quot = TRUE; +	  else if (double_quot) +	    double_quot = FALSE; +	} +      else if (*p == '%' && p[1]) +	{ +	  do_percent_subst (desktop_file, p[1], gs, documents, +			    single_quot, double_quot); +	  p++; +	} +      else +	g_string_append_c (gs, *p); +    } + +  g_free (exec); +  command = g_string_free (gs, FALSE); + +  /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */ +  if (g_key_file_has_key (desktop_file->key_file, +			  EGG_DESKTOP_FILE_GROUP, +			  EGG_DESKTOP_FILE_KEY_TERMINAL, +			  NULL)) +    { +      GError *terminal_error = NULL; +      gboolean use_terminal = +	g_key_file_get_boolean (desktop_file->key_file, +				EGG_DESKTOP_FILE_GROUP, +				EGG_DESKTOP_FILE_KEY_TERMINAL, +				&terminal_error); +      if (terminal_error) +	{ +	  g_free (command); +	  g_propagate_error (error, terminal_error); +	  return NULL; +	} + +      if (use_terminal) +	{ +	  gs = g_string_new ("xdg-terminal "); +	  append_quoted_word (gs, command, FALSE, FALSE); +	  g_free (command); +	  command = g_string_free (gs, FALSE); +	} +    } + +  return command; +} + +static GSList * +translate_document_list (EggDesktopFile *desktop_file, GSList *documents) +{ +  gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file); +  GSList *ret, *d; + +  for (d = documents, ret = NULL; d; d = d->next) +    { +      const char *document = d->data; +      gboolean is_uri = !g_path_is_absolute (document); +      char *translated; + +      if (accepts_uris) +	{ +	  if (is_uri) +	    translated = g_strdup (document); +	  else +	    translated = g_filename_to_uri (document, NULL, NULL); +	} +      else +	{ +	  if (is_uri) +	    translated = g_filename_from_uri (document, NULL, NULL); +	  else +	    translated = g_strdup (document); +	} + +      if (translated) +	ret = g_slist_prepend (ret, translated); +    } + +  return g_slist_reverse (ret); +} + +static void +free_document_list (GSList *documents) +{ +  GSList *d; + +  for (d = documents; d; d = d->next) +    g_free (d->data); +  g_slist_free (documents); +} + +/** + * egg_desktop_file_parse_exec: + * @desktop_file: a #EggDesktopFile + * @documents: a list of document paths or URIs + * @error: error pointer + * + * Parses @desktop_file's Exec key, inserting @documents into it, and + * returns the result. + * + * If @documents contains non-file: URIs and @desktop_file does not + * accept URIs, those URIs will be ignored. Likewise, if @documents + * contains more elements than @desktop_file accepts, the extra + * documents will be ignored. + * + * Return value: the parsed Exec string + **/ +char * +egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file, +			     GSList          *documents, +			     GError         **error) +{ +  GSList *translated, *docs; +  char *command; + +  docs = translated = translate_document_list (desktop_file, documents); +  command = parse_exec (desktop_file, &docs, error); +  free_document_list (translated); + +  return command; +} + +static gboolean +parse_link (EggDesktopFile  *desktop_file, +	    EggDesktopFile **app_desktop_file, +	    GSList         **documents, +	    GError         **error) +{ +  char *url; +  GKeyFile *key_file; + +  url = g_key_file_get_string (desktop_file->key_file, +			       EGG_DESKTOP_FILE_GROUP, +			       EGG_DESKTOP_FILE_KEY_URL, +			       error); +  if (!url) +    return FALSE; +  *documents = g_slist_prepend (NULL, url); + +  /* FIXME: use gvfs */ +  key_file = g_key_file_new (); +  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +			 EGG_DESKTOP_FILE_KEY_NAME, +			 "xdg-open"); +  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +			 EGG_DESKTOP_FILE_KEY_TYPE, +			 "Application"); +  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, +			 EGG_DESKTOP_FILE_KEY_EXEC, +			 "xdg-open %u"); +  *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL); +  return TRUE; +} + +#if GTK_CHECK_VERSION (2, 12, 0) +static char * +start_startup_notification (GdkDisplay     *display, +			    EggDesktopFile *desktop_file, +			    const char     *argv0, +			    int             screen, +			    int             workspace, +			    guint32         launch_time) +{ +  static int sequence = 0; +  char *startup_id; +  char *description, *wmclass; +  char *screen_str, *workspace_str; + +  if (g_key_file_has_key (desktop_file->key_file, +			  EGG_DESKTOP_FILE_GROUP, +			  EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, +			  NULL)) +    { +      if (!g_key_file_get_boolean (desktop_file->key_file, +				   EGG_DESKTOP_FILE_GROUP, +				   EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, +				   NULL)) +	return NULL; +      wmclass = NULL; +    } +  else +    { +      wmclass = g_key_file_get_string (desktop_file->key_file, +				       EGG_DESKTOP_FILE_GROUP, +				       EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS, +				       NULL); +      if (!wmclass) +	return NULL; +    } + +  if (launch_time == (guint32)-1) +    launch_time = gdk_x11_display_get_user_time (display); +  startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu", +				g_get_prgname (), +				(unsigned long)getpid (), +				g_get_host_name (), +				argv0, +				sequence++, +				(unsigned long)launch_time); + +  description = g_strdup_printf (_("Starting %s"), desktop_file->name); +  screen_str = g_strdup_printf ("%d", screen); +  workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace); + +  gdk_x11_display_broadcast_startup_message (display, "new", +					     "ID", startup_id, +					     "NAME", desktop_file->name, +					     "SCREEN", screen_str, +					     "BIN", argv0, +					     "ICON", desktop_file->icon, +					     "DESKTOP", workspace_str, +					     "DESCRIPTION", description, +					     "WMCLASS", wmclass, +					     NULL); + +  g_free (description); +  g_free (wmclass); +  g_free (screen_str); +  g_free (workspace_str); + +  return startup_id; +} + +static void +end_startup_notification (GdkDisplay *display, +			  const char *startup_id) +{ +  gdk_x11_display_broadcast_startup_message (display, "remove", +					     "ID", startup_id, +					     NULL); +} + +#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */) + +typedef struct { +  GdkDisplay *display; +  char *startup_id; +} StartupNotificationData; + +static gboolean +startup_notification_timeout (gpointer data) +{ +  StartupNotificationData *sn_data = data; + +  end_startup_notification (sn_data->display, sn_data->startup_id); +  g_object_unref (sn_data->display); +  g_free (sn_data->startup_id); +  g_free (sn_data); + +  return FALSE; +} + +static void +set_startup_notification_timeout (GdkDisplay *display, +				  const char *startup_id) +{ +  StartupNotificationData *sn_data; + +  sn_data = g_new (StartupNotificationData, 1); +  sn_data->display = g_object_ref (display); +  sn_data->startup_id = g_strdup (startup_id); + +  g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, +			 startup_notification_timeout, sn_data); +} +#endif /* GTK 2.12 */ + +static GPtrArray * +array_putenv (GPtrArray *env, char *variable) +{ +  guint i, keylen; + +  if (!env) +    { +      char **envp; + +      env = g_ptr_array_new (); + +      envp = g_listenv (); +      for (i = 0; envp[i]; i++) +        { +          const char *value; + +          value = g_getenv (envp[i]); +          g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i], +                                                 value ? value : "")); +        } +      g_strfreev (envp); +    } + +  keylen = strcspn (variable, "="); + +  /* Remove old value of key */ +  for (i = 0; i < env->len; i++) +    { +      char *envvar = env->pdata[i]; + +      if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=') +	{ +	  g_free (envvar); +	  g_ptr_array_remove_index_fast (env, i); +	  break; +	} +    } + +  /* Add new value */ +  g_ptr_array_add (env, g_strdup (variable)); + +  return env; +} + +static gboolean +egg_desktop_file_launchv (EggDesktopFile *desktop_file, +			  GSList *documents, va_list args, +			  GError **error) +{ +  EggDesktopFileLaunchOption option; +  GSList *translated_documents = NULL, *docs = NULL; +  char *command, **argv; +  int argc, i, screen_num; +  gboolean success, current_success; +  GdkDisplay *display; +  char *startup_id; + +  GPtrArray   *env = NULL; +  char       **variables = NULL; +  GdkScreen   *screen = NULL; +  int          workspace = -1; +  const char  *directory = NULL; +  guint32      launch_time = (guint32)-1; +  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH; +  GSpawnChildSetupFunc setup_func = NULL; +  gpointer     setup_data = NULL; + +  GPid        *ret_pid = NULL; +  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL; +  char       **ret_startup_id = NULL; + +  if (documents && desktop_file->document_code == 0) +    { +      g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +		   _("Application does not accept documents on command line")); +      return FALSE; +    } + +  /* Read the options: technically it's incorrect for the caller to +   * NULL-terminate the list of options (rather than 0-terminating +   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED, +   * it's more consistent with other glib/gtk methods, and it will +   * work as long as sizeof (int) <= sizeof (NULL), and NULL is +   * represented as 0. (Which is true everywhere we care about.) +   */ +  while ((option = va_arg (args, EggDesktopFileLaunchOption))) +    { +      switch (option) +	{ +	case EGG_DESKTOP_FILE_LAUNCH_CLEARENV: +	  if (env) +	    g_ptr_array_free (env, TRUE); +	  env = g_ptr_array_new (); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_PUTENV: +	  variables = va_arg (args, char **); +	  for (i = 0; variables[i]; i++) +	    env = array_putenv (env, variables[i]); +	  break; + +	case EGG_DESKTOP_FILE_LAUNCH_SCREEN: +	  screen = va_arg (args, GdkScreen *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: +	  workspace = va_arg (args, int); +	  break; + +	case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: +	  directory = va_arg (args, const char *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_TIME: +	  launch_time = va_arg (args, guint32); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_FLAGS: +	  flags |= va_arg (args, GSpawnFlags); +	  /* Make sure they didn't set any flags that don't make sense. */ +	  flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO; +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC: +	  setup_func = va_arg (args, GSpawnChildSetupFunc); +	  setup_data = va_arg (args, gpointer); +	  break; + +	case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID: +	  ret_pid = va_arg (args, GPid *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE: +	  ret_stdin = va_arg (args, int *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE: +	  ret_stdout = va_arg (args, int *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE: +	  ret_stderr = va_arg (args, int *); +	  break; +	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID: +	  ret_startup_id = va_arg (args, char **); +	  break; + +	default: +	  g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION, +		       _("Unrecognized launch option: %d"), +		       GPOINTER_TO_INT (option)); +	  success = FALSE; +	  goto out; +	} +    } + +  if (screen) +    { +      char *display_name = gdk_screen_make_display_name (screen); +      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name); +      env = array_putenv (env, display_env); +      g_free (display_name); +      g_free (display_env); + +      display = gdk_screen_get_display (screen); +    } +  else +    { +      display = gdk_display_get_default (); +      screen = gdk_display_get_default_screen (display); +    } +  screen_num = gdk_screen_get_number (screen); + +  translated_documents = translate_document_list (desktop_file, documents); +  docs = translated_documents; + +  success = FALSE; + +  do +    { +      command = parse_exec (desktop_file, &docs, error); +      if (!command) +	goto out; + +      if (!g_shell_parse_argv (command, &argc, &argv, error)) +	{ +	  g_free (command); +	  goto out; +	} +      g_free (command); + +#if GTK_CHECK_VERSION (2, 12, 0) +      startup_id = start_startup_notification (display, desktop_file, +					       argv[0], screen_num, +					       workspace, launch_time); +      if (startup_id) +	{ +	  char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s", +						  startup_id); +	  env = array_putenv (env, startup_id_env); +	  g_free (startup_id_env); +	} +#else +      startup_id = NULL; +#endif /* GTK 2.12 */ + +      if (env != NULL) +	g_ptr_array_add (env, NULL); + +      current_success = +	g_spawn_async_with_pipes (directory, +				  argv, +				  env ? (char **)(env->pdata) : NULL, +				  flags, +				  setup_func, setup_data, +				  ret_pid, +				  ret_stdin, ret_stdout, ret_stderr, +				  error); +      g_strfreev (argv); + +      if (startup_id) +	{ +#if GTK_CHECK_VERSION (2, 12, 0) +	  if (current_success) +	    { +	      set_startup_notification_timeout (display, startup_id); + +	      if (ret_startup_id) +		*ret_startup_id = startup_id; +	      else +		g_free (startup_id); +	    } +	  else +#endif /* GTK 2.12 */ +	    g_free (startup_id); +	} +      else if (ret_startup_id) +	*ret_startup_id = NULL; + +      if (current_success) +	{ +	  /* If we successfully launch any instances of the app, make +	   * sure we return TRUE and don't set @error. +	   */ +	  success = TRUE; +	  error = NULL; + +	  /* Also, only set the output params on the first one */ +	  ret_pid = NULL; +	  ret_stdin = ret_stdout = ret_stderr = NULL; +	  ret_startup_id = NULL; +	} +    } +  while (docs && current_success); + + out: +  if (env) +    { +      g_ptr_array_foreach (env, (GFunc)g_free, NULL); +      g_ptr_array_free (env, TRUE); +    } +  free_document_list (translated_documents); + +  return success; +} + +/** + * egg_desktop_file_launch: + * @desktop_file: an #EggDesktopFile + * @documents: a list of URIs or paths to documents to open + * @error: error pointer + * @...: additional options + * + * Launches @desktop_file with the given arguments. Additional options + * can be specified as follows: + * + *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments) + *       clears the environment in the child process + *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables) + *       adds the NAME=VALUE strings in the given %NULL-terminated + *       array to the child process's environment + *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen) + *       causes the application to be launched on the given screen + *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace) + *       causes the application to be launched on the given workspace + *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir) + *       causes the application to be launched in the given directory + *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time) + *       sets the "launch time" for the application. If the user + *       interacts with another window after @launch_time but before + *       the launched application creates its first window, the window + *       manager may choose to not give focus to the new application. + *       Passing 0 for @launch_time will explicitly request that the + *       application not receive focus. + *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags) + *       Sets additional #GSpawnFlags to use. See g_spawn_async() for + *       more details. + *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer) + *       Sets the child setup callback and the data to pass to it. + *       (See g_spawn_async() for more details.) + * + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid) + *       On a successful launch, sets *@pid to the PID of the launched + *       application. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id) + *       On a successful launch, sets *@startup_id to the Startup + *       Notification "startup id" of the launched application. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stdin. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stdout. + *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd) + *       On a successful launch, sets *@fd to the file descriptor of + *       a pipe connected to the application's stderr. + * + * The options should be terminated with a single %NULL. + * + * If @documents contains multiple documents, but + * egg_desktop_file_accepts_multiple() returns %FALSE for + * @desktop_file, then egg_desktop_file_launch() will actually launch + * multiple instances of the application. In that case, the return + * value (as well as any values passed via + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the + * first instance of the application that was launched (but the + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each + * instance). + * + * Return value: %TRUE if the application was successfully launched. + **/ +gboolean +egg_desktop_file_launch (EggDesktopFile *desktop_file, +			 GSList *documents, GError **error, +			 ...) +{ +  va_list args; +  gboolean success; +  EggDesktopFile *app_desktop_file; + +  switch (desktop_file->type) +    { +    case EGG_DESKTOP_FILE_TYPE_APPLICATION: +      va_start (args, error); +      success = egg_desktop_file_launchv (desktop_file, documents, +					  args, error); +      va_end (args); +      break; + +    case EGG_DESKTOP_FILE_TYPE_LINK: +      if (documents) +	{ +	  g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +		       _("Can't pass document URIs to a 'Type=Link' desktop entry")); +	  return FALSE; +	}	   + +      if (!parse_link (desktop_file, &app_desktop_file, &documents, error)) +	return FALSE; + +      va_start (args, error); +      success = egg_desktop_file_launchv (app_desktop_file, documents, +					  args, error); +      va_end (args); + +      egg_desktop_file_free (app_desktop_file); +      free_document_list (documents); +      break; + +    case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED: +    case EGG_DESKTOP_FILE_TYPE_DIRECTORY: +    default: +      g_set_error (error, EGG_DESKTOP_FILE_ERROR, +		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +		   _("Not a launchable item")); +      success = FALSE; +      break; +    } + +  return success; +} + + +GQuark +egg_desktop_file_error_quark (void) +{ +  return g_quark_from_static_string ("egg-desktop_file-error-quark"); +} + + +G_LOCK_DEFINE_STATIC (egg_desktop_file); +static EggDesktopFile *egg_desktop_file; + +static void +egg_set_desktop_file_internal (const char *desktop_file_path, +                               gboolean set_defaults) +{ +  GError *error = NULL; + +  G_LOCK (egg_desktop_file); +  if (egg_desktop_file) +    egg_desktop_file_free (egg_desktop_file); + +  egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); +  if (error) +    { +      g_warning ("Could not load desktop file '%s': %s", +		 desktop_file_path, error->message); +      g_error_free (error); +    } + +  if (set_defaults && egg_desktop_file != NULL) { +    /* Set localized application name and default window icon */ +    if (egg_desktop_file->name) +      g_set_application_name (egg_desktop_file->name); +    if (egg_desktop_file->icon) +      { +        if (g_path_is_absolute (egg_desktop_file->icon)) +          gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); +        else +          gtk_window_set_default_icon_name (egg_desktop_file->icon); +      } +  } + +  G_UNLOCK (egg_desktop_file); +} + +/** + * egg_set_desktop_file: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. This will also call g_set_application_name() + * with the localized application name from the desktop file, and + * gtk_window_set_default_icon_name() or + * gtk_window_set_default_icon_from_file() with the application's + * icon. Other code may use additional information from the desktop + * file. + * See egg_set_desktop_file_without_defaults() for a variant of this + * function that does not set the application name and default window + * icon. + * + * Note that for thread safety reasons, this function can only + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file_without_defaults(). + **/ +void +egg_set_desktop_file (const char *desktop_file_path) +{ +  egg_set_desktop_file_internal (desktop_file_path, TRUE); +} + +/** + * egg_set_desktop_file_without_defaults: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. + * See egg_set_desktop_file() for a variant of this function that + * sets the application name and default window icon from the information + * in the desktop file. + * + * Note that for thread safety reasons, this function can only + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file(). + **/ +void +egg_set_desktop_file_without_defaults (const char *desktop_file_path) +{ +  egg_set_desktop_file_internal (desktop_file_path, FALSE); +} + +/** + * egg_get_desktop_file: + *  + * Gets the application's #EggDesktopFile, as set by + * egg_set_desktop_file(). + *  + * Return value: the #EggDesktopFile, or %NULL if it hasn't been set. + **/ +EggDesktopFile * +egg_get_desktop_file (void) +{ +  EggDesktopFile *retval; + +  G_LOCK (egg_desktop_file); +  retval = egg_desktop_file; +  G_UNLOCK (egg_desktop_file); + +  return retval; +} diff --git a/gedit/smclient/eggdesktopfile.h b/gedit/smclient/eggdesktopfile.h new file mode 100755 index 00000000..18fe4631 --- /dev/null +++ b/gedit/smclient/eggdesktopfile.h @@ -0,0 +1,160 @@ +/* eggdesktopfile.h - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - + * Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_DESKTOP_FILE_H__ +#define __EGG_DESKTOP_FILE_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct EggDesktopFile EggDesktopFile; + +typedef enum { +	EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED, + +	EGG_DESKTOP_FILE_TYPE_APPLICATION, +	EGG_DESKTOP_FILE_TYPE_LINK, +	EGG_DESKTOP_FILE_TYPE_DIRECTORY +} EggDesktopFileType; + +EggDesktopFile     *egg_desktop_file_new                (const char   *desktop_file_path, +							 GError      **error); + +EggDesktopFile     *egg_desktop_file_new_from_data_dirs (const char   *desktop_file_path, +							 GError      **error); +EggDesktopFile     *egg_desktop_file_new_from_dirs      (const char   *desktop_file_path, +							 const char  **search_dirs, +							 GError      **error); +EggDesktopFile     *egg_desktop_file_new_from_key_file  (GKeyFile     *key_file, +							 const char   *source, +							 GError      **error); + +void                egg_desktop_file_free               (EggDesktopFile  *desktop_file); + +const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file); + +EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file); + +const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file); +const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file); + +gboolean            egg_desktop_file_can_launch         (EggDesktopFile  *desktop_file, +							 const char      *desktop_environment); + +gboolean            egg_desktop_file_accepts_documents  (EggDesktopFile  *desktop_file); +gboolean            egg_desktop_file_accepts_multiple   (EggDesktopFile  *desktop_file); +gboolean            egg_desktop_file_accepts_uris       (EggDesktopFile  *desktop_file); + +char               *egg_desktop_file_parse_exec         (EggDesktopFile  *desktop_file, +							 GSList          *documents, +							 GError         **error); + +gboolean            egg_desktop_file_launch             (EggDesktopFile  *desktop_file, +							 GSList          *documents, +							 GError         **error, +							 ...) G_GNUC_NULL_TERMINATED; + +typedef enum { +	EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1, +	EGG_DESKTOP_FILE_LAUNCH_PUTENV, +	EGG_DESKTOP_FILE_LAUNCH_SCREEN, +	EGG_DESKTOP_FILE_LAUNCH_WORKSPACE, +	EGG_DESKTOP_FILE_LAUNCH_DIRECTORY, +	EGG_DESKTOP_FILE_LAUNCH_TIME, +	EGG_DESKTOP_FILE_LAUNCH_FLAGS, +	EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC, +	EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, +	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE, +	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE, +	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE, +	EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID +} EggDesktopFileLaunchOption; + +/* Standard Keys */ +#define EGG_DESKTOP_FILE_GROUP			"Desktop Entry" + +#define EGG_DESKTOP_FILE_KEY_TYPE		"Type" +#define EGG_DESKTOP_FILE_KEY_VERSION		"Version" +#define EGG_DESKTOP_FILE_KEY_NAME		"Name" +#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME	"GenericName" +#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY		"NoDisplay" +#define EGG_DESKTOP_FILE_KEY_COMMENT		"Comment" +#define EGG_DESKTOP_FILE_KEY_ICON		"Icon" +#define EGG_DESKTOP_FILE_KEY_HIDDEN		"Hidden" +#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN	"OnlyShowIn" +#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN	"NotShowIn" +#define EGG_DESKTOP_FILE_KEY_TRY_EXEC		"TryExec" +#define EGG_DESKTOP_FILE_KEY_EXEC		"Exec" +#define EGG_DESKTOP_FILE_KEY_PATH		"Path" +#define EGG_DESKTOP_FILE_KEY_TERMINAL		"Terminal" +#define EGG_DESKTOP_FILE_KEY_MIME_TYPE		"MimeType" +#define EGG_DESKTOP_FILE_KEY_CATEGORIES		"Categories" +#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY	"StartupNotify" +#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS	"StartupWMClass" +#define EGG_DESKTOP_FILE_KEY_URL		"URL" + +/* Accessors */ +gboolean  egg_desktop_file_has_key                (EggDesktopFile  *desktop_file, +						   const char      *key, +						   GError         **error); +char     *egg_desktop_file_get_string             (EggDesktopFile  *desktop_file, +						   const char      *key, +						   GError         **error) G_GNUC_MALLOC; +char     *egg_desktop_file_get_locale_string      (EggDesktopFile  *desktop_file, +						   const char      *key, +						   const char      *locale, +						   GError         **error) G_GNUC_MALLOC; +gboolean  egg_desktop_file_get_boolean            (EggDesktopFile  *desktop_file, +						   const char      *key, +						   GError         **error); +double    egg_desktop_file_get_numeric            (EggDesktopFile  *desktop_file, +						   const char      *key, +						   GError         **error); +char    **egg_desktop_file_get_string_list        (EggDesktopFile  *desktop_file, +						   const char      *key, +						   gsize           *length, +						   GError         **error) G_GNUC_MALLOC; +char    **egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file, +						   const char      *key, +						   const char      *locale, +						   gsize           *length, +						   GError         **error) G_GNUC_MALLOC; + + +/* Errors */ +#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark() + +GQuark egg_desktop_file_error_quark (void); + +typedef enum { +	EGG_DESKTOP_FILE_ERROR_INVALID, +	EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, +	EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION +} EggDesktopFileError; + +/* Global application desktop file */ +void            egg_set_desktop_file                  (const char *desktop_file_path); +void            egg_set_desktop_file_without_defaults (const char *desktop_file_path); +EggDesktopFile *egg_get_desktop_file                  (void); + + +G_END_DECLS + +#endif /* __EGG_DESKTOP_FILE_H__ */ diff --git a/gedit/smclient/eggsmclient-osx.c b/gedit/smclient/eggsmclient-osx.c new file mode 100755 index 00000000..7d3ff4b6 --- /dev/null +++ b/gedit/smclient/eggsmclient-osx.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* EggSMClientOSX + * + * For details on the OS X logout process, see: + * http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618 + * + * EggSMClientOSX registers for the kAEQuitApplication AppleEvent; the + * handler we register (quit_requested()) will be invoked from inside + * the quartz event-handling code (specifically, from inside + * [NSApplication nextEventMatchingMask]) when an AppleEvent arrives. + * We use AESuspendTheCurrentEvent() and AEResumeTheCurrentEvent() to + * allow asynchronous / non-main-loop-reentering processing of the + * quit request. (These are part of the Carbon framework; it doesn't + * seem to be possible to handle AppleEvents asynchronously from + * Cocoa.) + */ + +#include "config.h" + +#include "eggsmclient-private.h" +#include <glib.h> +#include <Carbon/Carbon.h> +#include <CoreServices/CoreServices.h> + +#define EGG_TYPE_SM_CLIENT_OSX            (egg_sm_client_osx_get_type ()) +#define EGG_SM_CLIENT_OSX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX)) +#define EGG_SM_CLIENT_OSX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) +#define EGG_IS_SM_CLIENT_OSX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX)) +#define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX)) +#define EGG_SM_CLIENT_OSX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) + +typedef struct _EggSMClientOSX        EggSMClientOSX; +typedef struct _EggSMClientOSXClass   EggSMClientOSXClass; + +struct _EggSMClientOSX { +  EggSMClient parent; + +  AppleEvent quit_event, quit_reply; +  gboolean quit_requested, quitting; +}; + +struct _EggSMClientOSXClass +{ +  EggSMClientClass parent_class; + +}; + +static void     sm_client_osx_startup (EggSMClient *client, +				       const char  *client_id); +static void     sm_client_osx_will_quit (EggSMClient *client, +					 gboolean     will_quit); +static gboolean sm_client_osx_end_session (EggSMClient         *client, +					   EggSMClientEndStyle  style, +					   gboolean  request_confirmation); + +static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long); + +G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_osx_init (EggSMClientOSX *osx) +{ +  ; +} + +static void +egg_sm_client_osx_class_init (EggSMClientOSXClass *klass) +{ +  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + +  sm_client_class->startup             = sm_client_osx_startup; +  sm_client_class->will_quit           = sm_client_osx_will_quit; +  sm_client_class->end_session         = sm_client_osx_end_session; +} + +EggSMClient * +egg_sm_client_osx_new (void) +{ +  return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL); +} + +static void +sm_client_osx_startup (EggSMClient *client, +		       const char  *client_id) +{ +  AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, +			 NewAEEventHandlerUPP (quit_requested), +			 (long)GPOINTER_TO_SIZE (client), false); +} + +static gboolean +idle_quit_requested (gpointer client) +{ +  egg_sm_client_quit_requested (client); +  return FALSE; +} + +static pascal OSErr +quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon) +{ +  EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon); +  EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon); + +  g_return_val_if_fail (!osx->quit_requested, userCanceledErr); +     +  /* FIXME AEInteractWithUser? */ + +  osx->quit_requested = TRUE; +  AEDuplicateDesc (aevt, &osx->quit_event); +  AEDuplicateDesc (reply, &osx->quit_reply); +  AESuspendTheCurrentEvent (aevt); + +  /* Don't emit the "quit_requested" signal immediately, since we're +   * called from a weird point in the guts of gdkeventloop-quartz.c +   */ +  g_idle_add (idle_quit_requested, client); +  return noErr; +} + +static pascal OSErr +quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon) +{ +  EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon); + +  osx->quit_requested = FALSE; +  return osx->quitting ? noErr : userCanceledErr; +} + +static gboolean +idle_will_quit (gpointer client) +{ +  EggSMClientOSX *osx = (EggSMClientOSX *)client; + +  /* Resume the event with a new handler that will return a value to +   * the system. +   */ +  AEResumeTheCurrentEvent (&osx->quit_event, &osx->quit_reply, +			   NewAEEventHandlerUPP (quit_requested_resumed), +			   (long)GPOINTER_TO_SIZE (client)); +  AEDisposeDesc (&osx->quit_event); +  AEDisposeDesc (&osx->quit_reply); + +  if (osx->quitting) +    egg_sm_client_quit (client); +  return FALSE; +} + +static void +sm_client_osx_will_quit (EggSMClient *client, +			 gboolean     will_quit) +{ +  EggSMClientOSX *osx = (EggSMClientOSX *)client; + +  g_return_if_fail (osx->quit_requested); + +  osx->quitting = will_quit; + +  /* Finish in an idle handler since the caller might have called +   * egg_sm_client_will_quit() from inside the "quit_requested" signal +   * handler, but may not expect the "quit" signal to arrive during +   * the _will_quit() call. +   */ +  g_idle_add (idle_will_quit, client); +} + +static gboolean +sm_client_osx_end_session (EggSMClient         *client, +			   EggSMClientEndStyle  style, +			   gboolean             request_confirmation) +{ +  static const ProcessSerialNumber loginwindow_psn = { 0, kSystemProcess }; +  AppleEvent event = { typeNull, NULL }, reply = { typeNull, NULL }; +  AEAddressDesc target; +  AEEventID id; +  OSErr err; + +  switch (style) +    { +    case EGG_SM_CLIENT_END_SESSION_DEFAULT: +    case EGG_SM_CLIENT_LOGOUT: +      id = request_confirmation ? kAELogOut : kAEReallyLogOut; +      break; +    case EGG_SM_CLIENT_REBOOT: +      id = request_confirmation ? kAEShowRestartDialog : kAERestart; +      break; +    case EGG_SM_CLIENT_SHUTDOWN: +      id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown; +      break; +    } + +  err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn,  +		      sizeof (loginwindow_psn), &target); +  if (err != noErr) +    { +      g_warning ("Could not create descriptor for loginwindow: %d", err); +      return FALSE; +    } + +  err = AECreateAppleEvent (kCoreEventClass, id, &target, +			    kAutoGenerateReturnID, kAnyTransactionID, +			    &event); +  AEDisposeDesc (&target); +  if (err != noErr) +    { +      g_warning ("Could not create logout AppleEvent: %d", err); +      return FALSE; +    } + +  err = AESend (&event, &reply, kAENoReply, kAENormalPriority, +		kAEDefaultTimeout, NULL, NULL); +  AEDisposeDesc (&event); +  if (err == noErr) +    AEDisposeDesc (&reply); + +  return err == noErr; +} diff --git a/gedit/smclient/eggsmclient-private.h b/gedit/smclient/eggsmclient-private.h new file mode 100755 index 00000000..ccb10bfc --- /dev/null +++ b/gedit/smclient/eggsmclient-private.h @@ -0,0 +1,53 @@ +/* eggsmclient-private.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_PRIVATE_H__ +#define __EGG_SM_CLIENT_PRIVATE_H__ + +#include <gdkconfig.h> +#include "eggsmclient.h" + +G_BEGIN_DECLS + +GKeyFile *egg_sm_client_save_state     (EggSMClient *client); +void      egg_sm_client_quit_requested (EggSMClient *client); +void      egg_sm_client_quit_cancelled (EggSMClient *client); +void      egg_sm_client_quit           (EggSMClient *client); + +#if defined (GDK_WINDOWING_X11) +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +GType        egg_sm_client_xsmp_get_type (void); +EggSMClient *egg_sm_client_xsmp_new      (void); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +GType        egg_sm_client_dbus_get_type (void); +EggSMClient *egg_sm_client_dbus_new      (void); +# endif +#elif defined (GDK_WINDOWING_WIN32) +GType        egg_sm_client_win32_get_type (void); +EggSMClient *egg_sm_client_win32_new      (void); +#elif defined (GDK_WINDOWING_QUARTZ) +GType        egg_sm_client_osx_get_type (void); +EggSMClient *egg_sm_client_osx_new      (void); +#endif + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */ diff --git a/gedit/smclient/eggsmclient-win32.c b/gedit/smclient/eggsmclient-win32.c new file mode 100755 index 00000000..91a25715 --- /dev/null +++ b/gedit/smclient/eggsmclient-win32.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* EggSMClientWin32 + * + * For details on the Windows XP logout process, see: + * http://msdn.microsoft.com/en-us/library/aa376876.aspx. + * + * Vista adds some new APIs which EggSMClient does not make use of; see + * http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx + * + * When shutting down, Windows sends every top-level window a + * WM_QUERYENDSESSION event, which the application must respond to + * synchronously, saying whether or not it will quit. To avoid main + * loop re-entrancy problems (and to avoid having to muck about too + * much with the guts of the gdk-win32 main loop), we watch for this + * event in a separate thread, which then signals the main thread and + * waits for the main thread to handle the event. Since we don't want + * to require g_thread_init() to be called, we do this all using + * Windows-specific thread methods. + * + * After the application handles the WM_QUERYENDSESSION event, + * Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE + * parameter indicating whether the session is or is not actually + * going to end now. We handle this from the other thread as well. + * + * As mentioned above, Vista introduces several additional new APIs + * that don't fit into the (current) EggSMClient API. Windows also has + * an entirely separate shutdown-notification scheme for non-GUI apps, + * which we also don't handle here. + */ + +#include "config.h" + +#include "eggsmclient-private.h" +#include <gdk/gdk.h> + +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#include <windows.h> +#include <process.h> + +#define EGG_TYPE_SM_CLIENT_WIN32            (egg_sm_client_win32_get_type ()) +#define EGG_SM_CLIENT_WIN32(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32)) +#define EGG_SM_CLIENT_WIN32_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class)) +#define EGG_IS_SM_CLIENT_WIN32(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_WIN32)) +#define EGG_IS_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_WIN32)) +#define EGG_SM_CLIENT_WIN32_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class)) + +typedef struct _EggSMClientWin32        EggSMClientWin32; +typedef struct _EggSMClientWin32Class   EggSMClientWin32Class; + +struct _EggSMClientWin32 { +  EggSMClient parent; + +  HANDLE message_event, response_event; + +  volatile GSourceFunc event; +  volatile gboolean will_quit; +}; + +struct _EggSMClientWin32Class +{ +  EggSMClientClass parent_class; + +}; + +static void     sm_client_win32_startup (EggSMClient *client, +					 const char  *client_id); +static void     sm_client_win32_will_quit (EggSMClient *client, +					   gboolean     will_quit); +static gboolean sm_client_win32_end_session (EggSMClient         *client, +					     EggSMClientEndStyle  style, +					     gboolean  request_confirmation); + +static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, +					gpointer user_data); +static gboolean got_message (gpointer user_data); +static void sm_client_thread (gpointer data); + +G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_win32_init (EggSMClientWin32 *win32) +{ +  ; +} + +static void +egg_sm_client_win32_class_init (EggSMClientWin32Class *klass) +{ +  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + +  sm_client_class->startup             = sm_client_win32_startup; +  sm_client_class->will_quit           = sm_client_win32_will_quit; +  sm_client_class->end_session         = sm_client_win32_end_session; +} + +EggSMClient * +egg_sm_client_win32_new (void) +{ +  return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL); +} + +static void +sm_client_win32_startup (EggSMClient *client, +			 const char  *client_id) +{ +  EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; + +  win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL); +  win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL); +  g_win32_handle_source_add (win32->message_event, got_message, win32);   +  _beginthread (sm_client_thread, 0, client); +} + +static void +sm_client_win32_will_quit (EggSMClient *client, +			   gboolean     will_quit) +{ +  EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; + +  win32->will_quit = will_quit; +  SetEvent (win32->response_event); +} + +static gboolean +sm_client_win32_end_session (EggSMClient         *client, +			     EggSMClientEndStyle  style, +			     gboolean             request_confirmation) +{ +  UINT uFlags = EWX_LOGOFF; + +  switch (style) +    { +    case EGG_SM_CLIENT_END_SESSION_DEFAULT: +    case EGG_SM_CLIENT_LOGOUT: +      uFlags = EWX_LOGOFF; +      break; +    case EGG_SM_CLIENT_REBOOT: +      uFlags = EWX_REBOOT; +      break; +    case EGG_SM_CLIENT_SHUTDOWN: +      uFlags = EWX_POWEROFF; +      break; +    } + +  /* There's no way to make ExitWindowsEx() show a logout dialog, so +   * we ignore @request_confirmation. +   */ + +#ifdef SHTDN_REASON_FLAG_PLANNED +  ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED); +#else +  ExitWindowsEx (uFlags, 0); +#endif + +  return TRUE; +} + + +/* callbacks from logout-listener thread */ + +static gboolean +emit_quit_requested (gpointer smclient) +{ +  gdk_threads_enter (); +  egg_sm_client_quit_requested (smclient); +  gdk_threads_leave (); + +  return FALSE; +} + +static gboolean +emit_quit (gpointer smclient) +{ +  EggSMClientWin32 *win32 = smclient; + +  gdk_threads_enter (); +  egg_sm_client_quit (smclient); +  gdk_threads_leave (); + +  SetEvent (win32->response_event); +  return FALSE; +} + +static gboolean +emit_quit_cancelled (gpointer smclient) +{ +  EggSMClientWin32 *win32 = smclient; + +  gdk_threads_enter (); +  egg_sm_client_quit_cancelled (smclient); +  gdk_threads_leave (); + +  SetEvent (win32->response_event); +  return FALSE; +} + +static gboolean +got_message (gpointer smclient) +{ +  EggSMClientWin32 *win32 = smclient; + +  win32->event (win32); +  return TRUE; +} + +/* Windows HANDLE GSource */ + +typedef struct { +  GSource source; +  GPollFD pollfd; +} GWin32HandleSource; + +static gboolean +g_win32_handle_source_prepare (GSource *source, gint *timeout) +{ +  *timeout = -1; +  return FALSE; +} + +static gboolean +g_win32_handle_source_check (GSource *source) +{ +  GWin32HandleSource *hsource = (GWin32HandleSource *)source; + +  return hsource->pollfd.revents; +} + +static gboolean +g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ +  return (*callback) (user_data); +} + +static void +g_win32_handle_source_finalize (GSource *source) +{ +  ; +} + +GSourceFuncs g_win32_handle_source_funcs = { +  g_win32_handle_source_prepare, +  g_win32_handle_source_check, +  g_win32_handle_source_dispatch, +  g_win32_handle_source_finalize +}; + +static GSource * +g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data) +{ +  GWin32HandleSource *hsource; +  GSource *source; + +  source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource)); +  hsource = (GWin32HandleSource *)source; +  hsource->pollfd.fd = (int)handle; +  hsource->pollfd.events = G_IO_IN; +  hsource->pollfd.revents = 0; +  g_source_add_poll (source, &hsource->pollfd); + +  g_source_set_callback (source, callback, user_data, NULL); +  g_source_attach (source, NULL); +  return source; +} + +/* logout-listener thread */ + +LRESULT CALLBACK +sm_client_win32_window_procedure (HWND   hwnd, +				  UINT   message, +				  WPARAM wParam, +				  LPARAM lParam) +{ +  EggSMClientWin32 *win32 = +    (EggSMClientWin32 *)GetWindowLongPtr (hwnd, GWLP_USERDATA); + +  switch (message) +    { +    case WM_QUERYENDSESSION: +      win32->event = emit_quit_requested; +      SetEvent (win32->message_event); + +      WaitForSingleObject (win32->response_event, INFINITE); +      return win32->will_quit; + +    case WM_ENDSESSION: +      if (wParam) +	{ +	  /* The session is ending */ +	  win32->event = emit_quit; +	} +      else +	{ +	  /* Nope, the session *isn't* ending */ +	  win32->event = emit_quit_cancelled; +	} + +      SetEvent (win32->message_event); +      WaitForSingleObject (win32->response_event, INFINITE); + +      return 0; + +    default: +      return DefWindowProc (hwnd, message, wParam, lParam); +    } +} + +static void +sm_client_thread (gpointer smclient) +{ +  HINSTANCE instance; +  WNDCLASSEXW wcl;  +  ATOM klass; +  HWND window; +  MSG msg; + +  instance = GetModuleHandle (NULL); + +  memset (&wcl, 0, sizeof (WNDCLASSEX)); +  wcl.cbSize = sizeof (WNDCLASSEX); +  wcl.lpfnWndProc = sm_client_win32_window_procedure; +  wcl.hInstance = instance; +  wcl.lpszClassName = L"EggSmClientWindow"; +  klass = RegisterClassEx (&wcl); + +  window = CreateWindowEx (0, MAKEINTRESOURCE (klass), +			   L"EggSmClientWindow", 0, +			   10, 10, 50, 50, GetDesktopWindow (), +			   NULL, instance, NULL); +  SetWindowLongPtr (window, GWLP_USERDATA, (LONG_PTR)smclient); + +  /* main loop */ +  while (GetMessage (&msg, NULL, 0, 0)) +    DispatchMessage (&msg); +} diff --git a/gedit/smclient/eggsmclient-xsmp.c b/gedit/smclient/eggsmclient-xsmp.c new file mode 100755 index 00000000..a6d3f11f --- /dev/null +++ b/gedit/smclient/eggsmclient-xsmp.c @@ -0,0 +1,1370 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * Inspired by various other pieces of code including GsmClient (C) + * 2001 Havoc Pennington, MateClient (C) 1998 Carsten Schaar, and twm + * session code (C) 1998 The Open Group. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +#include "eggdesktopfile.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <X11/SM/SMlib.h> + +#include <gdk/gdk.h> + +#define EGG_TYPE_SM_CLIENT_XSMP            (egg_sm_client_xsmp_get_type ()) +#define EGG_SM_CLIENT_XSMP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP)) +#define EGG_SM_CLIENT_XSMP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) +#define EGG_IS_SM_CLIENT_XSMP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) + +typedef struct _EggSMClientXSMP        EggSMClientXSMP; +typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass; + +/* These mostly correspond to the similarly-named states in section + * 9.1 of the XSMP spec. Some of the states there aren't represented + * here, because we don't need them. SHUTDOWN_CANCELLED is slightly + * different from the spec; we use it when the client is IDLE after a + * ShutdownCancelled message, but the application is still interacting + * and doesn't know the shutdown has been cancelled yet. + */ +typedef enum +{ +  XSMP_STATE_IDLE, +  XSMP_STATE_SAVE_YOURSELF, +  XSMP_STATE_INTERACT_REQUEST, +  XSMP_STATE_INTERACT, +  XSMP_STATE_SAVE_YOURSELF_DONE, +  XSMP_STATE_SHUTDOWN_CANCELLED, +  XSMP_STATE_CONNECTION_CLOSED +} EggSMClientXSMPState; + +static const char *state_names[] = { +  "idle", +  "save-yourself", +  "interact-request", +  "interact", +  "save-yourself-done", +  "shutdown-cancelled", +  "connection-closed" +}; + +#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state]) + +struct _EggSMClientXSMP +{ +  EggSMClient parent; + +  SmcConn connection; +  char *client_id; + +  EggSMClientXSMPState state; +  char **restart_command; +  gboolean set_restart_command; +  int restart_style; + +  guint idle; + +  /* Current SaveYourself state */ +  guint expecting_initial_save_yourself : 1; +  guint need_save_state : 1; +  guint need_quit_requested : 1; +  guint interact_errors : 1; +  guint shutting_down : 1; + +  /* Todo list */ +  guint waiting_to_set_initial_properties : 1; +  guint waiting_to_emit_quit : 1; +  guint waiting_to_emit_quit_cancelled : 1; +  guint waiting_to_save_myself : 1; + +}; + +struct _EggSMClientXSMPClass +{ +  EggSMClientClass parent_class; + +}; + +static void     sm_client_xsmp_startup (EggSMClient *client, +					const char  *client_id); +static void     sm_client_xsmp_set_restart_command (EggSMClient  *client, +						    int           argc, +						    const char  **argv); +static void     sm_client_xsmp_will_quit (EggSMClient *client, +					  gboolean     will_quit); +static gboolean sm_client_xsmp_end_session (EggSMClient         *client, +					    EggSMClientEndStyle  style, +					    gboolean  request_confirmation); + +static void xsmp_save_yourself      (SmcConn   smc_conn, +				     SmPointer client_data, +				     int       save_style, +				     Bool      shutdown, +				     int       interact_style, +				     Bool      fast); +static void xsmp_die                (SmcConn   smc_conn, +				     SmPointer client_data); +static void xsmp_save_complete      (SmcConn   smc_conn, +				     SmPointer client_data); +static void xsmp_shutdown_cancelled (SmcConn   smc_conn, +				     SmPointer client_data); +static void xsmp_interact           (SmcConn   smc_conn, +				     SmPointer client_data); + +static SmProp *array_prop        (const char    *name, +				  ...); +static SmProp *ptrarray_prop     (const char    *name, +				  GPtrArray     *values); +static SmProp *string_prop       (const char    *name, +				  const char    *value); +static SmProp *card8_prop        (const char    *name, +				  unsigned char  value); + +static void set_properties         (EggSMClientXSMP *xsmp, ...); +static void delete_properties      (EggSMClientXSMP *xsmp, ...); + +static GPtrArray *generate_command (char       **restart_command, +				    const char  *client_id, +				    const char  *state_file); + +static void save_state            (EggSMClientXSMP *xsmp); +static void do_save_yourself      (EggSMClientXSMP *xsmp); +static void update_pending_events (EggSMClientXSMP *xsmp); + +static void     ice_init             (void); +static gboolean process_ice_messages (IceConn       ice_conn); +static void     smc_error_handler    (SmcConn       smc_conn, +				      Bool          swap, +				      int           offending_minor_opcode, +				      unsigned long offending_sequence, +				      int           error_class, +				      int           severity, +				      SmPointer     values); + +G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp) +{ +  xsmp->state = XSMP_STATE_CONNECTION_CLOSED; +  xsmp->connection = NULL; +  xsmp->restart_style = SmRestartIfRunning; +} + +static void +egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass) +{ +  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + +  sm_client_class->startup             = sm_client_xsmp_startup; +  sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; +  sm_client_class->will_quit           = sm_client_xsmp_will_quit; +  sm_client_class->end_session         = sm_client_xsmp_end_session; +} + +EggSMClient * +egg_sm_client_xsmp_new (void) +{ +  if (!g_getenv ("SESSION_MANAGER")) +    return NULL; + +  return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); +} + +static gboolean +sm_client_xsmp_set_initial_properties (gpointer user_data) +{ +  EggSMClientXSMP *xsmp = user_data; +  EggDesktopFile *desktop_file; +  GPtrArray *clone, *restart; +  char pid_str[64]; + +  if (xsmp->idle) +    { +      g_source_remove (xsmp->idle); +      xsmp->idle = 0; +    } +  xsmp->waiting_to_set_initial_properties = FALSE; + +  if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) +    xsmp->restart_style = SmRestartNever; + +  /* Parse info out of desktop file */ +  desktop_file = egg_get_desktop_file (); +  if (desktop_file) +    { +      GError *err = NULL; +      char *cmdline, **argv; +      int argc; + +      if (xsmp->restart_style == SmRestartIfRunning) +	{ +	  if (egg_desktop_file_get_boolean (desktop_file,  +					    "X-MATE-AutoRestart", NULL)) +	    xsmp->restart_style = SmRestartImmediately; +	} + +      if (!xsmp->set_restart_command) +	{ +	  cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); +	  if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) +	    { +	      egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), +						 argc, (const char **)argv); +	      g_strfreev (argv); +	    } +	  else +	    { +	      g_warning ("Could not parse Exec line in desktop file: %s", +			 err->message); +	      g_error_free (err); +	    } +	  g_free (cmdline); +	} +    } + +  if (!xsmp->set_restart_command) +    xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); + +  clone = generate_command (xsmp->restart_command, NULL, NULL); +  restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + +  g_debug ("Setting initial properties"); + +  /* Program, CloneCommand, RestartCommand, and UserID are required. +   * ProcessID isn't required, but the SM may be able to do something +   * useful with it. +   */ +  g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); +  set_properties (xsmp, +		  string_prop   (SmProgram, g_get_prgname ()), +		  ptrarray_prop (SmCloneCommand, clone), +		  ptrarray_prop (SmRestartCommand, restart), +		  string_prop   (SmUserID, g_get_user_name ()), +		  string_prop   (SmProcessID, pid_str), +		  card8_prop    (SmRestartStyleHint, xsmp->restart_style), +		  NULL); +  g_ptr_array_free (clone, TRUE); +  g_ptr_array_free (restart, TRUE); + +  if (desktop_file) +    { +      set_properties (xsmp, +		      string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), +		      NULL); +    } + +  update_pending_events (xsmp); +  return FALSE; +} + +/* This gets called from two different places: xsmp_die() (when the + * server asks us to disconnect) and process_ice_messages() (when the + * server disconnects unexpectedly). + */ +static void +sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp) +{ +  SmcConn connection; + +  if (!xsmp->connection) +    return; + +  g_debug ("Disconnecting"); + +  connection = xsmp->connection; +  xsmp->connection = NULL; +  SmcCloseConnection (connection, 0, NULL); +  xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + +  xsmp->waiting_to_save_myself = FALSE; +  update_pending_events (xsmp); +} + +static void +sm_client_xsmp_startup (EggSMClient *client, +			const char  *client_id) +{ +  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +  SmcCallbacks callbacks; +  char *ret_client_id; +  char error_string_ret[256]; + +  xsmp->client_id = g_strdup (client_id); + +  ice_init (); +  SmcSetErrorHandler (smc_error_handler); + +  callbacks.save_yourself.callback      = xsmp_save_yourself; +  callbacks.die.callback                = xsmp_die; +  callbacks.save_complete.callback      = xsmp_save_complete; +  callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; + +  callbacks.save_yourself.client_data      = xsmp; +  callbacks.die.client_data                = xsmp; +  callbacks.save_complete.client_data      = xsmp; +  callbacks.shutdown_cancelled.client_data = xsmp; + +  client_id = NULL; +  error_string_ret[0] = '\0'; +  xsmp->connection = +    SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, +		       SmcSaveYourselfProcMask | SmcDieProcMask | +		       SmcSaveCompleteProcMask | +		       SmcShutdownCancelledProcMask, +		       &callbacks, +		       xsmp->client_id, &ret_client_id, +		       sizeof (error_string_ret), error_string_ret); + +  if (!xsmp->connection) +    { +      g_warning ("Failed to connect to the session manager: %s\n", +		 error_string_ret[0] ? +		 error_string_ret : "no error message given"); +      xsmp->state = XSMP_STATE_CONNECTION_CLOSED; +      return; +    } + +  /* We expect a pointless initial SaveYourself if either (a) we +   * didn't have an initial client ID, or (b) we DID have an initial +   * client ID, but the server rejected it and gave us a new one. +   */ +  if (!xsmp->client_id || +      (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) +    xsmp->expecting_initial_save_yourself = TRUE; + +  if (ret_client_id) +    { +      g_free (xsmp->client_id); +      xsmp->client_id = g_strdup (ret_client_id); +      free (ret_client_id); + +      gdk_threads_enter (); +      gdk_set_sm_client_id (xsmp->client_id); +      gdk_threads_leave (); + +      g_debug ("Got client ID \"%s\"", xsmp->client_id); +    } + +  xsmp->state = XSMP_STATE_IDLE; + +  /* Do not set the initial properties until we reach the main loop, +   * so that the application has a chance to call +   * egg_set_desktop_file(). (This may also help the session manager +   * have a better idea of when the application is fully up and +   * running.) +   */ +  xsmp->waiting_to_set_initial_properties = TRUE; +  xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); +} + +static void +sm_client_xsmp_set_restart_command (EggSMClient  *client, +				    int           argc, +				    const char  **argv) +{ +  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +  int i; + +  g_strfreev (xsmp->restart_command); + +  xsmp->restart_command = g_new (char *, argc + 1); +  for (i = 0; i < argc; i++) +    xsmp->restart_command[i] = g_strdup (argv[i]); +  xsmp->restart_command[i] = NULL; + +  xsmp->set_restart_command = TRUE; +} + +static void +sm_client_xsmp_will_quit (EggSMClient *client, +			  gboolean     will_quit) +{ +  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + +  if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) +    { +      /* The session manager has already exited! Schedule a quit +       * signal. +       */ +      xsmp->waiting_to_emit_quit = TRUE; +      update_pending_events (xsmp); +      return; +    } +  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +      /* We received a ShutdownCancelled message while the application +       * was interacting; Schedule a quit_cancelled signal. +       */ +      xsmp->waiting_to_emit_quit_cancelled = TRUE; +      update_pending_events (xsmp); +      return; +    } + +  g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); + +  g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); +  SmcInteractDone (xsmp->connection, !will_quit); + +  if (will_quit && xsmp->need_save_state) +    save_state (xsmp); + +  g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); +  SmcSaveYourselfDone (xsmp->connection, will_quit); +  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static gboolean +sm_client_xsmp_end_session (EggSMClient         *client, +			    EggSMClientEndStyle  style, +			    gboolean             request_confirmation) +{ +  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; +  int save_type; + +  /* To end the session via XSMP, we have to send a +   * SaveYourselfRequest. We aren't allowed to do that if anything +   * else is going on, but we don't want to expose this fact to the +   * application. So we do our best to patch things up here... +   * +   * In the worst case, this method might block for some length of +   * time in process_ice_messages, but the only time that code path is +   * honestly likely to get hit is if the application tries to end the +   * session as the very first thing it does, in which case it +   * probably won't actually block anyway. It's not worth gunking up +   * the API to try to deal nicely with the other 0.01% of cases where +   * this happens. +   */ + +  while (xsmp->state != XSMP_STATE_IDLE || +	 xsmp->expecting_initial_save_yourself) +    { +      /* If we're already shutting down, we don't need to do anything. */ +      if (xsmp->shutting_down) +	return TRUE; + +      switch (xsmp->state) +	{ +	case XSMP_STATE_CONNECTION_CLOSED: +	  return FALSE; + +	case XSMP_STATE_SAVE_YOURSELF: +	  /* Trying to log out from the save_state callback? Whatever. +	   * Abort the save_state. +	   */ +	  SmcSaveYourselfDone (xsmp->connection, FALSE); +	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +	  break; + +	case XSMP_STATE_INTERACT_REQUEST: +	case XSMP_STATE_INTERACT: +	case XSMP_STATE_SHUTDOWN_CANCELLED: +	  /* Already in a shutdown-related state, just ignore +	   * the new shutdown request... +	   */ +	  return TRUE; + +	case XSMP_STATE_IDLE: +	  if (xsmp->waiting_to_set_initial_properties) +	    sm_client_xsmp_set_initial_properties (xsmp); + +	  if (!xsmp->expecting_initial_save_yourself) +	    break; +	  /* else fall through */ + +	case XSMP_STATE_SAVE_YOURSELF_DONE: +	  /* We need to wait for some response from the server.*/ +	  process_ice_messages (SmcGetIceConnection (xsmp->connection)); +	  break; + +	default: +	  /* Hm... shouldn't happen */ +	  return FALSE; +	} +    } + +  /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and +   * the user chooses to save the session. But mate-session will do +   * the wrong thing if we pass SmSaveBoth and the user chooses NOT to +   * save the session... Sigh. +   */ +  if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) +    save_type = SmSaveBoth; +  else +    save_type = SmSaveGlobal; + +  g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); +  SmcRequestSaveYourself (xsmp->connection, +			  save_type, +			  True, /* shutdown */ +			  SmInteractStyleAny, +			  !request_confirmation, /* fast */ +			  True /* global */); +  return TRUE; +} + +static gboolean +idle_do_pending_events (gpointer data) +{ +  EggSMClientXSMP *xsmp = data; +  EggSMClient *client = data; + +  gdk_threads_enter (); + +  xsmp->idle = 0; + +  if (xsmp->waiting_to_emit_quit) +    { +      xsmp->waiting_to_emit_quit = FALSE; +      egg_sm_client_quit (client); +      goto out; +    } + +  if (xsmp->waiting_to_emit_quit_cancelled) +    { +      xsmp->waiting_to_emit_quit_cancelled = FALSE; +      egg_sm_client_quit_cancelled (client); +      xsmp->state = XSMP_STATE_IDLE; +    } + +  if (xsmp->waiting_to_save_myself) +    { +      xsmp->waiting_to_save_myself = FALSE; +      do_save_yourself (xsmp); +    } + + out: +  gdk_threads_leave (); +  return FALSE; +} + +static void +update_pending_events (EggSMClientXSMP *xsmp) +{ +  gboolean want_idle = +    xsmp->waiting_to_emit_quit || +    xsmp->waiting_to_emit_quit_cancelled || +    xsmp->waiting_to_save_myself; + +  if (want_idle) +    { +      if (xsmp->idle == 0) +	xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); +    } +  else +    { +      if (xsmp->idle != 0) +	g_source_remove (xsmp->idle); +      xsmp->idle = 0; +    } +} + +static void +fix_broken_state (EggSMClientXSMP *xsmp, const char *message, +		  gboolean send_interact_done, +		  gboolean send_save_yourself_done) +{ +  g_warning ("Received XSMP %s message in state %s: client or server error", +	     message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  /* Forget any pending SaveYourself plans we had */ +  xsmp->waiting_to_save_myself = FALSE; +  update_pending_events (xsmp); + +  if (send_interact_done) +    SmcInteractDone (xsmp->connection, False); +  if (send_save_yourself_done) +    SmcSaveYourselfDone (xsmp->connection, True); + +  xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; +} + +/* SM callbacks */ + +static void +xsmp_save_yourself (SmcConn   smc_conn, +		    SmPointer client_data, +		    int       save_type, +		    Bool      shutdown, +		    int       interact_style, +		    Bool      fast) +{ +  EggSMClientXSMP *xsmp = client_data; +  gboolean wants_quit_requested; + +  g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", +	   save_type == SmSaveLocal ? "SmSaveLocal" : +	   save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", +	   shutdown ? "Shutdown" : "!Shutdown", +	   interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : +	   interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : +	   "SmInteractStyleNone", fast ? "Fast" : "!Fast", +	   EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  if (xsmp->state != XSMP_STATE_IDLE && +      xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) +    { +      fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); +      return; +    } + +  if (xsmp->waiting_to_set_initial_properties) +    sm_client_xsmp_set_initial_properties (xsmp); + +  /* If this is the initial SaveYourself, ignore it; we've already set +   * properties and there's no reason to actually save state too. +   */ +  if (xsmp->expecting_initial_save_yourself) +    { +      xsmp->expecting_initial_save_yourself = FALSE; + +      if (save_type == SmSaveLocal && +	  interact_style == SmInteractStyleNone && +	  !shutdown && !fast) +	{ +	  g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); +	  SmcSaveYourselfDone (xsmp->connection, True); +	  /* As explained in the comment at the end of +	   * do_save_yourself(), SAVE_YOURSELF_DONE is the correct +	   * state here, not IDLE. +	   */ +	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +	  return; +	} +      else +	g_warning ("First SaveYourself was not the expected one!"); +    } + +  /* Even ignoring the "fast" flag completely, there are still 18 +   * different combinations of save_type, shutdown and interact_style. +   * We interpret them as follows: +   * +   *   Type  Shutdown  Interact	 Interpretation +   *     G      F       A/E/N  	 do nothing (1) +   *     G      T         N    	 do nothing (1)* +   *     G      T        A/E   	 quit_requested (2) +   *    L/B     F       A/E/N  	 save_state (3) +   *    L/B     T         N    	 save_state (3)* +   *    L/B     T        A/E   	 quit_requested, then save_state (4) +   * +   *   1. Do nothing, because the SM asked us to do something +   *      uninteresting (save open files, but then don't quit +   *      afterward) or rude (save open files without asking the user +   *      for confirmation). +   * +   *   2. Request interaction and then emit ::quit_requested. This +   *      perhaps isn't quite correct for the SmInteractStyleErrors +   *      case, but we don't care. +   * +   *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these +   *      rows essentially get demoted to SmSaveLocal, because their +   *      Global halves correspond to "do nothing". +   * +   *   4. Request interaction, emit ::quit_requested, and then emit +   *      ::save_state after interacting. This is the SmSaveBoth +   *      equivalent of #2, but we also promote SmSaveLocal shutdown +   *      SaveYourselfs to SmSaveBoth here, because we want to give +   *      the user a chance to save open files before quitting. +   * +   * (* It would be nice if we could do something useful when the +   * session manager sends a SaveYourself with shutdown True and +   * SmInteractStyleNone. But we can't, so we just pretend it didn't +   * even tell us it was shutting down. The docs for ::quit mention +   * that it might not always be preceded by ::quit_requested.) +   */ + +  /* As an optimization, we don't actually request interaction and +   * emit ::quit_requested if the application isn't listening to the +   * signal. +   */ +  wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); + +  xsmp->need_save_state     = (save_type != SmSaveGlobal); +  xsmp->need_quit_requested = (shutdown && wants_quit_requested && +			       interact_style != SmInteractStyleNone); +  xsmp->interact_errors     = (interact_style == SmInteractStyleErrors); + +  xsmp->shutting_down       = shutdown; + +  do_save_yourself (xsmp); +} + +static void +do_save_yourself (EggSMClientXSMP *xsmp) +{ +  if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +      /* The SM cancelled a previous SaveYourself, but we haven't yet +       * had a chance to tell the application, so we can't start +       * processing this SaveYourself yet. +       */ +      xsmp->waiting_to_save_myself = TRUE; +      update_pending_events (xsmp); +      return; +    } + +  if (xsmp->need_quit_requested) +    { +      xsmp->state = XSMP_STATE_INTERACT_REQUEST; + +      g_debug ("Sending InteractRequest(%s)", +	       xsmp->interact_errors ? "Error" : "Normal"); +      SmcInteractRequest (xsmp->connection, +			  xsmp->interact_errors ? SmDialogError : SmDialogNormal, +			  xsmp_interact, +			  xsmp); +      return; +    } + +  if (xsmp->need_save_state) +    { +      save_state (xsmp); + +      /* Though unlikely, the client could have been disconnected +       * while the application was saving its state. +       */ +      if (!xsmp->connection) +	 return; +    } + +  g_debug ("Sending SaveYourselfDone(True)"); +  SmcSaveYourselfDone (xsmp->connection, True); + +  /* The client state diagram in the XSMP spec says that after a +   * non-shutdown SaveYourself, we go directly back to "idle". But +   * everything else in both the XSMP spec and the libSM docs +   * disagrees. +   */ +  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static void +save_state (EggSMClientXSMP *xsmp) +{ +  GKeyFile *state_file; +  char *state_file_path, *data; +  EggDesktopFile *desktop_file; +  GPtrArray *restart; +  int offset, fd; + +  /* We set xsmp->state before emitting save_state, but our caller is +   * responsible for setting it back afterward. +   */ +  xsmp->state = XSMP_STATE_SAVE_YOURSELF; + +  state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); +  if (!state_file) +    { +      restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); +      set_properties (xsmp, +		      ptrarray_prop (SmRestartCommand, restart), +		      NULL); +      g_ptr_array_free (restart, TRUE); +      delete_properties (xsmp, SmDiscardCommand, NULL); +      return; +    } + +  desktop_file = egg_get_desktop_file (); +  if (desktop_file) +    { +      GKeyFile *merged_file; +      char *desktop_file_path; + +      merged_file = g_key_file_new (); +      desktop_file_path = +	g_filename_from_uri (egg_desktop_file_get_source (desktop_file), +			     NULL, NULL); +      if (desktop_file_path && +	  g_key_file_load_from_file (merged_file, desktop_file_path, +				     G_KEY_FILE_KEEP_COMMENTS | +				     G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) +	{ +	  guint g, k, i; +	  char **groups, **keys, *value, *exec; + +	  groups = g_key_file_get_groups (state_file, NULL); +	  for (g = 0; groups[g]; g++) +	    { +	      keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); +	      for (k = 0; keys[k]; k++) +		{ +		  value = g_key_file_get_value (state_file, groups[g], +						keys[k], NULL); +		  if (value) +		    { +		      g_key_file_set_value (merged_file, groups[g], +					    keys[k], value); +		      g_free (value); +		    } +		} +	      g_strfreev (keys); +	    } +	  g_strfreev (groups); + +	  g_key_file_free (state_file); +	  state_file = merged_file; + +	  /* Update Exec key using "--sm-client-state-file %k" */ +	  restart = generate_command (xsmp->restart_command, +				      NULL, "%k"); +	  for (i = 0; i < restart->len; i++) +	    restart->pdata[i] = g_shell_quote (restart->pdata[i]); +	  g_ptr_array_add (restart, NULL); +	  exec = g_strjoinv (" ", (char **)restart->pdata); +	  g_strfreev ((char **)restart->pdata); +	  g_ptr_array_free (restart, FALSE); + +	  g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, +				 EGG_DESKTOP_FILE_KEY_EXEC, +				 exec); +	  g_free (exec); +	} +      else +	desktop_file = NULL; + +      g_free (desktop_file_path); +    } + +  /* Now write state_file to disk. (We can't use mktemp(), because +   * that requires the filename to end with "XXXXXX", and we want +   * it to end with ".desktop".) +   */ + +  data = g_key_file_to_data (state_file, NULL, NULL); +  g_key_file_free (state_file); + +  offset = 0; +  while (1) +    { +      state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", +					 g_get_user_config_dir (), +					 G_DIR_SEPARATOR, G_DIR_SEPARATOR, +					 g_get_prgname (), +					 (long)time (NULL) + offset, +					 desktop_file ? "desktop" : "state"); + +      fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); +      if (fd == -1) +	{ +	  if (errno == EEXIST) +	    { +	      offset++; +	      g_free (state_file_path); +	      continue; +	    } +	  else if (errno == ENOTDIR || errno == ENOENT) +	    { +	      char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); + +	      *sep = '\0'; +	      if (g_mkdir_with_parents (state_file_path, 0755) != 0) +		{ +		  g_warning ("Could not create directory '%s'", +			     state_file_path); +		  g_free (state_file_path); +		  state_file_path = NULL; +		  break; +		} + +	      continue; +	    } + +	  g_warning ("Could not create file '%s': %s", +		     state_file_path, g_strerror (errno)); +	  g_free (state_file_path); +	  state_file_path = NULL; +	  break; +	} + +      close (fd); +      g_file_set_contents (state_file_path, data, -1, NULL); +      break; +    } +  g_free (data); + +  restart = generate_command (xsmp->restart_command, xsmp->client_id, +			      state_file_path); +  set_properties (xsmp, +		  ptrarray_prop (SmRestartCommand, restart), +		  NULL); +  g_ptr_array_free (restart, TRUE); + +  if (state_file_path) +    { +      set_properties (xsmp, +		      array_prop (SmDiscardCommand, +				  "/bin/rm", "-rf", state_file_path, +				  NULL), +		      NULL); +      g_free (state_file_path); +    } +} + +static void +xsmp_interact (SmcConn   smc_conn, +	       SmPointer client_data) +{ +  EggSMClientXSMP *xsmp = client_data; +  EggSMClient *client = client_data; + +  g_debug ("Received Interact message in state %s", +	   EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) +    { +      fix_broken_state (xsmp, "Interact", TRUE, TRUE); +      return; +    } + +  xsmp->state = XSMP_STATE_INTERACT; +  egg_sm_client_quit_requested (client); +} + +static void +xsmp_die (SmcConn   smc_conn, +	  SmPointer client_data) +{ +  EggSMClientXSMP *xsmp = client_data; +  EggSMClient *client = client_data; + +  g_debug ("Received Die message in state %s", +	   EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  sm_client_xsmp_disconnect (xsmp); +  egg_sm_client_quit (client); +} + +static void +xsmp_save_complete (SmcConn   smc_conn, +		    SmPointer client_data) +{ +  EggSMClientXSMP *xsmp = client_data; + +  g_debug ("Received SaveComplete message in state %s", +	   EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) +    xsmp->state = XSMP_STATE_IDLE; +  else +    fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); +} + +static void +xsmp_shutdown_cancelled (SmcConn   smc_conn, +			 SmPointer client_data) +{ +  EggSMClientXSMP *xsmp = client_data; +  EggSMClient *client = client_data; + +  g_debug ("Received ShutdownCancelled message in state %s", +	   EGG_SM_CLIENT_XSMP_STATE (xsmp)); + +  xsmp->shutting_down = FALSE; + +  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) +    { +      /* We've finished interacting and now the SM has agreed to +       * cancel the shutdown. +       */ +      xsmp->state = XSMP_STATE_IDLE; +      egg_sm_client_quit_cancelled (client); +    } +  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) +    { +      /* Hm... ok, so we got a shutdown SaveYourself, which got +       * cancelled, but the application was still interacting, so we +       * didn't tell it yet, and then *another* SaveYourself arrived, +       * which we must still be waiting to tell the app about, except +       * that now that SaveYourself has been cancelled too! Dizzy yet? +       */ +      xsmp->waiting_to_save_myself = FALSE; +      update_pending_events (xsmp); +    } +  else +    { +      g_debug ("Sending SaveYourselfDone(False)"); +      SmcSaveYourselfDone (xsmp->connection, False); + +      if (xsmp->state == XSMP_STATE_INTERACT) +	{ +	  /* The application is currently interacting, so we can't +	   * tell it about the cancellation yet; we will wait until +	   * after it calls egg_sm_client_will_quit(). +	   */ +	  xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; +	} +      else +	{ +	  /* The shutdown was cancelled before the application got a +	   * chance to interact. +	   */ +	  xsmp->state = XSMP_STATE_IDLE; +	} +    } +} + +/* Utilities */ + +/* Create a restart/clone/Exec command based on @restart_command. + * If @client_id is non-%NULL, add "--sm-client-id @client_id". + * If @state_file is non-%NULL, add "--sm-client-state-file @state_file". + * + * None of the input strings are g_strdup()ed; the caller must keep + * them around until it is done with the returned GPtrArray, and must + * then free the array, but not its contents. + */ +static GPtrArray * +generate_command (char **restart_command, const char *client_id, +		  const char *state_file) +{ +  GPtrArray *cmd; +  int i; + +  cmd = g_ptr_array_new (); +  g_ptr_array_add (cmd, restart_command[0]); + +  if (client_id) +    { +      g_ptr_array_add (cmd, (char *)"--sm-client-id"); +      g_ptr_array_add (cmd, (char *)client_id); +    } + +  if (state_file) +    { +      g_ptr_array_add (cmd, (char *)"--sm-client-state-file"); +      g_ptr_array_add (cmd, (char *)state_file); +    } + +  for (i = 1; restart_command[i]; i++) +    g_ptr_array_add (cmd, restart_command[i]); + +  return cmd; +} + +/* Takes a NULL-terminated list of SmProp * values, created by + * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and + * frees them. + */ +static void +set_properties (EggSMClientXSMP *xsmp, ...) +{ +  GPtrArray *props; +  SmProp *prop; +  va_list ap; +  guint i; + +  props = g_ptr_array_new (); + +  va_start (ap, xsmp); +  while ((prop = va_arg (ap, SmProp *))) +    g_ptr_array_add (props, prop); +  va_end (ap); + +  if (xsmp->connection) +    { +      SmcSetProperties (xsmp->connection, props->len, +			(SmProp **)props->pdata); +    } + +  for (i = 0; i < props->len; i++) +    { +      prop = props->pdata[i]; +      g_free (prop->vals); +      g_free (prop); +    } +  g_ptr_array_free (props, TRUE); +} + +/* Takes a NULL-terminated list of property names and deletes them. */ +static void +delete_properties (EggSMClientXSMP *xsmp, ...) +{ +  GPtrArray *props; +  char *prop; +  va_list ap; + +  if (!xsmp->connection) +    return; + +  props = g_ptr_array_new (); + +  va_start (ap, xsmp); +  while ((prop = va_arg (ap, char *))) +    g_ptr_array_add (props, prop); +  va_end (ap); + +  SmcDeleteProperties (xsmp->connection, props->len, +		       (char **)props->pdata); + +  g_ptr_array_free (props, TRUE); +} + +/* Takes an array of strings and creates a LISTofARRAY8 property. The + * strings are neither dupped nor freed; they need to remain valid + * until you're done with the SmProp. + */ +static SmProp * +array_prop (const char *name, ...)  +{ +  SmProp *prop; +  SmPropValue pv; +  GArray *vals; +  char *value; +  va_list ap; + +  prop = g_new (SmProp, 1); +  prop->name = (char *)name; +  prop->type = (char *)SmLISTofARRAY8; + +  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + +  va_start (ap, name); +  while ((value = va_arg (ap, char *))) +    { +      pv.length = strlen (value); +      pv.value = value; +      g_array_append_val (vals, pv); +    } + +  prop->num_vals = vals->len; +  prop->vals = (SmPropValue *)vals->data; + +  g_array_free (vals, FALSE); + +  return prop; +} + +/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property. + * The array contents are neither dupped nor freed; they need to + * remain valid until you're done with the SmProp. + */ +static SmProp * +ptrarray_prop (const char *name, GPtrArray *values) +{ +  SmProp *prop; +  SmPropValue pv; +  GArray *vals; +  guint i; + +  prop = g_new (SmProp, 1); +  prop->name = (char *)name; +  prop->type = (char *)SmLISTofARRAY8; + +  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + +  for (i = 0; i < values->len; i++) +    { +      pv.length = strlen (values->pdata[i]); +      pv.value = values->pdata[i]; +      g_array_append_val (vals, pv); +    } + +  prop->num_vals = vals->len; +  prop->vals = (SmPropValue *)vals->data; + +  g_array_free (vals, FALSE); + +  return prop; +} + +/* Takes a string and creates an ARRAY8 property. The string is + * neither dupped nor freed; it needs to remain valid until you're + * done with the SmProp. + */ +static SmProp * +string_prop (const char *name, const char *value) +{ +  SmProp *prop; + +  prop = g_new (SmProp, 1); +  prop->name = (char *)name; +  prop->type = (char *)SmARRAY8; + +  prop->num_vals = 1; +  prop->vals = g_new (SmPropValue, 1); + +  prop->vals[0].length = strlen (value); +  prop->vals[0].value = (char *)value; + +  return prop; +} + +/* Takes a char and creates a CARD8 property. */ +static SmProp * +card8_prop (const char *name, unsigned char value) +{ +  SmProp *prop; +  char *card8val; + +  /* To avoid having to allocate and free prop->vals[0], we cheat and +   * make vals a 2-element-long array and then use the second element +   * to store value. +   */ + +  prop = g_new (SmProp, 1); +  prop->name = (char *)name; +  prop->type = (char *)SmCARD8; + +  prop->num_vals = 1; +  prop->vals = g_new (SmPropValue, 2); +  card8val = (char *)(&prop->vals[1]); +  card8val[0] = value; + +  prop->vals[0].length = 1; +  prop->vals[0].value = card8val; + +  return prop; +} + +/* ICE code. This makes no effort to play nice with anyone else trying + * to use libICE. Fortunately, no one uses libICE for anything other + * than SM. (DCOP uses ICE, but it has its own private copy of + * libICE.) + * + * When this moves to gtk, it will need to be cleverer, to avoid + * tripping over old apps that use MateClient or that use libSM + * directly. + */ + +#include <X11/ICE/ICElib.h> +#include <fcntl.h> + +static void        ice_error_handler    (IceConn        ice_conn, +					 Bool           swap, +					 int            offending_minor_opcode, +					 unsigned long  offending_sequence, +					 int            error_class, +					 int            severity, +					 IcePointer     values); +static void        ice_io_error_handler (IceConn        ice_conn); +static void        ice_connection_watch (IceConn        ice_conn, +					 IcePointer     client_data, +					 Bool           opening, +					 IcePointer    *watch_data); + +static void +ice_init (void) +{ +  IceSetIOErrorHandler (ice_io_error_handler); +  IceSetErrorHandler (ice_error_handler); +  IceAddConnectionWatch (ice_connection_watch, NULL); +} + +static gboolean +process_ice_messages (IceConn ice_conn) +{ +  IceProcessMessagesStatus status; + +  gdk_threads_enter (); +  status = IceProcessMessages (ice_conn, NULL, NULL); +  gdk_threads_leave (); + +  switch (status) +    { +    case IceProcessMessagesSuccess: +      return TRUE; + +    case IceProcessMessagesIOError: +      sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); +      return FALSE; + +    case IceProcessMessagesConnectionClosed: +      return FALSE; + +    default: +      g_assert_not_reached (); +    } +} + +static gboolean +ice_iochannel_watch (GIOChannel   *channel, +		     GIOCondition  condition, +		     gpointer      client_data) +{ +  return process_ice_messages (client_data); +} + +static void +ice_connection_watch (IceConn     ice_conn, +		      IcePointer  client_data, +		      Bool        opening, +		      IcePointer *watch_data) +{ +  guint watch_id; + +  if (opening) +    { +      GIOChannel *channel; +      int fd = IceConnectionNumber (ice_conn); + +      fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); +      channel = g_io_channel_unix_new (fd); +      watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, +				 ice_iochannel_watch, ice_conn); +      g_io_channel_unref (channel); + +      *watch_data = GUINT_TO_POINTER (watch_id); +    } +  else +    { +      watch_id = GPOINTER_TO_UINT (*watch_data); +      g_source_remove (watch_id); +    } +} + +static void +ice_error_handler (IceConn       ice_conn, +		   Bool          swap, +		   int           offending_minor_opcode, +		   unsigned long offending_sequence, +		   int           error_class, +		   int           severity, +		   IcePointer    values) +{ +  /* Do nothing */ +}  + +static void +ice_io_error_handler (IceConn ice_conn) +{ +  /* Do nothing */ +}  + +static void +smc_error_handler (SmcConn       smc_conn, +                   Bool          swap, +                   int           offending_minor_opcode, +                   unsigned long offending_sequence, +                   int           error_class, +                   int           severity, +                   SmPointer     values) +{ +  /* Do nothing */ +} diff --git a/gedit/smclient/eggsmclient.c b/gedit/smclient/eggsmclient.c new file mode 100755 index 00000000..4b65f283 --- /dev/null +++ b/gedit/smclient/eggsmclient.c @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +static void egg_sm_client_debug_handler (const char *log_domain, +					 GLogLevelFlags log_level, +					 const char *message, +					 gpointer user_data); + +enum { +  SAVE_STATE, +  QUIT_REQUESTED, +  QUIT_CANCELLED, +  QUIT, +  LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +struct _EggSMClientPrivate { +  GKeyFile *state_file; +}; + +#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate)) + +G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT) + +static EggSMClient *global_client; +static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL; + +static void +egg_sm_client_init (EggSMClient *client) +{ +  ; +} + +static void +egg_sm_client_class_init (EggSMClientClass *klass) +{ +  GObjectClass *object_class = G_OBJECT_CLASS (klass); + +  g_type_class_add_private (klass, sizeof (EggSMClientPrivate)); + +  /** +   * EggSMClient::save_state: +   * @client: the client +   * @state_file: a #GKeyFile to save state information into +   * +   * Emitted when the session manager has requested that the +   * application save information about its current state. The +   * application should save its state into @state_file, and then the +   * session manager may then restart the application in a future +   * session and tell it to initialize itself from that state. +   * +   * You should not save any data into @state_file's "start group" +   * (ie, the %NULL group). Instead, applications should save their +   * data into groups with names that start with the application name, +   * and libraries that connect to this signal should save their data +   * into groups with names that start with the library name. +   * +   * Alternatively, rather than (or in addition to) using @state_file, +   * the application can save its state by calling +   * egg_sm_client_set_restart_command() during the processing of this +   * signal (eg, to include a list of files to open). +   **/ +  signals[SAVE_STATE] = +    g_signal_new ("save_state", +                  G_OBJECT_CLASS_TYPE (object_class), +                  G_SIGNAL_RUN_LAST, +                  G_STRUCT_OFFSET (EggSMClientClass, save_state), +                  NULL, NULL, +                  g_cclosure_marshal_VOID__POINTER, +                  G_TYPE_NONE, +                  1, G_TYPE_POINTER); + +  /** +   * EggSMClient::quit_requested: +   * @client: the client +   * +   * Emitted when the session manager requests that the application +   * exit (generally because the user is logging out). The application +   * should decide whether or not it is willing to quit (perhaps after +   * asking the user what to do with documents that have unsaved +   * changes) and then call egg_sm_client_will_quit(), passing %TRUE +   * or %FALSE to give its answer to the session manager. (It does not +   * need to give an answer before returning from the signal handler; +   * it can interact with the user asynchronously and then give its +   * answer later on.) If the application does not connect to this +   * signal, then #EggSMClient will automatically return %TRUE on its +   * behalf. +   * +   * The application should not save its session state as part of +   * handling this signal; if the user has requested that the session +   * be saved when logging out, then ::save_state will be emitted +   * separately. +   *  +   * If the application agrees to quit, it should then wait for either +   * the ::quit_cancelled or ::quit signals to be emitted. +   **/ +  signals[QUIT_REQUESTED] = +    g_signal_new ("quit_requested", +                  G_OBJECT_CLASS_TYPE (object_class), +                  G_SIGNAL_RUN_LAST, +                  G_STRUCT_OFFSET (EggSMClientClass, quit_requested), +                  NULL, NULL, +                  g_cclosure_marshal_VOID__VOID, +                  G_TYPE_NONE, +                  0); + +  /** +   * EggSMClient::quit_cancelled: +   * @client: the client +   * +   * Emitted when the session manager decides to cancel a logout after +   * the application has already agreed to quit. After receiving this +   * signal, the application can go back to what it was doing before +   * receiving the ::quit_requested signal. +   **/ +  signals[QUIT_CANCELLED] = +    g_signal_new ("quit_cancelled", +                  G_OBJECT_CLASS_TYPE (object_class), +                  G_SIGNAL_RUN_LAST, +                  G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled), +                  NULL, NULL, +                  g_cclosure_marshal_VOID__VOID, +                  G_TYPE_NONE, +                  0); + +  /** +   * EggSMClient::quit: +   * @client: the client +   * +   * Emitted when the session manager wants the application to quit +   * (generally because the user is logging out). The application +   * should exit as soon as possible after receiving this signal; if +   * it does not, the session manager may choose to forcibly kill it. +   * +   * Normally a GUI application would only be sent a ::quit if it +   * agreed to quit in response to a ::quit_requested signal. However, +   * this is not guaranteed; in some situations the session manager +   * may decide to end the session without giving applications a +   * chance to object. +   **/ +  signals[QUIT] = +    g_signal_new ("quit", +                  G_OBJECT_CLASS_TYPE (object_class), +                  G_SIGNAL_RUN_LAST, +                  G_STRUCT_OFFSET (EggSMClientClass, quit), +                  NULL, NULL, +                  g_cclosure_marshal_VOID__VOID, +                  G_TYPE_NONE, +                  0); +} + +static gboolean sm_client_disable = FALSE; +static char *sm_client_state_file = NULL; +static char *sm_client_id = NULL; +static char *sm_config_prefix = NULL; + +static gboolean +sm_client_post_parse_func (GOptionContext  *context, +			   GOptionGroup    *group, +			   gpointer         data, +			   GError         **error) +{ +  EggSMClient *client = egg_sm_client_get (); + +  if (sm_client_id == NULL) +    { +      const gchar *desktop_autostart_id; + +      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + +      if (desktop_autostart_id != NULL) +        sm_client_id = g_strdup (desktop_autostart_id); +    } + +  /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to +   * use the same client id. */ +  g_unsetenv ("DESKTOP_AUTOSTART_ID"); + +  if (EGG_SM_CLIENT_GET_CLASS (client)->startup) +    EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id); +  return TRUE; +} + +/** + * egg_sm_client_get_option_group: + * + * Creates a %GOptionGroup containing the session-management-related + * options. You should add this group to the application's + * %GOptionContext if you want to use #EggSMClient. + * + * Return value: the %GOptionGroup + **/ +GOptionGroup * +egg_sm_client_get_option_group (void) +{ +  const GOptionEntry entries[] = { +    { "sm-client-disable", 0, 0, +      G_OPTION_ARG_NONE, &sm_client_disable, +      N_("Disable connection to session manager"), NULL }, +    { "sm-client-state-file", 0, 0, +      G_OPTION_ARG_FILENAME, &sm_client_state_file, +      N_("Specify file containing saved configuration"), N_("FILE") }, +    { "sm-client-id", 0, 0, +      G_OPTION_ARG_STRING, &sm_client_id, +      N_("Specify session management ID"), N_("ID") }, +    /* MateClient compatibility option */ +    { "sm-disable", 0, G_OPTION_FLAG_HIDDEN, +      G_OPTION_ARG_NONE, &sm_client_disable, +      NULL, NULL }, +    /* MateClient compatibility option. This is a dummy option that only +     * exists so that sessions saved by apps with MateClient can be restored +     * later when they've switched to EggSMClient. See bug #575308. +     */ +    { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN, +      G_OPTION_ARG_STRING, &sm_config_prefix, +      NULL, NULL }, +    { NULL } +  }; +  GOptionGroup *group; + +  /* Use our own debug handler for the "EggSMClient" domain. */ +  g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, +		     egg_sm_client_debug_handler, NULL); + +  group = g_option_group_new ("sm-client", +			      _("Session management options:"), +			      _("Show session management options"), +			      NULL, NULL); +  g_option_group_add_entries (group, entries); +  g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func); + +  return group; +} + +/** + * egg_sm_client_set_mode: + * @mode: an #EggSMClient mode + * + * Sets the "mode" of #EggSMClient as follows: + * + *    %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely + *    disabled. The application will not even connect to the session + *    manager. (egg_sm_client_get() will still return an #EggSMClient, + *    but it will just be a dummy object.) + * + *    %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to + *    the session manager (and thus will receive notification when the + *    user is logging out, etc), but will request to not be + *    automatically restarted with saved state in future sessions. + * + *    %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will + *    function normally. + * + * This must be called before the application's main loop begins. + **/ +void +egg_sm_client_set_mode (EggSMClientMode mode) +{ +  global_client_mode = mode; +} + +/** + * egg_sm_client_get_mode: + * + * Gets the global #EggSMClientMode. See egg_sm_client_set_mode() + * for details. + * + * Return value: the global #EggSMClientMode + **/ +EggSMClientMode +egg_sm_client_get_mode (void) +{ +  return global_client_mode; +} + +/** + * egg_sm_client_get: + * + * Returns the master #EggSMClient for the application. + * + * On platforms that support saved sessions (ie, POSIX/X11), the + * application will only request to be restarted by the session + * manager if you call egg_set_desktop_file() to set an application + * desktop file. In particular, if the desktop file contains the key + * "X + * + * Return value: the master #EggSMClient. + **/ +EggSMClient * +egg_sm_client_get (void) +{ +  if (!global_client) +    { +      if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && +	  !sm_client_disable) +	{ +#if defined (GDK_WINDOWING_WIN32) +	  global_client = egg_sm_client_win32_new (); +#elif defined (GDK_WINDOWING_QUARTZ) +	  global_client = egg_sm_client_osx_new (); +#else +	  /* If both D-Bus and XSMP are compiled in, try XSMP first +	   * (since it supports state saving) and fall back to D-Bus +	   * if XSMP isn't available. +	   */ +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +	  global_client = egg_sm_client_xsmp_new (); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +	  if (!global_client) +	    global_client = egg_sm_client_dbus_new (); +# endif +#endif +	} + +      /* Fallback: create a dummy client, so that callers don't have +       * to worry about a %NULL return value. +       */ +      if (!global_client) +	global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL); +    } + +  return global_client; +} + +/** + * egg_sm_client_is_resumed: + * @client: the client + * + * Checks whether or not the current session has been resumed from + * a previous saved session. If so, the application should call + * egg_sm_client_get_state_file() and restore its state from the + * returned #GKeyFile. + * + * Return value: %TRUE if the session has been resumed + **/ +gboolean +egg_sm_client_is_resumed (EggSMClient *client) +{ +  g_return_val_if_fail (client == global_client, FALSE); + +  return sm_client_state_file != NULL; +} + +/** + * egg_sm_client_get_state_file: + * @client: the client + * + * If the application was resumed by the session manager, this will + * return the #GKeyFile containing its state from the previous + * session. + * + * Note that other libraries and #EggSMClient itself may also store + * state in the key file, so if you call egg_sm_client_get_groups(), + * on it, the return value will likely include groups that you did not + * put there yourself. (It is also not guaranteed that the first + * group created by the application will still be the "start group" + * when it is resumed.) + * + * Return value: the #GKeyFile containing the application's earlier + * state, or %NULL on error. You should not free this key file; it + * is owned by @client. + **/ +GKeyFile * +egg_sm_client_get_state_file (EggSMClient *client) +{ +  EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client); +  char *state_file_path; +  GError *err = NULL; + +  g_return_val_if_fail (client == global_client, NULL); + +  if (!sm_client_state_file) +    return NULL; +  if (priv->state_file) +    return priv->state_file; + +  if (!strncmp (sm_client_state_file, "file://", 7)) +    state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL); +  else +    state_file_path = g_strdup (sm_client_state_file); + +  priv->state_file = g_key_file_new (); +  if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err)) +    { +      g_warning ("Could not load SM state file '%s': %s", +		 sm_client_state_file, err->message); +      g_clear_error (&err); +      g_key_file_free (priv->state_file); +      priv->state_file = NULL; +    } + +  g_free (state_file_path); +  return priv->state_file; +} + +/** + * egg_sm_client_set_restart_command: + * @client: the client + * @argc: the length of @argv + * @argv: argument vector + * + * Sets the command used to restart @client if it does not have a + * .desktop file that can be used to find its restart command. + * + * This can also be used when handling the ::save_state signal, to + * save the current state via an updated command line. (Eg, providing + * a list of filenames to open when the application is resumed.) + **/ +void +egg_sm_client_set_restart_command (EggSMClient  *client, +				   int           argc, +				   const char  **argv) +{ +  g_return_if_fail (EGG_IS_SM_CLIENT (client)); + +  if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command) +    EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv); +} + +/** + * egg_sm_client_will_quit: + * @client: the client + * @will_quit: whether or not the application is willing to quit + * + * This MUST be called in response to the ::quit_requested signal, to + * indicate whether or not the application is willing to quit. The + * application may call it either directly from the signal handler, or + * at some later point (eg, after asynchronously interacting with the + * user). + * + * If the application does not connect to ::quit_requested, + * #EggSMClient will call this method on its behalf (passing %TRUE + * for @will_quit). + * + * After calling this method, the application should wait to receive + * either ::quit_cancelled or ::quit. + **/ +void +egg_sm_client_will_quit (EggSMClient *client, +			 gboolean     will_quit) +{ +  g_return_if_fail (EGG_IS_SM_CLIENT (client)); + +  if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit) +    EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit); +} + +/** + * egg_sm_client_end_session: + * @style: a hint at how to end the session + * @request_confirmation: whether or not the user should get a chance + * to confirm the action + * + * Requests that the session manager end the current session. @style + * indicates how the session should be ended, and + * @request_confirmation indicates whether or not the user should be + * given a chance to confirm the logout/reboot/shutdown. Both of these + * flags are merely hints though; the session manager may choose to + * ignore them. + * + * Return value: %TRUE if the request was sent; %FALSE if it could not + * be (eg, because it could not connect to the session manager). + **/ +gboolean +egg_sm_client_end_session (EggSMClientEndStyle  style, +			   gboolean             request_confirmation) +{ +  EggSMClient *client = egg_sm_client_get (); + +  g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE); + +  if (EGG_SM_CLIENT_GET_CLASS (client)->end_session) +    { +      return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style, +							    request_confirmation); +    } +  else +    return FALSE; +} + +/* Signal-emitting callbacks from platform-specific code */ + +GKeyFile * +egg_sm_client_save_state (EggSMClient *client) +{ +  GKeyFile *state_file; +  char *group; + +  g_return_val_if_fail (client == global_client, NULL); + +  state_file = g_key_file_new (); + +  g_debug ("Emitting save_state"); +  g_signal_emit (client, signals[SAVE_STATE], 0, state_file); +  g_debug ("Done emitting save_state"); + +  group = g_key_file_get_start_group (state_file); +  if (group) +    { +      g_free (group); +      return state_file; +    } +  else +    { +      g_key_file_free (state_file); +      return NULL; +    } +} + +void +egg_sm_client_quit_requested (EggSMClient *client) +{ +  g_return_if_fail (client == global_client); + +  if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE)) +    { +      g_debug ("Not emitting quit_requested because no one is listening"); +      egg_sm_client_will_quit (client, TRUE); +      return; +    } + +  g_debug ("Emitting quit_requested"); +  g_signal_emit (client, signals[QUIT_REQUESTED], 0); +  g_debug ("Done emitting quit_requested"); +} + +void +egg_sm_client_quit_cancelled (EggSMClient *client) +{ +  g_return_if_fail (client == global_client); + +  g_debug ("Emitting quit_cancelled"); +  g_signal_emit (client, signals[QUIT_CANCELLED], 0); +  g_debug ("Done emitting quit_cancelled"); +} + +void +egg_sm_client_quit (EggSMClient *client) +{ +  g_return_if_fail (client == global_client); + +  g_debug ("Emitting quit"); +  g_signal_emit (client, signals[QUIT], 0); +  g_debug ("Done emitting quit"); + +  /* FIXME: should we just call gtk_main_quit() here? */ +} + +static void +egg_sm_client_debug_handler (const char *log_domain, +			     GLogLevelFlags log_level, +			     const char *message, +			     gpointer user_data) +{ +  static int debug = -1; + +  if (debug < 0) +    debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL); + +  if (debug) +    g_log_default_handler (log_domain, log_level, message, NULL); +} diff --git a/gedit/smclient/eggsmclient.h b/gedit/smclient/eggsmclient.h new file mode 100755 index 00000000..e620b754 --- /dev/null +++ b/gedit/smclient/eggsmclient.h @@ -0,0 +1,117 @@ +/* eggsmclient.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_H__ +#define __EGG_SM_CLIENT_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_SM_CLIENT            (egg_sm_client_get_type ()) +#define EGG_SM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient)) +#define EGG_SM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass)) +#define EGG_IS_SM_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT)) +#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT)) +#define EGG_SM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass)) + +typedef struct _EggSMClient        EggSMClient; +typedef struct _EggSMClientClass   EggSMClientClass; +typedef struct _EggSMClientPrivate EggSMClientPrivate; + +typedef enum { +  EGG_SM_CLIENT_END_SESSION_DEFAULT, +  EGG_SM_CLIENT_LOGOUT, +  EGG_SM_CLIENT_REBOOT, +  EGG_SM_CLIENT_SHUTDOWN +} EggSMClientEndStyle; + +typedef enum { +  EGG_SM_CLIENT_MODE_DISABLED, +  EGG_SM_CLIENT_MODE_NO_RESTART, +  EGG_SM_CLIENT_MODE_NORMAL +} EggSMClientMode; + +struct _EggSMClient +{ +  GObject parent; + +}; + +struct _EggSMClientClass +{ +  GObjectClass parent_class; + +  /* signals */ +  void (*save_state)       (EggSMClient *client, +			    GKeyFile    *state_file); + +  void (*quit_requested)   (EggSMClient *client); +  void (*quit_cancelled)   (EggSMClient *client); +  void (*quit)             (EggSMClient *client); + +  /* virtual methods */ +  void	   (*startup)             (EggSMClient          *client, +				   const char           *client_id); +  void	   (*set_restart_command) (EggSMClient          *client, +				   int                   argc, +				   const char          **argv); +  void	   (*will_quit)           (EggSMClient          *client, +				   gboolean              will_quit); +  gboolean (*end_session)         (EggSMClient          *client, +				   EggSMClientEndStyle   style, +				   gboolean              request_confirmation); + +  /* Padding for future expansion */ +  void (*_egg_reserved1) (void); +  void (*_egg_reserved2) (void); +  void (*_egg_reserved3) (void); +  void (*_egg_reserved4) (void); +}; + +GType            egg_sm_client_get_type            (void) G_GNUC_CONST; + +GOptionGroup    *egg_sm_client_get_option_group    (void); + +/* Initialization */ +void             egg_sm_client_set_mode            (EggSMClientMode mode); +EggSMClientMode  egg_sm_client_get_mode            (void); +EggSMClient     *egg_sm_client_get                 (void); + +/* Resuming a saved session */ +gboolean         egg_sm_client_is_resumed          (EggSMClient *client); +GKeyFile        *egg_sm_client_get_state_file      (EggSMClient *client); + +/* Alternate means of saving state */ +void             egg_sm_client_set_restart_command (EggSMClient  *client, +						    int           argc, +						    const char  **argv); + +/* Handling "quit_requested" signal */ +void             egg_sm_client_will_quit           (EggSMClient *client, +						    gboolean     will_quit); + +/* Initiate a logout/reboot/shutdown */ +gboolean         egg_sm_client_end_session         (EggSMClientEndStyle  style, +						    gboolean             request_confirmation); + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_H__ */  | 
