gimp/app/widgets/gimpsessioninfo-dock.c
Michael Natterer fbd746f95a app: don't rely on gtk_container_remove() to always destroy the child
Instead, either destroy the child instead of removing it, or remove
*and* destroy it in cases where the remove() api on the "parent"
doesn't match GTK+'s parent/child relation (like with all our dock
widgets). We can't rely on remove() to implicitly detstroy, because
there might be arbitrary other code holding references, such as
accessibility modules and whatnot. Most likely fixes unclear crashes
in accessibility code and other crashes we blamed GTK+ for.
2012-02-21 00:36:18 +01:00

372 lines
10 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpsessioninfo-dock.c
* Copyright (C) 2001-2007 Michael Natterer <mitch@gimp.org>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpconfig/gimpconfig.h"
#include "widgets-types.h"
#include "gimpdialogfactory.h"
#include "gimpdock.h"
#include "gimpdockbook.h"
#include "gimpdockcontainer.h"
#include "gimpdockwindow.h"
#include "gimpsessioninfo.h"
#include "gimpsessioninfo-aux.h"
#include "gimpsessioninfo-book.h"
#include "gimpsessioninfo-dock.h"
#include "gimpsessioninfo-private.h"
#include "gimptoolbox.h"
#include "gimpwidgets-utils.h"
enum
{
SESSION_INFO_SIDE,
SESSION_INFO_POSITION,
SESSION_INFO_BOOK
};
static GimpAlignmentType gimp_session_info_dock_get_side (GimpDock *dock);
static GimpAlignmentType
gimp_session_info_dock_get_side (GimpDock *dock)
{
GimpAlignmentType result = -1;
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (dock));
if (GIMP_IS_DOCK_CONTAINER (toplevel))
{
GimpDockContainer *container = GIMP_DOCK_CONTAINER (toplevel);
result = gimp_dock_container_get_dock_side (container, dock);
}
return result;
}
/* public functions */
GimpSessionInfoDock *
gimp_session_info_dock_new (const gchar *dock_type)
{
GimpSessionInfoDock *dock_info = NULL;
dock_info = g_slice_new0 (GimpSessionInfoDock);
dock_info->dock_type = g_strdup (dock_type);
dock_info->side = -1;
return dock_info;
}
void
gimp_session_info_dock_free (GimpSessionInfoDock *dock_info)
{
g_return_if_fail (dock_info != NULL);
if (dock_info->dock_type)
{
g_free (dock_info->dock_type);
dock_info->dock_type = NULL;
}
if (dock_info->books)
{
g_list_free_full (dock_info->books,
(GDestroyNotify) gimp_session_info_book_free);
dock_info->books = NULL;
}
g_slice_free (GimpSessionInfoDock, dock_info);
}
void
gimp_session_info_dock_serialize (GimpConfigWriter *writer,
GimpSessionInfoDock *dock_info)
{
GList *list;
g_return_if_fail (writer != NULL);
g_return_if_fail (dock_info != NULL);
gimp_config_writer_open (writer, dock_info->dock_type);
if (dock_info->side != -1)
{
const char *side_text =
dock_info->side == GIMP_ALIGN_LEFT ? "left" : "right";
gimp_config_writer_open (writer, "side");
gimp_config_writer_print (writer, side_text, strlen (side_text));
gimp_config_writer_close (writer);
}
if (dock_info->position != 0)
gimp_session_write_position (writer, dock_info->position);
for (list = dock_info->books; list; list = g_list_next (list))
gimp_session_info_book_serialize (writer, list->data);
gimp_config_writer_close (writer);
}
GTokenType
gimp_session_info_dock_deserialize (GScanner *scanner,
gint scope,
GimpSessionInfoDock **dock_info,
const gchar *dock_type)
{
GTokenType token;
g_return_val_if_fail (scanner != NULL, G_TOKEN_LEFT_PAREN);
g_return_val_if_fail (dock_info != NULL, G_TOKEN_LEFT_PAREN);
g_scanner_scope_add_symbol (scanner, scope, "side",
GINT_TO_POINTER (SESSION_INFO_SIDE));
g_scanner_scope_add_symbol (scanner, scope, "position",
GINT_TO_POINTER (SESSION_INFO_POSITION));
g_scanner_scope_add_symbol (scanner, scope, "book",
GINT_TO_POINTER (SESSION_INFO_BOOK));
*dock_info = gimp_session_info_dock_new (dock_type);
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
switch (GPOINTER_TO_INT (scanner->value.v_symbol))
{
GimpSessionInfoBook *book;
case SESSION_INFO_SIDE:
token = G_TOKEN_IDENTIFIER;
if (g_scanner_peek_next_token (scanner) != token)
break;
g_scanner_get_next_token (scanner);
if (strcmp ("left", scanner->value.v_identifier) == 0)
(*dock_info)->side = GIMP_ALIGN_LEFT;
else
(*dock_info)->side = GIMP_ALIGN_RIGHT;
break;
case SESSION_INFO_POSITION:
token = G_TOKEN_INT;
if (! gimp_scanner_parse_int (scanner, &((*dock_info)->position)))
(*dock_info)->position = 0;
break;
case SESSION_INFO_BOOK:
g_scanner_set_scope (scanner, scope + 1);
token = gimp_session_info_book_deserialize (scanner, scope + 1,
&book);
if (token == G_TOKEN_LEFT_PAREN)
{
(*dock_info)->books = g_list_append ((*dock_info)->books, book);
g_scanner_set_scope (scanner, scope);
}
else
return token;
break;
default:
return token;
}
token = G_TOKEN_RIGHT_PAREN;
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default:
break;
}
}
g_scanner_scope_remove_symbol (scanner, scope, "book");
g_scanner_scope_remove_symbol (scanner, scope, "position");
g_scanner_scope_remove_symbol (scanner, scope, "side");
return token;
}
GimpSessionInfoDock *
gimp_session_info_dock_from_widget (GimpDock *dock)
{
GimpSessionInfoDock *dock_info;
GList *list;
GtkWidget *parent;
g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
dock_info = gimp_session_info_dock_new (GIMP_IS_TOOLBOX (dock) ?
"gimp-toolbox" :
"gimp-dock");
for (list = gimp_dock_get_dockbooks (dock); list; list = g_list_next (list))
{
GimpSessionInfoBook *book;
book = gimp_session_info_book_from_widget (list->data);
dock_info->books = g_list_prepend (dock_info->books, book);
}
dock_info->books = g_list_reverse (dock_info->books);
dock_info->side = gimp_session_info_dock_get_side (dock);
parent = gtk_widget_get_parent (GTK_WIDGET (dock));
if (GTK_IS_PANED (parent))
{
GtkPaned *paned = GTK_PANED (parent);
if (GTK_WIDGET (dock) == gtk_paned_get_child2 (paned))
dock_info->position = gtk_paned_get_position (paned);
}
return dock_info;
}
GimpDock *
gimp_session_info_dock_restore (GimpSessionInfoDock *dock_info,
GimpDialogFactory *factory,
GdkScreen *screen,
GimpDockContainer *dock_container)
{
gint n_books = 0;
GtkWidget *dock;
GList *iter;
GimpUIManager *ui_manager;
g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
ui_manager = gimp_dock_container_get_ui_manager (dock_container);
dock = gimp_dialog_factory_dialog_new (factory,
screen,
ui_manager,
dock_info->dock_type,
-1 /*view_size*/,
FALSE /*present*/);
g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
/* Add the dock to the dock window immediately so the stuff in the
* dock has access to e.g. a dialog factory
*/
gimp_dock_container_add_dock (dock_container,
GIMP_DOCK (dock),
dock_info);
/* Note that if it is a toolbox, we will get here even though we
* don't have any books
*/
for (iter = dock_info ? dock_info->books : NULL;
iter;
iter = g_list_next (iter))
{
GimpSessionInfoBook *book_info = iter->data;
GtkWidget *dockbook;
dockbook = GTK_WIDGET (gimp_session_info_book_restore (book_info,
GIMP_DOCK (dock)));
if (dockbook)
{
GtkWidget *parent = gtk_widget_get_parent (dockbook);
n_books++;
if (GTK_IS_PANED (parent))
{
GtkPaned *paned = GTK_PANED (parent);
if (dockbook == gtk_paned_get_child2 (paned))
gtk_paned_set_position (paned, book_info->position);
}
}
}
/* Now remove empty dockbooks from the list, check the comment in
* gimp_session_info_book_restore() which explains why the dock
* can contain empty dockbooks at all
*/
if (dock_info && dock_info->books)
{
GList *books;
books = g_list_copy (gimp_dock_get_dockbooks (GIMP_DOCK (dock)));
while (books)
{
GtkContainer *dockbook = books->data;
GList *children = gtk_container_get_children (dockbook);
if (children)
{
g_list_free (children);
}
else
{
g_object_ref (dockbook);
gimp_dock_remove_book (GIMP_DOCK (dock), GIMP_DOCKBOOK (dockbook));
gtk_widget_destroy (GTK_WIDGET (dockbook));
g_object_unref (dockbook);
n_books--;
}
books = g_list_remove (books, dockbook);
}
}
/* if we removed all books again, the dock was destroyed, so bail out */
if (dock_info && dock_info->books && n_books == 0)
{
return NULL;
}
gtk_widget_show (dock);
return GIMP_DOCK (dock);
}