/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2001 Ximian, Inc.
* Copyright (C) 2007 Vincent Geddes.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors:
* Chema Celorio <chema@celorio.com>
* Paolo Borelli <pborelli@katamail.com>
* Vincent Geddes <vgeddes@gnome.org>
*/
#include <config.h>
#include "glade-window.h"
#include <gladeui/glade.h>
#include <gladeui/glade-design-view.h>
#include <gladeui/glade-popup.h>
#include <gladeui/glade-inspector.h>
#include <string.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtkstock.h>
#define ACTION_GROUP_STATIC "GladeStatic"
#define ACTION_GROUP_PROJECT "GladeProject"
#define ACTION_GROUP_PROJECTS_LIST_MENU "GladeProjectsList"
#define READONLY_INDICATOR (_("[Read Only]"))
#define URL_USER_MANUAL "http://glade.gnome.org/manual/index.html"
#define URL_DEVELOPER_MANUAL "http://glade.gnome.org/docs/index.html"
#define CONFIG_GROUP_WINDOWS "Glade Windows"
#define GLADE_WINDOW_DEFAULT_WIDTH 720
#define GLADE_WINDOW_DEFAULT_HEIGHT 540
#define CONFIG_KEY_X "x"
#define CONFIG_KEY_Y "y"
#define CONFIG_KEY_WIDTH "width"
#define CONFIG_KEY_HEIGHT "height"
#define CONFIG_KEY_DETACHED "detached"
#define GLADE_WINDOW_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
GLADE_TYPE_WINDOW, \
GladeWindowPrivate))
enum {
DOCK_PALETTE,
DOCK_INSPECTOR,
DOCK_EDITOR,
N_DOCKS
};
typedef struct {
GtkWidget *widget; /* the widget with scrollbars */
GtkWidget *paned; /* GtkPaned in the main window containing which part */
gboolean first_child; /* whether this widget is packed with gtk_paned_pack1() */
gboolean detached; /* whether this widget should be floating */
char *title; /* window title, untranslated */
char *id; /* id to use in config file */
GdkRectangle window_pos; /* x and y == G_MININT means unset */
} ToolDock;
struct _GladeWindowPrivate
{
GladeApp *app;
GtkWidget *main_vbox;
GtkWidget *notebook;
GladeDesignView *active_view;
gint num_tabs;
GtkTooltips *tooltips;
GtkWidget *inspectors_notebook;
GtkWidget *statusbar; /* A pointer to the status bar. */
guint statusbar_menu_context_id; /* The context id of the menu bar */
guint statusbar_actions_context_id; /* The context id of actions messages */
GtkUIManager *ui; /* The UIManager */
guint projects_list_menu_ui_id; /* Merge id for projects list menu */
GtkActionGroup *static_actions; /* All the static actions */
GtkActionGroup *project_actions; /* All the project actions */
GtkActionGroup *projects_list_menu_actions; /* Projects list menu actions */
GtkLabel *label; /* the title of property editor dock */
GtkRecentManager *recent_manager;
GtkWidget *recent_menu;
gchar *default_path; /* the default path for open/save operations */
GtkToggleToolButton *selector_button; /* the widget selector button (replaces the one in the palette) */
GtkToggleToolButton *drag_resize_button; /* sets the pointer to drag/resize mode */
gboolean setting_pointer_mode; /* avoid feedback signal loops */
GtkToolItem *undo; /* customized buttons for undo/redo with history */
GtkToolItem *redo;
GtkWidget *toolbar; /* Actions are added to the toolbar */
gint actions_start; /* start of action items */
/* paned windows that tools get docked into/out of */
GtkWidget *left_pane;
GtkWidget *right_pane;
GdkRectangle position;
ToolDock docks[N_DOCKS];
};
static void refresh_undo_redo (GladeWindow *window);
static void recent_chooser_item_activated_cb (GtkRecentChooser *chooser,
GladeWindow *window);
static void check_reload_project (GladeWindow *window,
GladeProject *project);
static void save_windows_config (GladeWindow *window);
G_DEFINE_TYPE (GladeWindow, glade_window, GTK_TYPE_WINDOW)
static void
about_dialog_activate_link_func (GtkAboutDialog *dialog, const gchar *link, GladeWindow *window)
{
GtkWidget *warning_dialog;
gboolean retval;
retval = glade_util_url_show (link);
if (!retval)
{
warning_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("Could not display the URL '%s'"),
link);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warning_dialog),
_("No suitable web browser could be found."));
gtk_window_set_title (GTK_WINDOW (warning_dialog), "");
g_signal_connect_swapped (warning_dialog, "response",
G_CALLBACK (gtk_widget_destroy),
warning_dialog);
gtk_widget_show (warning_dialog);
}
}
/* locates the help file "glade.xml" with respect to current locale */
static gchar*
locate_help_file ()
{
const gchar* const* locales = g_get_language_names ();
/* check if user manual has been installed, if not, GLADE_GNOMEHELPDIR is empty */
if (strlen (GLADE_GNOMEHELPDIR) == 0)
return NULL;
for (; *locales; locales++)
{
gchar *path;
path = g_build_path (G_DIR_SEPARATOR_S, GLADE_GNOMEHELPDIR, "glade", *locales, "glade.xml", NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS))
{
return (path);
}
g_free (path);
}
return NULL;
}
static gboolean
help_show (const gchar *link_id)
{
gchar *file, *url, *command;
gboolean retval;
gint exit_status = -1;
GError *error = NULL;
file = locate_help_file ();
if (file == NULL)
return FALSE;
if (link_id != NULL) {
url = g_strconcat ("ghelp://", file, "?", link_id, NULL);
} else {
url = g_strconcat ("ghelp://", file, NULL);
}
command = g_strconcat ("gnome-open ", url, NULL);
retval = g_spawn_command_line_sync (command,
NULL,
NULL,
&exit_status,
&error);
if (!retval) {
g_error_free (error);
error = NULL;
}
g_free (command);
g_free (file);
g_free (url);
return retval && !exit_status;
}
/* the following functions are taken from gedit-utils.c */
static gchar *
str_middle_truncate (const gchar *string,
guint truncate_length)
{
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. */
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));
return g_string_free (truncated, FALSE);
}
/*
* Doubles underscore to avoid spurious menu accels - taken from gedit-utils.c
*/
static gchar *
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);
}
typedef enum
{
FORMAT_NAME_MARK_UNSAVED = 1 << 0,
FORMAT_NAME_ESCAPE_UNDERSCORES = 1 << 1,
FORMAT_NAME_MIDDLE_TRUNCATE = 1 << 2
} FormatNameFlags;
#define MAX_TITLE_LENGTH 100
static gchar *
get_formatted_project_name_for_display (GladeProject *project, FormatNameFlags format_flags)
{
gchar *name, *pass1, *pass2, *pass3;
g_return_val_if_fail (project != NULL, NULL);
name = glade_project_get_name (project);
if ((format_flags & FORMAT_NAME_MARK_UNSAVED)
&& glade_project_get_modified (project))
pass1 = g_strdup_printf ("*%s", name);
else
pass1 = g_strdup (name);
if (format_flags & FORMAT_NAME_ESCAPE_UNDERSCORES)
pass2 = escape_underscores (pass1, -1);
else
pass2 = g_strdup (pass1);
if (format_flags & FORMAT_NAME_MIDDLE_TRUNCATE)
pass3 = str_middle_truncate (pass2, MAX_TITLE_LENGTH);
else
pass3 = g_strdup (pass2);
g_free (name);
g_free (pass1);
g_free (pass2);
return pass3;
}
static gchar *
replace_home_dir_with_tilde (const gchar *path)
{
#ifdef G_OS_UNIX
gchar *tmp;
gchar *home;
g_return_val_if_fail (path != NULL, NULL);
/* Note that g_get_home_dir returns a const string */
tmp = (gchar *) g_get_home_dir ();
if (tmp == NULL)
return g_strdup (path);
home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
if (home == NULL)
return g_strdup (path);
if (strcmp (path, home) == 0)
{
g_free (home);
return g_strdup ("~");
}
tmp = home;
home = g_strdup_printf ("%s/", tmp);
g_free (tmp);
if (g_str_has_prefix (path, home))
{
gchar *res;
res = g_strdup_printf ("~/%s", path + strlen (home));
g_free (home);
return res;
}
g_free (home);
return g_strdup (path);
#else
return g_strdup (path);
#endif
}
static void
refresh_title (GladeWindow *window)
{
GladeProject *project;
gchar *title, *name = NULL;
if (window->priv->active_view)
{
project = glade_design_view_get_project (window->priv->active_view);
name = get_formatted_project_name_for_display (project,
FORMAT_NAME_MARK_UNSAVED |
FORMAT_NAME_MIDDLE_TRUNCATE);
if (glade_project_get_readonly (project) != FALSE)
title = g_strdup_printf ("%s %s", name, READONLY_INDICATOR);
else
title = g_strdup_printf ("%s", name);
g_free (name);
}
else
{
title = g_strdup (_("User Interface Designer"));
}
gtk_window_set_title (GTK_WINDOW (window), title);
g_free (title);
}
static const gchar*
get_default_path (GladeWindow *window)
{
return window->priv->default_path;
}
static void
update_default_path (GladeWindow *window, GladeProject *project)
{
gchar *path;
g_return_if_fail (glade_project_get_path (project) != NULL);
path = g_path_get_dirname (glade_project_get_path (project));
g_free (window->priv->default_path);
window->priv->default_path = g_strdup (path);
g_free (path);
}
static gboolean
window_state_event_cb (GtkWidget *widget,
GdkEventWindowState *event,
GladeWindow *window)
{
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));
gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), show);
}
return FALSE;
}
static GtkWidget *
create_recent_chooser_menu (GladeWindow *window, GtkRecentManager *manager)
{
GtkWidget *recent_menu;
GtkRecentFilter *filter;
recent_menu = gtk_recent_chooser_menu_new_for_manager (manager);
gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (recent_menu), TRUE);
gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (recent_menu), FALSE);
gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU);
gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (recent_menu), TRUE);
filter = gtk_recent_filter_new ();
gtk_recent_filter_add_application (filter, g_get_application_name());
gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter);
return recent_menu;
}
static void
window_screen_changed_cb (GtkWidget *widget,
GdkScreen *old_screen,
GladeWindow *window)
{
GtkWidget *menu_item;
GdkScreen *screen;
screen = gtk_widget_get_screen (widget);
window->priv->recent_manager = gtk_recent_manager_get_for_screen (screen);
gtk_menu_detach (GTK_MENU (window->priv->recent_menu));
g_object_unref (G_OBJECT (window->priv->recent_menu));
window->priv->recent_menu = create_recent_chooser_menu (window, window->priv->recent_manager);
g_signal_connect (window->priv->recent_menu,
"item-activated",
G_CALLBACK (recent_chooser_item_activated_cb),
window);
menu_item = gtk_ui_manager_get_widget (window->priv->ui, "/MenuBar/FileMenu/OpenRecent");
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), window->priv->recent_menu);
}
static void
activate_action (GtkToolButton *toolbutton,
GladeWidgetAction *action)
{
GladeWidget *widget;
if ((widget = g_object_get_data (G_OBJECT (toolbutton), "glade-widget")))
glade_widget_adaptor_action_activate (widget->adaptor,
widget->object,
action->klass->path);
}
static void
action_notify_sensitive (GObject *gobject,
GParamSpec *arg1,
GtkWidget *item)
{
GladeWidgetAction *action = GLADE_WIDGET_ACTION (gobject);
gtk_widget_set_sensitive (item, action->sensitive);
}
static void
action_disconnect (gpointer data, GClosure *closure)
{
g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC,
0, 0, NULL,
action_notify_sensitive,
NULL);
}
static void
clean_actions (GladeWindow *window)
{
GtkContainer *container = GTK_CONTAINER (window->priv->toolbar);
GtkToolbar *bar = GTK_TOOLBAR (window->priv->toolbar);
GtkToolItem *item;
if (window->priv->actions_start)
{
while ((item = gtk_toolbar_get_nth_item (bar, window->priv->actions_start)))
gtk_container_remove (container, GTK_WIDGET (item));
}
}
static void
add_actions (GladeWindow *window,
GladeWidget *widget,
GList *actions)
{
GtkToolbar *bar = GTK_TOOLBAR (window->priv->toolbar);
GtkToolItem *item = gtk_separator_tool_item_new ();
gint n = 0;
GList *l;
gtk_toolbar_insert (bar, item, -1);
gtk_widget_show (GTK_WIDGET (item));
if (window->priv->actions_start == 0)
window->priv->actions_start = gtk_toolbar_get_item_index (bar, item);
for (l = actions; l; l = g_list_next (l))
{
GladeWidgetAction *a = l->data;
if (!a->klass->important) continue;
if (a->actions)
{
g_warning ("Trying to add a group action to the toolbar is unsupported");
continue;
}
item = gtk_tool_button_new_from_stock ((a->klass->stock) ? a->klass->stock : "gtk-execute");
if (a->klass->label)
gtk_tool_button_set_label (GTK_TOOL_BUTTON (item),
a->klass->label);
g_object_set_data (G_OBJECT (item), "glade-widget", widget);
/* We use destroy_data to keep track of notify::sensitive callbacks
* on the action object and disconnect them when the toolbar item
* gets destroyed.
*/
g_signal_connect_data (item, "clicked",
G_CALLBACK (activate_action),
a, action_disconnect, 0);
gtk_widget_set_sensitive (GTK_WIDGET (item), a->sensitive);
g_signal_connect (a, "notify::sensitive",
G_CALLBACK (activate_action),
GTK_WIDGET (item));
gtk_toolbar_insert (bar, item, -1);
gtk_tool_item_set_homogeneous (item, FALSE);
gtk_widget_show (GTK_WIDGET (item));
n++;
}
if (n == 0)
clean_actions (window);
}
static void
project_selection_changed_cb (GladeProject *project, GladeWindow *window)
{
GladeWidget *glade_widget = NULL;
GtkLabel *label;
GList *list;
gchar *text;
gint num;
/* This is sometimes called with a NULL project (to make the label
* insensitive with no projects loaded)
*/
g_return_if_fail (GLADE_IS_WINDOW (window));
label = window->priv->label;
/* Only update the editor if the selection has changed on
* the currently active project.
*/
if (glade_app_get_editor() &&
project && (project == glade_app_get_project ()))
{
gtk_widget_set_sensitive (GTK_WIDGET (label), TRUE);
list = glade_project_selection_get (project);
num = g_list_length (list);
if (num == 1 && !GLADE_IS_PLACEHOLDER (list->data))
{
glade_widget = glade_widget_get_from_gobject (G_OBJECT (list->data));
text = g_strdup_printf (_("%s [%s] - Properties"),
glade_widget_get_name (glade_widget),
G_OBJECT_TYPE_NAME (glade_widget->object));
gtk_label_set_text (label, text);
g_free (text);
clean_actions (window);
if (glade_widget->actions)
add_actions (window, glade_widget, glade_widget->actions);
}
else
{
gtk_label_set_text (label, _("Properties"));
}
}
else if (glade_app_get_editor ())
{
gtk_widget_set_sensitive (GTK_WIDGET (label), FALSE);
gtk_label_set_text (label, _("Properties"));
}
}
static GladeDesignView *
get_active_view (GladeWindow *window)
{
g_return_val_if_fail (GLADE_IS_WINDOW (window), NULL);
return window->priv->active_view;
}
static gchar *
format_project_list_item_tooltip (GladeProject *project)
{
gchar *tooltip, *path, *name;
if (glade_project_get_path (project))
{
path = replace_home_dir_with_tilde (glade_project_get_path (project));
if (glade_project_get_readonly (project))
{
tooltip = g_strdup_printf ("Activate '%s' %s",
path,
READONLY_INDICATOR);
}
else
{
tooltip = g_strdup_printf ("Activate '%s'", path);
}
g_free (path);
}
else
{
name = glade_project_get_name (project);
tooltip = g_strdup_printf ("Activate '%s'", name);
g_free (name);
}
return tooltip;
}
static void
refresh_projects_list_item (GladeWindow *window, GladeProject *project)
{
GtkAction *action;
gchar *project_name;
gchar *tooltip;
/* Get associated action */
action = GTK_ACTION (g_object_get_data (G_OBJECT (project), "project-list-action"));
/* Set action label */
project_name = get_formatted_project_name_for_display (project,
FORMAT_NAME_MARK_UNSAVED |
FORMAT_NAME_ESCAPE_UNDERSCORES |
FORMAT_NAME_MIDDLE_TRUNCATE);
g_object_set (action, "label", project_name, NULL);
/* Set action tooltip */
tooltip = format_project_list_item_tooltip (project);
g_object_set (action, "tooltip", tooltip, NULL);
g_free (tooltip);
g_free (project_name);
}
static void
refresh_next_prev_project_sensitivity (GladeWindow *window)
{
GladeDesignView *view;
GtkAction *action;
gint view_number;
view = get_active_view (window);
if (view != NULL)
{
view_number = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), GTK_WIDGET (view));
g_return_if_fail (view_number >= 0);
action = gtk_action_group_get_action (window->priv->project_actions, "PreviousProject");
gtk_action_set_sensitive (action, view_number != 0);
action = gtk_action_group_get_action (window->priv->project_actions, "NextProject");
gtk_action_set_sensitive (action,
view_number < gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook)) - 1);
}
else
{
action = gtk_action_group_get_action (window->priv->project_actions, "PreviousProject");
gtk_action_set_sensitive (action, FALSE);
action = gtk_action_group_get_action (window->priv->project_actions, "NextProject");
gtk_action_set_sensitive (action, FALSE);
}
}
static void
new_cb (GtkAction *action, GladeWindow *window)
{
glade_window_new_project (window);
}
static void
project_notify_handler_cb (GladeProject *project, GParamSpec *spec, GladeWindow *window)
{
GtkAction *action;
if (strcmp (spec->name, "modified") == 0)
{
refresh_title (window);
refresh_projects_list_item (window, project);
}
else if (strcmp (spec->name, "read-only") == 0)
{
action = gtk_action_group_get_action (window->priv->project_actions, "Save");
gtk_action_set_sensitive (action,
!glade_project_get_readonly (project));
}
else if (strcmp (spec->name, "has-selection") == 0)
{
action = gtk_action_group_get_action (window->priv->project_actions, "Cut");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
action = gtk_action_group_get_action (window->priv->project_actions, "Copy");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
action = gtk_action_group_get_action (window->priv->project_actions, "Delete");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
}
}
static void
clipboard_notify_handler_cb (GladeClipboard *clipboard, GParamSpec *spec, GladeWindow *window)
{
GtkAction *action;
if (strcmp (spec->name, "has-selection") == 0)
{
action = gtk_action_group_get_action (window->priv->project_actions, "Paste");
gtk_action_set_sensitive (action,
glade_clipboard_get_has_selection (clipboard));
}
}
static GtkWidget*
construct_dock_item (GladeWindow *window, const gchar *title, GtkWidget *child)
{
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *alignment;
vbox = gtk_vbox_new (FALSE, 0);
label = gtk_label_new (title);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_misc_set_padding (GTK_MISC (label), 2, 5);
alignment = gtk_alignment_new (0, 0, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 0, 12);
gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (alignment), label);
gtk_box_pack_start (GTK_BOX (vbox), child, TRUE, TRUE, 0);
gtk_widget_show (alignment);
gtk_widget_show (label);
gtk_widget_show (child);
gtk_widget_show (vbox);
/* FIXME: naughty */
g_object_set_data (G_OBJECT (vbox), "dock-label", label);
return vbox;
}
static void
on_selector_button_toggled (GtkToggleToolButton *button, GladeWindow *window)
{
if (window->priv->setting_pointer_mode)
return;
if (gtk_toggle_tool_button_get_active (window->priv->selector_button))
{
glade_palette_deselect_current_item (glade_app_get_palette(), FALSE);
glade_app_set_pointer_mode (GLADE_POINTER_SELECT);
}
else
gtk_toggle_tool_button_set_active (window->priv->selector_button, TRUE);
}
static void
on_drag_resize_button_toggled (GtkToggleToolButton *button, GladeWindow *window)
{
if (window->priv->setting_pointer_mode)
return;
if (gtk_toggle_tool_button_get_active (window->priv->drag_resize_button))
glade_app_set_pointer_mode (GLADE_POINTER_DRAG_RESIZE);
else
gtk_toggle_tool_button_set_active (window->priv->drag_resize_button, TRUE);
}
static void
on_pointer_mode_changed (GladeApp *app,
GParamSpec *pspec,
GladeWindow *window)
{
window->priv->setting_pointer_mode = TRUE;
if (glade_app_get_pointer_mode () == GLADE_POINTER_SELECT)
gtk_toggle_tool_button_set_active (window->priv->selector_button, TRUE);
else
gtk_toggle_tool_button_set_active (window->priv->selector_button, FALSE);
if (glade_app_get_pointer_mode () == GLADE_POINTER_DRAG_RESIZE)
gtk_toggle_tool_button_set_active (window->priv->drag_resize_button, TRUE);
else
gtk_toggle_tool_button_set_active (window->priv->drag_resize_button, FALSE);
window->priv->setting_pointer_mode = FALSE;
}
static void
set_sensitivity_according_to_project (GladeWindow *window, GladeProject *project)
{
GtkAction *action;
action = gtk_action_group_get_action (window->priv->project_actions, "Save");
gtk_action_set_sensitive (action,
!glade_project_get_readonly (project));
action = gtk_action_group_get_action (window->priv->project_actions, "Cut");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
action = gtk_action_group_get_action (window->priv->project_actions, "Copy");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
action = gtk_action_group_get_action (window->priv->project_actions, "Paste");
gtk_action_set_sensitive (action,
glade_clipboard_get_has_selection
(glade_app_get_clipboard ()));
action = gtk_action_group_get_action (window->priv->project_actions, "Delete");
gtk_action_set_sensitive (action,
glade_project_get_has_selection (project));
refresh_next_prev_project_sensitivity (window);
}
static void
recent_add (GladeWindow *window, const gchar *path)
{
GtkRecentData *recent_data;
gchar *uri;
GError *error = NULL;
uri = g_filename_to_uri (path, NULL, &error);
if (error)
{
g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
g_error_free (error);
return;
}
recent_data = g_slice_new (GtkRecentData);
recent_data->display_name = NULL;
recent_data->description = NULL;
recent_data->mime_type = "application/x-glade";
recent_data->app_name = (gchar *) g_get_application_name ();
recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
recent_data->groups = NULL;
recent_data->is_private = FALSE;
gtk_recent_manager_add_full (window->priv->recent_manager,
uri,
recent_data);
g_free (uri);
g_free (recent_data->app_exec);
g_slice_free (GtkRecentData, recent_data);
}
static void
recent_remove (GladeWindow *window, const gchar *path)
{
gchar *uri;
GError *error = NULL;
uri = g_filename_to_uri (path, NULL, &error);
if (error)
{
g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
g_error_free (error);
return;
}
gtk_recent_manager_remove_item (window->priv->recent_manager,
uri,
NULL);
g_free (uri);
}
/* switch to a project and check if we need to reload it.
*
*/
static void
switch_to_project (GladeWindow *window, GladeProject *project)
{
GladeWindowPrivate *priv = window->priv;
guint i, n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
/* increase project popularity */
recent_add (window, glade_project_get_path (project));
update_default_path (window, project);
for (i = 0; i < n; i++)
{
GladeProject *project_i;
GtkWidget *view;
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
project_i = glade_design_view_get_project (GLADE_DESIGN_VIEW (view));
if (project == project_i)
{
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), i);
break;
}
}
check_reload_project (window, project);
}
static void
projects_list_menu_activate_cb (GtkAction *action, GladeWindow *window)
{
gint n;
if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (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 void
refresh_projects_list_menu (GladeWindow *window)
{
GladeWindowPrivate *p = window->priv;
GList *actions, *l;
GSList *group = NULL;
gint n, i;
guint id;
if (p->projects_list_menu_ui_id != 0)
gtk_ui_manager_remove_ui (p->ui, p->projects_list_menu_ui_id);
/* Remove all current actions */
actions = gtk_action_group_list_actions (p->projects_list_menu_actions);
for (l = actions; l != NULL; l = l->next)
{
g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data),
G_CALLBACK (projects_list_menu_activate_cb), window);
gtk_action_group_remove_action (p->projects_list_menu_actions, 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->ui) : 0;
/* Add an action for each project */
for (i = 0; i < n; i++)
{
GtkWidget *view;
GladeProject *project;
GtkRadioAction *action;
gchar *action_name;
gchar *project_name;
gchar *tooltip;
gchar *accel;
view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (p->notebook), i);
project = glade_design_view_get_project (GLADE_DESIGN_VIEW (view));
/* 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);
project_name = get_formatted_project_name_for_display (project,
FORMAT_NAME_MARK_UNSAVED |
FORMAT_NAME_MIDDLE_TRUNCATE |
FORMAT_NAME_ESCAPE_UNDERSCORES);
tooltip = format_project_list_item_tooltip (project);
/* 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,
project_name,
tooltip,
NULL,
i);
/* Link action and project */
g_object_set_data (G_OBJECT (project), "project-list-action", action);
g_object_set_data (G_OBJECT (action), "project", project);
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->projects_list_menu_actions,
GTK_ACTION (action),
accel);
g_signal_connect (action, "activate",
G_CALLBACK (projects_list_menu_activate_cb),
window);
gtk_ui_manager_add_ui (p->ui, id,
"/MenuBar/ProjectMenu/ProjectsListPlaceholder",
action_name, action_name,
GTK_UI_MANAGER_MENUITEM,
FALSE);
if (GLADE_DESIGN_VIEW (view) == p->active_view)
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
g_object_unref (action);
g_free (action_name);
g_free (project_name);
g_free (tooltip);
g_free (accel);
}
p->projects_list_menu_ui_id = id;
}
static void
open_cb (GtkAction *action, GladeWindow *window)
{
GtkWidget *filechooser;
gchar *path = NULL, *default_path;
filechooser = glade_util_file_dialog_new (_("Open\342\200\246"), GTK_WINDOW (window),
GLADE_FILE_DIALOG_ACTION_OPEN);
default_path = g_strdup (get_default_path (window));
if (default_path != NULL)
{
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser), default_path);
g_free (default_path);
}
if (gtk_dialog_run (GTK_DIALOG(filechooser)) == GTK_RESPONSE_OK)
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
gtk_widget_destroy (filechooser);
if (!path)
return;
glade_window_open_project (window, path);
g_free (path);
}
static void
save (GladeWindow *window, GladeProject *project, const gchar *path)
{
GError *error = NULL;
gchar *display_name, *display_path = g_strdup (path);
time_t mtime;
GtkWidget *dialog;
GtkWidget *button;
gint response;
/* check for external modification to the project file */
mtime = glade_util_get_file_mtime (glade_project_get_path (project), NULL);
if (mtime > glade_project_get_file_mtime (project)) {
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
_("The file %s has been modified since reading it"),
glade_project_get_path (project));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("If you save it, all the external changes could be lost. Save it anyway?"));
gtk_window_set_title (GTK_WINDOW (dialog), "");
button = gtk_button_new_with_mnemonic (_("_Save Anyway"));
gtk_button_set_image (GTK_BUTTON (button),
gtk_image_new_from_stock (GTK_STOCK_SAVE,
GTK_ICON_SIZE_BUTTON));
gtk_widget_show (button);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_ACCEPT);
gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Don't Save"), GTK_RESPONSE_REJECT);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_REJECT)
{
g_free (display_path);
return;
}
}
/* Interestingly; we cannot use `path' after glade_project_reset_path
* because we are getting called with glade_project_get_path (project) as an argument.
*/
if (!glade_project_save (project, path, &error))
{
/* Reset path so future saves will prompt the file chooser */
glade_project_reset_path (project);
glade_util_ui_message (GTK_WIDGET (window), GLADE_UI_ERROR,
_("Failed to save %s: %s"),
display_path, error->message);
g_error_free (error);
g_free (display_path);
return;
}
glade_app_update_instance_count (project);
/* Get display_name here, it could have changed with "Save As..." */
display_name = glade_project_get_name (project);
recent_add (window, glade_project_get_path (project));
update_default_path (window, project);
/* refresh names */
refresh_title (window);
refresh_projects_list_item (window, project);
glade_util_flash_message (window->priv->statusbar,
window->priv->statusbar_actions_context_id,
_("Project '%s' saved"), display_name);
g_free (display_path);
g_free (display_name);
}
static void
save_as (GladeWindow *window)
{
GladeProject *project, *another_project;
GtkWidget *filechooser;
GtkWidget *dialog;
gchar *path = NULL;
gchar *real_path, *ch, *project_name;
project = glade_design_view_get_project (window->priv->active_view);
if (project == NULL)
return;
filechooser = glade_util_file_dialog_new (_("Save As\342\200\246"),
GTK_WINDOW (window),
GLADE_FILE_DIALOG_ACTION_SAVE);
if (glade_project_get_path (project))
{
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filechooser), glade_project_get_path (project));
}
else
{
gchar *default_path = g_strdup (get_default_path (window));
if (default_path != NULL)
{
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser), default_path);
g_free (default_path);
}
project_name = glade_project_get_name (project);
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filechooser), project_name);
g_free (project_name);
}
if (gtk_dialog_run (GTK_DIALOG(filechooser)) == GTK_RESPONSE_OK)
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
gtk_widget_destroy (filechooser);
if (!path)
return;
ch = strrchr (path, '.');
if (!ch || strchr (ch, G_DIR_SEPARATOR))
real_path = g_strconcat (path, ".glade", NULL);
else
real_path = g_strdup (path);
g_free (path);
/* checks if selected path is actually writable */
if (glade_util_file_is_writeable (real_path) == FALSE)
{
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("Could not save the file %s"),
real_path);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("You do not have the permissions "
"necessary to save the file."));
gtk_window_set_title (GTK_WINDOW (dialog), "");
g_signal_connect_swapped (dialog, "response",
G_CALLBACK (gtk_widget_destroy),
dialog);
gtk_widget_show (dialog);
g_free (real_path);
return;
}
/* checks if another open project is using selected path */
if ((another_project = glade_app_get_project_by_path (real_path)) != NULL)
{
if (project != another_project) {
glade_util_ui_message (GTK_WIDGET (window),
GLADE_UI_ERROR,
_("Could not save file %s. Another project with that path is open."),
real_path);
g_free (real_path);
return;
}
}
save (window, project, real_path);
g_free (real_path);
}
static void
save_cb (GtkAction *action, GladeWindow *window)
{
GladeProject *project;
project = glade_design_view_get_project (window->priv->active_view);
if (project == NULL)
{
/* Just in case the menu-item or button is not insensitive */
glade_util_ui_message (GTK_WIDGET (window), GLADE_UI_WARN, _("No open projects to save"));
return;
}
if (glade_project_get_path (project) != NULL)
{
save (window, project, glade_project_get_path (project));
return;
}
/* If instead we dont have a path yet, fire up a file selector */
save_as (window);
}
static void
save_as_cb (GtkAction *action, GladeWindow *window)
{
save_as (window);
}
static gboolean
confirm_close_project (GladeWindow *window, GladeProject *project)
{
GtkWidget *dialog;
gboolean close = FALSE;
gchar *msg, *project_name = NULL;
gint ret;
GError *error = NULL;
project_name = glade_project_get_name (project);
msg = g_strdup_printf (_("<span weight=\"bold\" size=\"larger\">Save changes "
"to project \"%s\" before closing?</span>\n\n"
"Your changes will be lost if you don't save them."), project_name);
dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
msg);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("_Close without Saving"), GTK_RESPONSE_NO,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_YES,
GTK_RESPONSE_CANCEL,
GTK_RESPONSE_NO,
-1);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
ret = gtk_dialog_run (GTK_DIALOG (dialog));
switch (ret) {
case GTK_RESPONSE_YES:
/* if YES we save the project: note we cannot use save_cb
* since it saves the current project, while the modified
* project we are saving may be not the current one.
*/
if (glade_project_get_path (project) != NULL)
{
if ((close = glade_project_save
(project, glade_project_get_path (project), &error)) == FALSE)
{
glade_util_ui_message
(GTK_WIDGET (window), GLADE_UI_ERROR,
_("Failed to save %s to %s: %s"),
project_name, glade_project_get_path (project), error->message);
g_error_free (error);
}
}
else
{
GtkWidget *filechooser;
gchar *path = NULL;
gchar *default_path;
filechooser =
glade_util_file_dialog_new (_("Save\342\200\246"),
GTK_WINDOW (window),
GLADE_FILE_DIALOG_ACTION_SAVE);
default_path = g_strdup (get_default_path (window));
if (default_path != NULL)
{
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser), default_path);
g_free (default_path);
}
gtk_file_chooser_set_current_name
(GTK_FILE_CHOOSER (filechooser), project_name);
if (gtk_dialog_run (GTK_DIALOG(filechooser)) == GTK_RESPONSE_OK)
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
gtk_widget_destroy (filechooser);
if (!path)
break;
save (window, project, path);
g_free (path);
close = FALSE;
}
break;
case GTK_RESPONSE_NO:
close = TRUE;
break;
case GTK_RESPONSE_CANCEL:
case GTK_RESPONSE_DELETE_EVENT:
close = FALSE;
break;
default:
g_assert_not_reached ();
close = FALSE;
}
g_free (msg);
g_free (project_name);
gtk_widget_destroy (dialog);
return close;
}
static void
do_close (GladeWindow *window, GladeDesignView *view)
{
gint n;
n = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), GTK_WIDGET (view));
g_object_ref (view);
gtk_notebook_remove_page (GTK_NOTEBOOK (window->priv->notebook), n);
g_object_unref (view);
}
static void
close_cb (GtkAction *action, GladeWindow *window)
{
GladeDesignView *view;
GladeProject *project;
gboolean close;
view = window->priv->active_view;
project = glade_design_view_get_project (view);
if (view == NULL)
return;
if (glade_project_get_modified (project))
{
close = confirm_close_project (window, project);
if (!close)
return;
}
do_close (window, view);
}
static void
quit_cb (GtkAction *action, GladeWindow *window)
{
GList *list;
for (list = glade_app_get_projects (); list; list = list->next)
{
GladeProject *project = GLADE_PROJECT (list->data);
if (glade_project_get_modified (project))
{
gboolean quit = confirm_close_project (window, project);
if (!quit)
return;
}
}
while (glade_app_get_projects ())
{
GladeProject *project = GLADE_PROJECT (glade_app_get_projects ()->data);
do_close (window, glade_design_view_get_from_project (project));
}
save_windows_config (window);
gtk_main_quit ();
}
static void
copy_cb (GtkAction *action, GladeWindow *window)
{
glade_app_command_copy ();
}
static void
cut_cb (GtkAction *action, GladeWindow *window)
{
glade_app_command_cut ();
}
static void
paste_cb (GtkAction *action, GladeWindow *window)
{
glade_app_command_paste (NULL);
}
static void
delete_cb (GtkAction *action, GladeWindow *window)
{
if (!glade_app_get_project ())
{
g_warning ("delete should not be sensitive: we don't have a project");
return;
}
glade_app_command_delete ();
}
static void
undo_cb (GtkAction *action, GladeWindow *window)
{
if (!glade_app_get_project ())
{
g_warning ("undo should not be sensitive: we don't have a project");
return;
}
glade_app_command_undo ();
}
static void
redo_cb (GtkAction *action, GladeWindow *window)
{
if (!glade_app_get_project ())
{
g_warning ("redo should not be sensitive: we don't have a project");
return;
}
glade_app_command_redo ();
}
static gboolean
hide_window_on_delete (GtkWidget *window, gpointer not_used, GtkUIManager *ui)
{
glade_util_hide_window (GTK_WINDOW (window));
return TRUE;
}
static void
doc_search_cb (GladeEditor *editor,
const gchar *book,
const gchar *page,
const gchar *search,
GladeWindow *window)
{
glade_util_search_devhelp (book, page, search);
}
static void
previous_project_cb (GtkAction *action, GladeWindow *window)
{
gtk_notebook_prev_page (GTK_NOTEBOOK (window->priv->notebook));
}
static void
next_project_cb (GtkAction *action, GladeWindow *window)
{
gtk_notebook_next_page (GTK_NOTEBOOK (window->priv->notebook));
}
static void
inspector_item_activated_cb (GladeInspector *inspector,
GladeWindow *window)
{
GList *item = glade_inspector_get_selected_items (inspector);
g_assert (GLADE_IS_WIDGET (item->data) && (item->next == NULL));
/* bring window on top since inspector may be detached */
gtk_window_present (GTK_WINDOW (window));
/* switch to this widget in the workspace */
glade_widget_show (GLADE_WIDGET (item->data));
g_list_free (item);
}
static void
notebook_switch_page_cb (GtkNotebook *notebook,
GtkNotebookPage *page,
guint page_num,
GladeWindow *window)
{
GladeDesignView *view;
GladeProject *project;
GtkAction *action;
gchar *action_name;
view = GLADE_DESIGN_VIEW (gtk_notebook_get_nth_page (notebook, page_num));
/* CHECK: I don't know why but it seems notebook_switch_page is called
two times every time the user change the active tab */
if (view == window->priv->active_view)
return;
window->priv->active_view = view;
project = glade_design_view_get_project (view);
/* FIXME: this does not feel good */
glade_app_set_project (project);
refresh_title (window);
set_sensitivity_according_to_project (window, project);
/* switch to the project's inspector */
gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), page_num);
/* activate the corresponding item in the project menu */
action_name = g_strdup_printf ("Tab_%d", page_num);
action = gtk_action_group_get_action (window->priv->projects_list_menu_actions,
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_view 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);
}
static void
notebook_tab_added_cb (GtkNotebook *notebook,
GladeDesignView *view,
guint page_num,
GladeWindow *window)
{
GladeProject *project;
GtkWidget *inspector;
++window->priv->num_tabs;
project = glade_design_view_get_project (view);
g_signal_connect (G_OBJECT (project), "notify::modified",
G_CALLBACK (project_notify_handler_cb),
window);
g_signal_connect (G_OBJECT (project), "selection-changed",
G_CALLBACK (project_selection_changed_cb), window);
g_signal_connect (G_OBJECT (project), "notify::has-selection",
G_CALLBACK (project_notify_handler_cb),
window);
g_signal_connect (G_OBJECT (project), "notify::read-only",
G_CALLBACK (project_notify_handler_cb),
window);
/* create inspector */
inspector = glade_inspector_new ();
gtk_widget_show (inspector);
glade_inspector_set_project (GLADE_INSPECTOR (inspector), project);
g_signal_connect (inspector, "item-activated",
G_CALLBACK (inspector_item_activated_cb),
window);
gtk_notebook_append_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), inspector, NULL);
set_sensitivity_according_to_project (window, project);
refresh_projects_list_menu (window);
refresh_title (window);
project_selection_changed_cb (glade_app_get_project (), window);
if (window->priv->num_tabs > 0)
gtk_action_group_set_sensitive (window->priv->project_actions, TRUE);
}
static void
notebook_tab_removed_cb (GtkNotebook *notebook,
GladeDesignView *view,
guint page_num,
GladeWindow *window)
{
GladeProject *project;
GladeInspector *inspector;
--window->priv->num_tabs;
if (window->priv->num_tabs == 0)
window->priv->active_view = NULL;
project = glade_design_view_get_project (view);
g_signal_handlers_disconnect_by_func (G_OBJECT (project),
G_CALLBACK (project_notify_handler_cb),
window);
g_signal_handlers_disconnect_by_func (G_OBJECT (project),
G_CALLBACK (project_selection_changed_cb),
window);
/* FIXME: this function needs to be preferably called somewhere else */
glade_app_remove_project (project);
inspector = (GladeInspector *) gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), page_num);
gtk_notebook_remove_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), page_num);
refresh_projects_list_menu (window);
refresh_title (window);
project_selection_changed_cb (glade_app_get_project (), window);
if (window->priv->active_view)
set_sensitivity_according_to_project (window, glade_design_view_get_project (window->priv->active_view));
else
gtk_action_group_set_sensitive (window->priv->project_actions, FALSE);
}
static void
recent_chooser_item_activated_cb (GtkRecentChooser *chooser, GladeWindow *window)
{
gchar *uri, *path;
GError *error = NULL;
uri = gtk_recent_chooser_get_current_uri (chooser);
path = g_filename_from_uri (uri, NULL, NULL);
if (error)
{
g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
g_error_free (error);
return;
}
glade_window_open_project (window, path);
g_free (uri);
g_free (path);
}
static void
palette_appearance_change_cb (GtkRadioAction *action,
GtkRadioAction *current,
GladeWindow *window)
{
gint value;
value = gtk_radio_action_get_current_value (action);
glade_palette_set_item_appearance (glade_app_get_palette (), value);
}
static void
palette_toggle_small_icons_cb (GtkAction *action, GladeWindow *window)
{
glade_palette_set_use_small_item_icons (glade_app_get_palette(),
gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
}
static void
show_clipboard_cb (GtkAction *action, GladeWindow *window)
{
static GtkWidget *view = NULL;
g_return_if_fail (window != NULL);
if (view == NULL)
{
view = glade_app_get_clipboard_view ();
g_signal_connect (view, "delete_event",
G_CALLBACK (hide_window_on_delete),
window->priv->ui);
gtk_widget_show_all (view);
}
gtk_window_present (GTK_WINDOW (view));
}
static void
toggle_editor_help_cb (GtkAction *action, GladeWindow *window)
{
if (glade_util_have_devhelp() == FALSE)
return;
if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
glade_editor_show_context_info (glade_app_get_editor ());
else
glade_editor_hide_context_info (glade_app_get_editor ());
}
/* This function is meant to be attached to key-press-event of a toplevel,
* it simply allows the window contents to treat key events /before/
* accelerator keys come into play (this way widgets dont get deleted
* when cutting text in an entry etc.).
*/
static gint
hijack_key_press (GtkWidget *win,
GdkEventKey *event,
GladeWindow *window)
{
if (GTK_WINDOW (win)->focus_widget &&
(event->keyval == GDK_Delete || /* Filter Delete from accelerator keys */
((event->state & GDK_CONTROL_MASK) && /* CNTL keys... */
((event->keyval == GDK_c || event->keyval == GDK_C) || /* CNTL-C (copy) */
(event->keyval == GDK_x || event->keyval == GDK_X) || /* CNTL-X (cut) */
(event->keyval == GDK_v || event->keyval == GDK_V))))) /* CNTL-V (paste) */
{
return gtk_widget_event (GTK_WINDOW (win)->focus_widget,
(GdkEvent *)event);
}
return FALSE;
}
static gboolean
on_dock_deleted (GtkWidget *widget,
GdkEvent *event,
GtkAction *dock_action)
{
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (dock_action), TRUE);
return TRUE;
}
static gboolean
on_dock_resized (GtkWidget *window,
GdkEventConfigure *event,
ToolDock *dock)
{
dock->window_pos.width = event->width;
dock->window_pos.height = event->height;
gtk_window_get_position (GTK_WINDOW (window),
&dock->window_pos.x,
&dock->window_pos.y);
return FALSE;
}
static void
toggle_dock_cb (GtkAction *action, GladeWindow *window)
{
GtkWidget *toplevel;
ToolDock *dock;
guint dock_type;
dock_type = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (action), "glade-dock-type"));
g_return_if_fail (dock_type < N_DOCKS);
dock = &window->priv->docks[dock_type];
if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
{
toplevel = gtk_widget_get_toplevel (dock->widget);
g_object_ref (dock->widget);
gtk_container_remove (GTK_CONTAINER (toplevel), dock->widget);
if (dock->first_child)
gtk_paned_pack1 (GTK_PANED (dock->paned), dock->widget, FALSE, FALSE);
else
gtk_paned_pack2 (GTK_PANED (dock->paned), dock->widget, FALSE, FALSE);
g_object_unref (dock->widget);
gtk_widget_show (dock->paned);
dock->detached = FALSE;
gtk_widget_destroy (toplevel);
} else {
toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (toplevel),
dock->window_pos.width,
dock->window_pos.height);
if (dock->window_pos.x > G_MININT && dock->window_pos.y > G_MININT)
gtk_window_move (GTK_WINDOW (toplevel),
dock->window_pos.x,
dock->window_pos.y);
gtk_window_set_title (GTK_WINDOW (toplevel), dock->title);
g_object_ref (dock->widget);
gtk_container_remove (GTK_CONTAINER (dock->paned), dock->widget);
gtk_container_add (GTK_CONTAINER (toplevel), dock->widget);
g_object_unref (dock->widget);
g_signal_connect (G_OBJECT (toplevel), "delete-event",
G_CALLBACK (on_dock_deleted), action);
g_signal_connect (G_OBJECT (toplevel), "configure-event",
G_CALLBACK (on_dock_resized), dock);
if (!GTK_PANED (dock->paned)->child1 &&
!GTK_PANED (dock->paned)->child2)
gtk_widget_hide (dock->paned);
gtk_window_add_accel_group (GTK_WINDOW (toplevel),
gtk_ui_manager_get_accel_group (window->priv->ui));
g_signal_connect (G_OBJECT (toplevel), "key-press-event",
G_CALLBACK (hijack_key_press), window);
dock->detached = TRUE;
gtk_window_present (GTK_WINDOW (toplevel));
}
}
static void
show_help_cb (GtkAction *action, GladeWindow *window)
{
GtkWidget *dialog;
gboolean retval;
retval = help_show (NULL);
if (retval)
return;
/* fallback to displaying online user manual */
retval = glade_util_url_show (URL_USER_MANUAL);
if (!retval)
{
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("Could not display the online user manual"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("No suitable web browser executable could be found "
"to be executed and to display the URL: %s"),
URL_USER_MANUAL);
gtk_window_set_title (GTK_WINDOW (dialog), "");
g_signal_connect_swapped (dialog, "response",
G_CALLBACK (gtk_widget_destroy),
dialog);
gtk_widget_show (dialog);
}
}
static void
show_developer_manual_cb (GtkAction *action, GladeWindow *window)
{
GtkWidget *dialog;
gboolean retval;
if (glade_util_have_devhelp ())
{
glade_util_search_devhelp ("gladeui", NULL, NULL);
return;
}
retval = glade_util_url_show (URL_DEVELOPER_MANUAL);
if (!retval)
{
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("Could not display the online developer reference manual"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("No suitable web browser executable could be found "
"to be executed and to display the URL: %s"),
URL_DEVELOPER_MANUAL);
gtk_window_set_title (GTK_WINDOW (dialog), "");
g_signal_connect_swapped (dialog, "response",
G_CALLBACK (gtk_widget_destroy),
dialog);
gtk_widget_show (dialog);
}
}
static void
about_cb (GtkAction *action, GladeWindow *window)
{
static const gchar * const authors[] =
{ "Chema Celorio <chema@ximian.com>",
"Joaquin Cuenca Abela <e98cuenc@yahoo.com>",
"Paolo Borelli <pborelli@katamail.com>",
"Archit Baweja <bighead@users.sourceforge.net>",
"Shane Butler <shane_b@operamail.com>",
"Tristan Van Berkom <tvb@gnome.org>",
"Ivan Wong <email@ivanwong.info>",
"Juan Pablo Ugarte <juanpablougarte@gmail.com>",
"Vincent Geddes <vincent.geddes@gmail.com>",
NULL };
static const gchar * const artists[] =
{ "Vincent Geddes <vgeddes@gnome.org>",
"Andreas Nilsson <andreas@andreasn.se>",
NULL };
static const gchar * const documenters[] =
{ "GNOME Documentation Team <gnome-doc-list@gnome.org>",
"Sun GNOME Documentation Team <gdocteam@sun.com>",
NULL };
static const gchar license[] =
N_("Glade 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."
"\n\n"
"Glade 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."
"\n\n"
"You should have received a copy of the GNU General Public License "
"along with Glade; if not, write to the Free Software "
"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, "
"MA 02110-1301, USA.");
static const gchar copyright[] =
"Copyright \xc2\xa9 2001-2006 Ximian, Inc.\n"
"Copyright \xc2\xa9 2001-2006 Joaquin Cuenca Abela, Paolo Borelli, et al.\n"
"Copyright \xc2\xa9 2001-2006 Tristan Van Berkom, Juan Pablo Ugarte, et al.";
gtk_show_about_dialog (GTK_WINDOW (window),
"name", g_get_application_name (),
"logo-icon-name", "glade-3",
"authors", authors,
"artists", artists,
"documenters", documenters,
"translator-credits", _("translator-credits"),
"comments", _("A user interface designer for GTK+ and GNOME."),
"license", _(license),
"wrap-license", TRUE,
"copyright", copyright,
"version", PACKAGE_VERSION,
"website", "http://glade.gnome.org",
NULL);
}
static const gchar ui_info[] =
"<ui>"
" <menubar name='MenuBar'>"
" <menu action='FileMenu'>"
" <menuitem action='New'/>"
" <menuitem action='Open'/>"
" <menuitem action='OpenRecent'/>"
" <separator/>"
" <menuitem action='Save'/>"
" <menuitem action='SaveAs'/>"
" <separator/>"
" <menuitem action='Close'/>"
" <menuitem action='Quit'/>"
" </menu>"
" <menu action='EditMenu'>"
" <menuitem action='Undo'/>"
" <menuitem action='Redo'/>"
" <separator/>"
" <menuitem action='Cut'/>"
" <menuitem action='Copy'/>"
" <menuitem action='Paste'/>"
" <menuitem action='Delete'/>"
" </menu>"
" <menu action='ViewMenu'>"
" <menuitem action='Clipboard'/>"
" <separator/>"
" <menu action='PaletteAppearance'>"
" <menuitem action='IconsAndLabels'/>"
" <menuitem action='IconsOnly'/>"
" <menuitem action='LabelsOnly'/>"
" <separator/>"
" <menuitem action='UseSmallIcons'/>"
" </menu>"
" <menuitem action='PropertyEditorHelp'/>"
" <separator/>"
" <menuitem action='DockPalette'/>"
" <menuitem action='DockInspector'/>"
" <menuitem action='DockEditor'/>"
" </menu>"
" <menu action='ProjectMenu'>"
" <menuitem action='PreviousProject'/>"
" <menuitem action='NextProject'/>"
" <separator/>"
" <placeholder name='ProjectsListPlaceholder'/>"
" </menu>"
" <menu action='HelpMenu'>"
" <menuitem action='HelpContents'/>"
" <menuitem action='DeveloperReference'/>"
" <separator/>"
" <menuitem action='About'/>"
" </menu>"
" </menubar>"
" <toolbar name='ToolBar'>"
" <toolitem action='New'/>"
" <toolitem action='Open'/>"
" <toolitem action='Save'/>"
" <separator/>"
" <toolitem action='Cut'/>"
" <toolitem action='Copy'/>"
" <toolitem action='Paste'/>"
" </toolbar>"
"</ui>";
static GtkActionEntry static_entries[] = {
{ "FileMenu", NULL, N_("_File") },
{ "EditMenu", NULL, N_("_Edit") },
{ "ViewMenu", NULL, N_("_View") },
{ "ProjectMenu", NULL, N_("_Projects") },
{ "HelpMenu", NULL, N_("_Help") },
{ "UndoMenu", NULL, NULL },
{ "RedoMenu", NULL, NULL },
/* FileMenu */
{ "New", GTK_STOCK_NEW, NULL, "<control>N",
N_("Create a new project"), G_CALLBACK (new_cb) },
{ "Open", GTK_STOCK_OPEN, N_("_Open\342\200\246") ,"<control>O",
N_("Open a project"), G_CALLBACK (open_cb) },
{ "OpenRecent", NULL, N_("Open _Recent") },
{ "Quit", GTK_STOCK_QUIT, NULL, "<control>Q",
N_("Quit the program"), G_CALLBACK (quit_cb) },
/* ViewMenu */
{ "PaletteAppearance", NULL, N_("Palette _Appearance") },
/* HelpMenu */
{ "About", GTK_STOCK_ABOUT, NULL, NULL,
N_("About this application"), G_CALLBACK (about_cb) },
{ "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",
N_("Display the user manual"), G_CALLBACK (show_help_cb) },
{ "DeveloperReference", NULL, N_("_Developer Reference"), NULL,
N_("Display the developer reference manual"), G_CALLBACK (show_developer_manual_cb) }
};
static guint n_static_entries = G_N_ELEMENTS (static_entries);
static GtkActionEntry project_entries[] = {
/* FileMenu */
{ "Save", GTK_STOCK_SAVE, NULL, "<control>S",
N_("Save the current project"), G_CALLBACK (save_cb) },
{ "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As\342\200\246"), NULL,
N_("Save the current project with a different name"), G_CALLBACK (save_as_cb) },
{ "Close", GTK_STOCK_CLOSE, NULL, "<control>W",
N_("Close the current project"), G_CALLBACK (close_cb) },
/* EditMenu */
{ "Undo", GTK_STOCK_UNDO, NULL, "<control>Z",
N_("Undo the last action"), G_CALLBACK (undo_cb) },
{ "Redo", GTK_STOCK_REDO, NULL, "<shift><control>Z",
N_("Redo the last action"), G_CALLBACK (redo_cb) },
{ "Cut", GTK_STOCK_CUT, NULL, NULL,
N_("Cut the selection"), G_CALLBACK (cut_cb) },
{ "Copy", GTK_STOCK_COPY, NULL, NULL,
N_("Copy the selection"), G_CALLBACK (copy_cb) },
{ "Paste", GTK_STOCK_PASTE, NULL, NULL,
N_("Paste the clipboard"), G_CALLBACK (paste_cb) },
{ "Delete", GTK_STOCK_DELETE, NULL, "Delete",
N_("Delete the selection"), G_CALLBACK (delete_cb) },
/* ViewMenu */
{ "Clipboard", NULL, N_("_Clipboard"), NULL,
N_("Show the clipboard"),
G_CALLBACK (show_clipboard_cb) },
/* ProjectsMenu */
{ "PreviousProject", NULL, N_("_Previous Project"), "<control>Page_Up",
N_("Activate previous project"), G_CALLBACK (previous_project_cb) },
{ "NextProject", NULL, N_("_Next Project"), "<control>Page_Down",
N_("Activate next project"), G_CALLBACK (next_project_cb) }
};
static guint n_project_entries = G_N_ELEMENTS (project_entries);
static GtkToggleActionEntry view_entries[] = {
{ "UseSmallIcons", NULL, N_("_Use Small Icons"), NULL,
N_("Show items using small icons"),
G_CALLBACK (palette_toggle_small_icons_cb), FALSE },
{ "PropertyEditorHelp", NULL, N_("Context _Help"), NULL,
N_("Show or hide contextual help buttons in the editor"),
G_CALLBACK (toggle_editor_help_cb), FALSE },
{ "DockPalette", NULL, N_("Dock _Palette"), NULL,
N_("Dock the palette into the main window"),
G_CALLBACK (toggle_dock_cb), TRUE },
{ "DockInspector", NULL, N_("Dock _Inspector"), NULL,
N_("Dock the inspector into the main window"),
G_CALLBACK (toggle_dock_cb), TRUE },
{ "DockEditor", NULL, N_("Dock _Editor"), NULL,
N_("Dock the editor into the main window"),
G_CALLBACK (toggle_dock_cb), TRUE },
};
static guint n_view_entries = G_N_ELEMENTS (view_entries);
static GtkRadioActionEntry radio_entries[] = {
{ "IconsAndLabels", NULL, N_("Text beside icons"), NULL,
N_("Display items as text beside icons"), GLADE_ITEM_ICON_AND_LABEL },
{ "IconsOnly", NULL, N_("_Icons only"), NULL,
N_("Display items as icons only"), GLADE_ITEM_ICON_ONLY },
{ "LabelsOnly", NULL, N_("_Text only"), NULL,
N_("Display items as text only"), GLADE_ITEM_LABEL_ONLY },
};
static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
static void
menu_item_selected_cb (GtkItem *item, GladeWindow *window)
{
GtkAction *action;
gchar *tooltip;
action = GTK_ACTION (g_object_get_data (G_OBJECT (item), "action-for-proxy"));
g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
if (tooltip != NULL)
gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar),
window->priv->statusbar_menu_context_id, tooltip);
g_free (tooltip);
}
static void
menu_item_deselected_cb (GtkItem *item, GladeWindow *window)
{
gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
window->priv->statusbar_menu_context_id);
}
static void
ui_connect_proxy_cb (GtkUIManager *ui,
GtkAction *action,
GtkWidget *proxy,
GladeWindow *window)
{
if (GTK_IS_MENU_ITEM (proxy))
{
g_object_set_data (G_OBJECT (proxy), "action-for-proxy", action);
g_signal_connect(G_OBJECT(proxy), "select",
G_CALLBACK (menu_item_selected_cb), window);
g_signal_connect(G_OBJECT(proxy), "deselect",
G_CALLBACK (menu_item_deselected_cb), window);
}
}
static void
ui_disconnect_proxy_cb (GtkUIManager *manager,
GtkAction *action,
GtkWidget *proxy,
GladeWindow *window)
{
if (GTK_IS_MENU_ITEM (proxy))
{
g_signal_handlers_disconnect_by_func
(proxy, G_CALLBACK (menu_item_selected_cb), window);
g_signal_handlers_disconnect_by_func
(proxy, G_CALLBACK (menu_item_deselected_cb), window);
}
}
static GtkWidget *
construct_menu (GladeWindow *window)
{
GError *error = NULL;
window->priv->static_actions = gtk_action_group_new (ACTION_GROUP_STATIC);
gtk_action_group_set_translation_domain (window->priv->static_actions, GETTEXT_PACKAGE);
gtk_action_group_add_actions (window->priv->static_actions,
static_entries,
n_static_entries,
window);
gtk_action_group_add_toggle_actions (window->priv->static_actions,
view_entries,
n_view_entries,
window);
gtk_action_group_add_radio_actions (window->priv->static_actions, radio_entries,
n_radio_entries, GLADE_ITEM_ICON_ONLY,
G_CALLBACK (palette_appearance_change_cb), window);
window->priv->project_actions = gtk_action_group_new (ACTION_GROUP_PROJECT);
gtk_action_group_set_translation_domain (window->priv->project_actions, GETTEXT_PACKAGE);
gtk_action_group_add_actions (window->priv->project_actions,
project_entries,
n_project_entries,
window);
window->priv->projects_list_menu_actions =
gtk_action_group_new (ACTION_GROUP_PROJECTS_LIST_MENU);
gtk_action_group_set_translation_domain (window->priv->projects_list_menu_actions,
GETTEXT_PACKAGE);
window->priv->ui = gtk_ui_manager_new ();
g_signal_connect(G_OBJECT(window->priv->ui), "connect-proxy",
G_CALLBACK (ui_connect_proxy_cb), window);
g_signal_connect(G_OBJECT(window->priv->ui), "disconnect-proxy",
G_CALLBACK (ui_disconnect_proxy_cb), window);
gtk_ui_manager_insert_action_group (window->priv->ui, window->priv->static_actions, 0);
gtk_ui_manager_insert_action_group (window->priv->ui, window->priv->project_actions, 1);
gtk_ui_manager_insert_action_group (window->priv->ui, window->priv->projects_list_menu_actions, 3);
gtk_window_add_accel_group (GTK_WINDOW (window),
gtk_ui_manager_get_accel_group (window->priv->ui));
if (!gtk_ui_manager_add_ui_from_string (window->priv->ui, ui_info, -1, &error))
{
g_message ("Building menus failed: %s", error->message);
g_error_free (error);
}
return gtk_ui_manager_get_widget (window->priv->ui, "/MenuBar");
}
enum
{
TARGET_URI_LIST
};
static GtkTargetEntry drop_types[] =
{
{"text/uri-list", 0, TARGET_URI_LIST}
};
static void
drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x, gint y,
GtkSelectionData *selection_data,
guint info, guint time, GladeWindow *window)
{
gchar **uris, **str;
gchar *data;
if (info != TARGET_URI_LIST)
return;
/* On MS-Windows, it looks like `selection_data->data' is not NULL terminated. */
data = g_new (gchar, selection_data->length + 1);
memcpy (data, selection_data->data, selection_data->length);
data[selection_data->length] = 0;
uris = g_uri_list_extract_uris (data);
for (str = uris; *str; str++)
{
GError *error = NULL;
gchar *path = g_filename_from_uri (*str, NULL, &error);
if (path)
{
glade_window_open_project (window, path);
}
else
{
g_warning ("Could not convert uri to local path: %s", error->message);
g_error_free (error);
}
g_free (path);
}
g_strfreev (uris);
}
static gboolean
delete_event (GtkWindow *w, GdkEvent *event, GladeWindow *window)
{
quit_cb (NULL, window);
/* return TRUE to stop other handlers */
return TRUE;
}
static GtkWidget *
create_selector_tool_button (GtkToolbar *toolbar)
{
GtkToolItem *button;
GtkWidget *image;
gchar *image_path;
image_path = g_build_filename (glade_app_get_pixmaps_dir (), "selector.png", NULL);
image = gtk_image_new_from_file (image_path);
g_free (image_path);
button = gtk_toggle_tool_button_new ();
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (button), image);
gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Select"));
gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (button),
toolbar->tooltips,
_("Select widgets in the workspace"),
NULL);
gtk_widget_show (GTK_WIDGET (button));
gtk_widget_show (image);
return GTK_WIDGET (button);
}
static GtkWidget *
create_drag_resize_tool_button (GtkToolbar *toolbar)
{
GtkToolItem *button;
GtkWidget *image;
gchar *image_path;
image_path = g_build_filename (glade_app_get_pixmaps_dir (), "drag-resize.png", NULL);
image = gtk_image_new_from_file (image_path);
g_free (image_path);
button = gtk_toggle_tool_button_new ();
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (button), image);
gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Drag Resize"));
gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (button),
toolbar->tooltips,
_("Drag and resize widgets in the workspace"),
NULL);
gtk_widget_show (GTK_WIDGET (button));
gtk_widget_show (image);
return GTK_WIDGET (button);
}
static void
add_project (GladeWindow *window, GladeProject *project)
{
GtkWidget *view;
g_return_if_fail (GLADE_IS_PROJECT (project));
view = glade_design_view_new (project);
gtk_widget_show (view);
glade_app_add_project (project);
gtk_notebook_append_page (GTK_NOTEBOOK (window->priv->notebook), GTK_WIDGET (view), NULL);
gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), -1);
}
void
glade_window_new_project (GladeWindow *window)
{
GladeProject *project;
g_return_if_fail (GLADE_IS_WINDOW (window));
project = glade_project_new ();
if (!project)
{
glade_util_ui_message (GTK_WIDGET (window),
GLADE_UI_ERROR,
_("Could not create a new project."));
return;
}
add_project (window, project);
}
static gboolean
open_project (GladeWindow *window, const gchar *path)
{
GladeProject *project;
project = glade_project_load (path);
if (!project)
{
recent_remove (window, path);
return FALSE;
}
add_project (window, project);
/* increase project popularity */
recent_add (window, glade_project_get_path (project));
update_default_path (window, project);
return TRUE;
}
static void
check_reload_project (GladeWindow *window, GladeProject *project)
{
GladeDesignView *view;
gchar *path;
gint ret;
GtkWidget *dialog;
GtkWidget *button;
gint response;
/* Reopen the project if it has external modifications.
* Prompt for permission to reopen.
*/
if ((glade_util_get_file_mtime (glade_project_get_path (project), NULL)
<= glade_project_get_file_mtime (project)))
{
return;
}
if (glade_project_get_modified (project))
{
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
_("The project %s has unsaved changes"),
glade_project_get_path (project));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("If you reload it, all unsaved changes "
"could be lost. Reload it anyway?"));
}
else
{
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
_("The project file %s has been externally modified"),
glade_project_get_path (project));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("Do you want to reload the project?"));
}
gtk_window_set_title (GTK_WINDOW (dialog), "");
button = gtk_button_new_with_mnemonic (_("_Reload"));
gtk_button_set_image (GTK_BUTTON (button),
gtk_image_new_from_stock (GTK_STOCK_REFRESH,
GTK_ICON_SIZE_BUTTON));
gtk_widget_show (button);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_ACCEPT);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_ACCEPT,
GTK_RESPONSE_REJECT,
-1);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_REJECT)
{
return;
}
/* Reopen */
view = glade_design_view_get_from_project (project);
path = g_strdup (glade_project_get_path (project));
do_close (window, view);
ret = open_project (window, path);
g_free (path);
}
/**
* glade_window_open_project:
* @window: a #GladeWindow
* @path: the filesystem path of the project
*
* Opens a project file. If the project is already open, switch to that
* project.
*
* Returns: #TRUE if the project was opened
*/
gboolean
glade_window_open_project (GladeWindow *window,
const gchar *path)
{
GladeProject *project;
g_return_val_if_fail (GLADE_IS_WINDOW (window), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
/* dont allow a project to be opened twice */
project = glade_app_get_project_by_path (path);
if (project)
{
/* just switch to the project */
switch_to_project (window, project);
return TRUE;
}
else
{
return open_project (window, path);
}
}
static void
change_menu_label (GladeWindow *window,
const gchar *path,
const gchar *action_label,
const gchar *action_description)
{
GtkBin *bin;
GtkLabel *label;
gchar *text;
g_assert (GLADE_IS_WINDOW (window));
g_return_if_fail (path != NULL);
g_return_if_fail (action_label != NULL);
bin = GTK_BIN (gtk_ui_manager_get_widget (window->priv->ui, path));
label = GTK_LABEL (gtk_bin_get_child (bin));
if (action_description == NULL)
text = g_strdup (action_label);
else
text = g_strdup_printf ("%s: %s", action_label, action_description);
gtk_label_set_text_with_mnemonic (label, text);
g_free (text);
}
static void
refresh_undo_redo (GladeWindow *window)
{
GladeCommand *undo = NULL, *redo = NULL;
GladeProject *project;
GtkAction *action;
gchar *tooltip;
project = glade_app_get_project ();
if (project != NULL)
{
undo = glade_project_next_undo_item (project);
redo = glade_project_next_redo_item (project);
}
/* Refresh Undo */
action = gtk_action_group_get_action (window->priv->project_actions, "Undo");
gtk_action_set_sensitive (action, undo != NULL);
change_menu_label
(window, "/MenuBar/EditMenu/Undo", _("_Undo"), undo ? undo->description : NULL);
tooltip = g_strdup_printf (_("Undo: %s"), undo ? undo->description : _("the last action"));
g_object_set (action, "tooltip", tooltip, NULL);
g_free (tooltip);
/* Refresh Redo */
action = gtk_action_group_get_action (window->priv->project_actions, "Redo");
gtk_action_set_sensitive (action, redo != NULL);
change_menu_label
(window, "/MenuBar/EditMenu/Redo", _("_Redo"), redo ? redo->description : NULL);
tooltip = g_strdup_printf (_("Redo: %s"), redo ? redo->description : _("the last action"));
g_object_set (action, "tooltip", tooltip, NULL);
g_free (tooltip);
/* Refresh menus */
gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (window->priv->undo), glade_project_undo_items (project));
gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (window->priv->redo), glade_project_redo_items (project));
}
static void
update_ui (GladeApp *app, GladeWindow *window)
{
if (window->priv->active_view)
gtk_widget_queue_draw (GTK_WIDGET (window->priv->active_view));
refresh_undo_redo (window);
}
static void
glade_window_dispose (GObject *object)
{
GladeWindow *window = GLADE_WINDOW (object);
if (window->priv->app)
{
g_object_unref (window->priv->app);
window->priv->app = NULL;
}
G_OBJECT_CLASS (glade_window_parent_class)->dispose (object);
}
static void
glade_window_finalize (GObject *object)
{
guint i;
g_free (GLADE_WINDOW (object)->priv->default_path);
for (i = 0; i < N_DOCKS; i++)
{
ToolDock *dock = &GLADE_WINDOW (object)->priv->docks[i];
g_free (dock->title);
g_free (dock->id);
}
G_OBJECT_CLASS (glade_window_parent_class)->finalize (object);
}
static gboolean
glade_window_configure_event (GtkWidget *widget,
GdkEventConfigure *event)
{
GladeWindow *window = GLADE_WINDOW (widget);
gboolean retval;
window->priv->position.width = event->width;
window->priv->position.height = event->height;
retval = GTK_WIDGET_CLASS(glade_window_parent_class)->configure_event (widget, event);
gtk_window_get_position (GTK_WINDOW (widget),
&window->priv->position.x,
&window->priv->position.y);
return retval;
}
static void
key_file_set_window_position (GKeyFile *config,
GdkRectangle *position,
const char *id,
gboolean detached,
gboolean save_detached)
{
char *key_x, *key_y, *key_width, *key_height, *key_detached;
key_x = g_strdup_printf ("%s-" CONFIG_KEY_X, id);
key_y = g_strdup_printf ("%s-" CONFIG_KEY_Y, id);
key_width = g_strdup_printf ("%s-" CONFIG_KEY_WIDTH, id);
key_height = g_strdup_printf ("%s-" CONFIG_KEY_HEIGHT, id);
key_detached = g_strdup_printf ("%s-" CONFIG_KEY_DETACHED, id);
/* we do not want to save position of docks which
* were never detached */
if (position->x > G_MININT)
g_key_file_set_integer (config, CONFIG_GROUP_WINDOWS,
key_x, position->x);
if (position->y > G_MININT)
g_key_file_set_integer (config, CONFIG_GROUP_WINDOWS,
key_y, position->y);
g_key_file_set_integer (config, CONFIG_GROUP_WINDOWS,
key_width, position->width);
g_key_file_set_integer (config, CONFIG_GROUP_WINDOWS,
key_height, position->height);
if (save_detached)
g_key_file_set_boolean (config, CONFIG_GROUP_WINDOWS,
key_detached, detached);
g_free (key_detached);
g_free (key_height);
g_free (key_width);
g_free (key_y);
g_free (key_x);
}
static void
save_windows_config (GladeWindow *window)
{
guint i;
GKeyFile *config;
config = glade_app_get_config ();
for (i = 0; i < N_DOCKS; ++i)
{
ToolDock *dock = &window->priv->docks[i];
key_file_set_window_position (config, &dock->window_pos, dock->id,
dock->detached, TRUE);
}
key_file_set_window_position (config, &window->priv->position,
"main", FALSE, FALSE);
glade_app_config_save ();
}
static int
key_file_get_int (GKeyFile *config,
const char *group,
const char *key,
int default_value)
{
if (g_key_file_has_key (config, group, key, NULL))
return g_key_file_get_integer (config, group, key, NULL);
else
return default_value;
}
static void
key_file_get_window_position (GKeyFile *config,
const char *id,
GdkRectangle *pos,
gboolean *detached)
{
char *key_x, *key_y, *key_width, *key_height, *key_detached;
key_x = g_strdup_printf ("%s-" CONFIG_KEY_X, id);
key_y = g_strdup_printf ("%s-" CONFIG_KEY_Y, id);
key_width = g_strdup_printf ("%s-" CONFIG_KEY_WIDTH, id);
key_height = g_strdup_printf ("%s-" CONFIG_KEY_HEIGHT, id);
key_detached = g_strdup_printf ("%s-" CONFIG_KEY_DETACHED, id);
pos->x = key_file_get_int (config, CONFIG_GROUP_WINDOWS, key_x, pos->x);
pos->y = key_file_get_int (config, CONFIG_GROUP_WINDOWS, key_y, pos->y);
pos->width = key_file_get_int (config, CONFIG_GROUP_WINDOWS, key_width, pos->width);
pos->height = key_file_get_int (config, CONFIG_GROUP_WINDOWS, key_height, pos->height);
if (detached && g_key_file_has_key (config, CONFIG_GROUP_WINDOWS, key_detached, NULL))
*detached = g_key_file_get_boolean (config, CONFIG_GROUP_WINDOWS, key_detached, NULL);
g_free (key_x);
g_free (key_y);
g_free (key_width);
g_free (key_height);
g_free (key_detached);
}
static void
glade_window_set_initial_size (GladeWindow *window)
{
GKeyFile *config;
GdkRectangle position = {
G_MININT, G_MININT, GLADE_WINDOW_DEFAULT_WIDTH, GLADE_WINDOW_DEFAULT_HEIGHT
};
config = glade_app_get_config ();
key_file_get_window_position (config, "main", &position, NULL);
gtk_window_set_default_size (GTK_WINDOW (window), position.width, position.height);
if (position.x > G_MININT && position.y > G_MININT)
gtk_window_move (GTK_WINDOW (window), position.x, position.y);
}
static void
show_dock_first_time (GladeWindow *window,
guint dock_type,
const char *action_id)
{
GKeyFile *config;
int detached = -1;
GtkAction *action;
ToolDock *dock;
action = gtk_action_group_get_action (window->priv->static_actions, action_id);
g_object_set_data (G_OBJECT (action), "glade-dock-type", GUINT_TO_POINTER (dock_type));
dock = &window->priv->docks[dock_type];
config = glade_app_get_config ();
key_file_get_window_position (config, dock->id, &dock->window_pos, &detached);
if (detached == 1)
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
}
static void
setup_dock (ToolDock *dock,
GtkWidget *dock_widget,
guint default_width,
guint default_height,
const char *window_title,
const char *id,
GtkWidget *paned,
gboolean first_child)
{
dock->widget = dock_widget;
dock->window_pos.x = dock->window_pos.y = G_MININT;
dock->window_pos.width = default_width;
dock->window_pos.height = default_height;
dock->title = g_strdup (window_title);
dock->id = g_strdup (id);
dock->paned = paned;
dock->first_child = first_child;
dock->detached = FALSE;
}
static void
glade_window_init (GladeWindow *window)
{
GladeWindowPrivate *priv;
GtkWidget *vbox;
GtkWidget *hpaned1;
GtkWidget *hpaned2;
GtkWidget *vpaned;
GtkWidget *menubar;
GtkWidget *editor_item;
GtkWidget *palette;
GtkWidget *editor;
GtkWidget *dockitem;
GtkWidget *widget;
GtkWidget *sep;
GtkAction *action;
GtkAccelGroup *accel_group;
window->priv = priv = GLADE_WINDOW_GET_PRIVATE (window);
priv->label = NULL;
priv->default_path = NULL;
priv->app = glade_app_new ();
glade_window_set_initial_size (window);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
/* menubar */
menubar = construct_menu (window);
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0);
gtk_widget_show (menubar);
/* toolbar */
priv->toolbar = gtk_ui_manager_get_widget (priv->ui, "/ToolBar");
gtk_box_pack_start (GTK_BOX (vbox), priv->toolbar, FALSE, TRUE, 0);
gtk_widget_show (priv->toolbar);
/* tooltips object */
priv->tooltips = gtk_tooltips_new ();
/* undo/redo buttons */
priv->undo = gtk_menu_tool_button_new_from_stock (GTK_STOCK_UNDO);
priv->redo = gtk_menu_tool_button_new_from_stock (GTK_STOCK_REDO);
gtk_widget_show (GTK_WIDGET (priv->undo));
gtk_widget_show (GTK_WIDGET (priv->redo));
gtk_menu_tool_button_set_arrow_tooltip (GTK_MENU_TOOL_BUTTON (priv->undo),
priv->tooltips,
_("Go back in undo history"),
NULL);
gtk_menu_tool_button_set_arrow_tooltip (GTK_MENU_TOOL_BUTTON (priv->redo),
priv->tooltips,
("Go forward in undo history"),
NULL);
sep = GTK_WIDGET (gtk_separator_tool_item_new ());
gtk_widget_show (sep);
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), GTK_TOOL_ITEM (sep), 3);
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), GTK_TOOL_ITEM (priv->undo), 4);
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), GTK_TOOL_ITEM (priv->redo), 5);
action = gtk_ui_manager_get_action (priv->ui, "/MenuBar/EditMenu/Undo");
gtk_action_connect_proxy (action, GTK_WIDGET (priv->undo));
action = gtk_ui_manager_get_action (priv->ui, "/MenuBar/EditMenu/Redo");
gtk_action_connect_proxy (action, GTK_WIDGET (priv->redo));
/* main contents */
hpaned1 = gtk_hpaned_new ();
hpaned2 = gtk_hpaned_new ();
vpaned = gtk_vpaned_new ();
priv->left_pane = hpaned2;
priv->right_pane = vpaned;
gtk_container_set_border_width (GTK_CONTAINER (hpaned1), 2);
gtk_box_pack_start (GTK_BOX (vbox), hpaned1, TRUE, TRUE, 0);
gtk_paned_pack1 (GTK_PANED (hpaned1), hpaned2, TRUE, FALSE);
gtk_paned_pack2 (GTK_PANED (hpaned1), vpaned, FALSE, FALSE);
/* divider position between design area and editor/inspector */
gtk_paned_set_position (GTK_PANED (hpaned1), 350);
/* divider position between tree and editor */
gtk_paned_set_position (GTK_PANED (vpaned), 150);
gtk_widget_show_all (hpaned1);
gtk_widget_show_all (hpaned2);
gtk_widget_show_all (vpaned);
/* notebook */
priv->notebook = gtk_notebook_new ();
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
gtk_paned_pack2 (GTK_PANED (hpaned2), priv->notebook, TRUE, FALSE);
gtk_widget_show (priv->notebook);
/* palette */
palette = GTK_WIDGET (glade_app_get_palette ());
glade_palette_set_show_selector_button (GLADE_PALETTE (palette), FALSE);
gtk_paned_pack1 (GTK_PANED (hpaned2), palette, FALSE, FALSE);
gtk_widget_show (palette);
setup_dock (&priv->docks[DOCK_PALETTE], palette, 200, 540,
_("Palette"), "palette", hpaned2, TRUE);
/* inspectors */
priv->inspectors_notebook = gtk_notebook_new ();
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->inspectors_notebook), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->inspectors_notebook), FALSE);
gtk_widget_show (priv->inspectors_notebook);
gtk_paned_pack1 (GTK_PANED (vpaned), priv->inspectors_notebook, FALSE, FALSE);
setup_dock (&priv->docks[DOCK_INSPECTOR], priv->inspectors_notebook, 300, 540,
_("Inspector"), "inspector", vpaned, TRUE);
/* editor */
editor = GTK_WIDGET (glade_app_get_editor ());
dockitem = construct_dock_item (window, _("Properties"), editor);
priv->label = GTK_LABEL (g_object_get_data (G_OBJECT (dockitem), "dock-label"));
gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
gtk_misc_set_alignment (GTK_MISC (priv->label), 0, 0.5);
gtk_paned_pack2 (GTK_PANED (vpaned), dockitem, TRUE, FALSE);
setup_dock (&priv->docks[DOCK_EDITOR], dockitem, 500, 700,
_("Properties"), "properties", vpaned, FALSE);
show_dock_first_time (window, DOCK_PALETTE, "DockPalette");
show_dock_first_time (window, DOCK_INSPECTOR, "DockInspector");
show_dock_first_time (window, DOCK_EDITOR, "DockEditor");
/* status bar */
priv->statusbar = gtk_statusbar_new ();
priv->statusbar_menu_context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
"menu");
priv->statusbar_actions_context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar),
"actions");
gtk_box_pack_end (GTK_BOX (vbox), priv->statusbar, FALSE, TRUE, 0);
gtk_widget_show (priv->statusbar);
gtk_widget_show (vbox);
/* devhelp */
editor_item = gtk_ui_manager_get_widget (priv->ui,
"/MenuBar/ViewMenu/PropertyEditorHelp");
gtk_widget_set_sensitive (editor_item, FALSE);
/* recent files */
priv->recent_manager = gtk_recent_manager_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (window)));
priv->recent_menu = create_recent_chooser_menu (window, priv->recent_manager);
g_signal_connect (priv->recent_menu,
"item-activated",
G_CALLBACK (recent_chooser_item_activated_cb),
window);
widget = gtk_ui_manager_get_widget (priv->ui, "/MenuBar/FileMenu/OpenRecent");
gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), priv->recent_menu);
/* palette selector & drag/resize buttons */
sep = GTK_WIDGET (gtk_separator_tool_item_new());
gtk_widget_show (GTK_WIDGET (sep));
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), GTK_TOOL_ITEM (sep), -1);
priv->selector_button =
GTK_TOGGLE_TOOL_BUTTON (create_selector_tool_button (GTK_TOOLBAR (priv->toolbar)));
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar),
GTK_TOOL_ITEM (priv->selector_button), -1);
priv->drag_resize_button =
GTK_TOGGLE_TOOL_BUTTON (create_drag_resize_tool_button
(GTK_TOOLBAR (priv->toolbar)));
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar),
GTK_TOOL_ITEM (priv->drag_resize_button), -1);
gtk_toggle_tool_button_set_active (priv->selector_button, TRUE);
gtk_toggle_tool_button_set_active (priv->drag_resize_button, FALSE);
g_signal_connect (G_OBJECT (priv->selector_button), "toggled",
G_CALLBACK (on_selector_button_toggled), window);
g_signal_connect (G_OBJECT (priv->drag_resize_button), "toggled",
G_CALLBACK (on_drag_resize_button_toggled), window);
g_signal_connect (G_OBJECT (glade_app_get()), "notify::pointer-mode",
G_CALLBACK (on_pointer_mode_changed), window);
/* support for opening a file by dragging onto the project window */
gtk_drag_dest_set (GTK_WIDGET (window),
GTK_DEST_DEFAULT_ALL,
drop_types, G_N_ELEMENTS (drop_types),
GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (G_OBJECT (window), "drag-data-received",
G_CALLBACK (drag_data_received), window);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), window);
/* GtkNotebook signals */
g_signal_connect (priv->notebook,
"switch-page",
G_CALLBACK (notebook_switch_page_cb),
window);
g_signal_connect (priv->notebook,
"page-added",
G_CALLBACK (notebook_tab_added_cb),
window);
g_signal_connect (priv->notebook,
"page-removed",
G_CALLBACK (notebook_tab_removed_cb),
window);
/* GtkWindow events */
g_signal_connect (window, "screen-changed",
G_CALLBACK (window_screen_changed_cb),
window);
g_signal_connect (window, "window-state-event",
G_CALLBACK (window_state_event_cb),
window);
g_signal_connect (G_OBJECT (window), "key-press-event",
G_CALLBACK (hijack_key_press), window);
/* GladeApp signals */
g_signal_connect (G_OBJECT (priv->app), "update-ui",
G_CALLBACK (update_ui),
window);
/* Clipboard signals */
g_signal_connect (G_OBJECT (glade_app_get_clipboard ()), "notify::has-selection",
G_CALLBACK (clipboard_notify_handler_cb),
window);
gtk_about_dialog_set_url_hook ((GtkAboutDialogActivateLinkFunc) about_dialog_activate_link_func, window, NULL);
glade_app_set_window (GTK_WIDGET (window));
accel_group = gtk_ui_manager_get_accel_group(priv->ui);
gtk_window_add_accel_group (GTK_WINDOW (glade_app_get_clipboard_view ()), accel_group);
}
static void
glade_window_class_init (GladeWindowClass *klass)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = G_OBJECT_CLASS (klass);
widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = glade_window_dispose;
object_class->finalize = glade_window_finalize;
widget_class->configure_event = glade_window_configure_event;
g_type_class_add_private (klass, sizeof (GladeWindowPrivate));
}
GtkWidget *
glade_window_new (void)
{
return g_object_new (GLADE_TYPE_WINDOW, NULL);
}
void
glade_window_check_devhelp (GladeWindow *window)
{
GtkWidget *editor_item;
g_return_if_fail (GLADE_IS_WINDOW (window));
editor_item = gtk_ui_manager_get_widget (window->priv->ui,
"/MenuBar/ViewMenu/PropertyEditorHelp");
if (glade_util_have_devhelp ())
{
GladeEditor *editor = glade_app_get_editor ();
glade_editor_show_info (editor);
glade_editor_hide_context_info (editor);
g_signal_handlers_disconnect_by_func (editor, doc_search_cb, window);
g_signal_connect (editor, "gtk-doc-search",
G_CALLBACK (doc_search_cb), window);
gtk_widget_set_sensitive (editor_item, TRUE);
}
else
{
gtk_widget_set_sensitive (editor_item, FALSE);
}
}
syntax highlighted by Code2HTML, v. 0.9.1