gimp/app/widgets/gimpdataeditor.c

535 lines
17 KiB
C
Raw Normal View History

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpdataeditor.c
* Copyright (C) 2002-2004 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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libgimpwidgets/gimpwidgets.h"
#include "widgets-types.h"
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpdata.h"
#include "core/gimpdatafactory.h"
#include "gimpdataeditor.h"
#include "gimpdocked.h"
Move away from creating all item_factories statically in menus_init() but 2003-01-10 Michael Natterer <mitch@gimp.org> Move away from creating all item_factories statically in menus_init() but create a new one for each place where one is needed: * app/widgets/Makefile.am * app/widgets/widgets-types.h * app/widgets/gimpmenufactory.[ch]: new factory which creates and configures the GimpItemFactories it knows about on-the-fly. * app/widgets/gimpitemfactory.[ch]: added gimp_item_factory_update() which calls the "update_func". Added "gboolean update_on_popup" so item_factories can be configured to require manual updates (used for the <Image> factory). * app/gui/menus.[ch]: create a "global_menu_factory" and register all menus we have with it. Added various setup functions which do stuff like adding the "Open Recent" menu or reorder plug-in menu entries. Removed the debugging stuff... * app/gui/Makefile.am * app/gui/debug-commands.[ch]: ...and added it here. * app/gui/gui.c: create the <Toolbox>, the popup-<Image> and the <Paths> factories here because they are still global. * app/gui/plug-in-menus.[ch]: changed the "image_factory" parameters to "item_factory" and create/update the entries for the passed item_factory only. Makes the whole stuff much more straightforward. * app/plug-in/plug-ins.c: don't call plug_in_make_menu(). * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell.[ch]: added "menu_factory" and "popup_factory" parameters to gimp_display_new() and gimp_display_shell_new(). Create the menubar_factory and the qmask_factory dynamically. Pass the shell, not a Gimp to the QMask callbacks. Changed gimp_display_shell_set_menu_sensitivity() to gimp_display_shell_menu_update() and don't call it directly (it's a GimpItemFactory update_func now). Call gimp_item_factory_update() on the resp. factories instead. * app/gui/qmask-commands.c * app/display/gimpdisplayshell-callbacks.c * app/tools/gimpimagemaptool.c: changed accordingly. * app/widgets/gimpbrusheditor.c * app/widgets/gimpbrushfactoryview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcolormapeditor.[ch] * app/widgets/gimpcontainereditor.[ch] * app/widgets/gimpdataeditor.[ch] * app/widgets/gimpdatafactoryview.[ch] * app/widgets/gimpdialogfactory.[ch] * app/widgets/gimpdock.c * app/widgets/gimpdockbook.[ch] * app/widgets/gimpdocumentview.[ch] * app/widgets/gimpgradienteditor.[ch] * app/widgets/gimpimageview.[ch] * app/widgets/gimpitemlistview.[ch] * app/widgets/gimppaletteeditor.[ch]: pass around lots of GimpMenuFactory pointers and menu_identifiers so all views can create their item_factories themselves. Unref the factories when they are no longer needed because they belong to the views now. * app/gui/dialogs-commands.c * app/gui/dialogs-constructors.c * app/gui/dialogs.c * app/gui/brush-select.c * app/gui/gradient-select.c * app/gui/palette-select.c * app/gui/pattern-select.c: changed accordingly. * app/gui/file-dialog-utils.[ch] (file_dialog_new): require menu_factory and menu_identifier parameters. * app/gui/file-open-dialog.[ch] * app/gui/file-save-dialog.[ch]: removed file_*_dialog_menu_init() (they went to menus.c as setup_funcs). Added file_*_dialog_set_type() and moved the <Load> and <Save> factory callbacks to file-commands.c * app/gui/file-commands.[ch]: changed accordingly. * app/gui/view-commands.c: changed the statusbar, menubar, rulers and guides callbacks to do their job only if the setting has actually changed. Don't update whole item factories afterwards. Instead, just change the state of the items that actually need update. Unrelated: * app/core/gimpchannel.c (gimp_channel_init): set "bounds_known" and friends to FALSE since we don't know that the new channel will be empty (fixes QMask and probably other stuff). * app/gui/image-commands.c * app/gui/vectors-commands.c: cleanup.
2003-01-11 01:55:53 +08:00
#include "gimpmenufactory.h"
#include "gimpsessioninfo.h"
#include "gimpuimanager.h"
#include "gimp-intl.h"
enum
{
PROP_0,
PROP_DATA_FACTORY,
PROP_DATA
};
static void gimp_data_editor_docked_iface_init (GimpDockedInterface *iface);
static GObject * gimp_data_editor_constructor (GType type,
guint n_params,
GObjectConstructParam *params);
static void gimp_data_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_data_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_data_editor_dispose (GObject *object);
static void gimp_data_editor_set_aux_info (GimpDocked *docked,
GList *aux_info);
static GList * gimp_data_editor_get_aux_info (GimpDocked *docked);
static gchar * gimp_data_editor_get_title (GimpDocked *docked);
static void gimp_data_editor_real_set_data (GimpDataEditor *editor,
GimpData *data);
static gboolean gimp_data_editor_name_key_press (GtkWidget *widget,
GdkEventKey *kevent,
GimpDataEditor *editor);
static void gimp_data_editor_name_activate (GtkWidget *widget,
GimpDataEditor *editor);
static gboolean gimp_data_editor_name_focus_out (GtkWidget *widget,
GdkEvent *event,
GimpDataEditor *editor);
static void gimp_data_editor_data_name_changed (GimpObject *object,
GimpDataEditor *editor);
static void gimp_data_editor_save_clicked (GtkWidget *widget,
GimpDataEditor *editor);
static void gimp_data_editor_revert_clicked (GtkWidget *widget,
GimpDataEditor *editor);
static void gimp_data_editor_save_dirty (GimpDataEditor *editor);
G_DEFINE_TYPE_WITH_CODE (GimpDataEditor, gimp_data_editor, GIMP_TYPE_EDITOR,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
gimp_data_editor_docked_iface_init));
#define parent_class gimp_data_editor_parent_class
static GimpDockedInterface *parent_docked_iface = NULL;
static void
gimp_data_editor_class_init (GimpDataEditorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = gimp_data_editor_constructor;
object_class->set_property = gimp_data_editor_set_property;
object_class->get_property = gimp_data_editor_get_property;
object_class->dispose = gimp_data_editor_dispose;
klass->set_data = gimp_data_editor_real_set_data;
g_object_class_install_property (object_class, PROP_DATA_FACTORY,
g_param_spec_object ("data-factory",
NULL, NULL,
GIMP_TYPE_DATA_FACTORY,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_DATA,
g_param_spec_object ("data",
NULL, NULL,
GIMP_TYPE_DATA,
GIMP_PARAM_READWRITE));
}
static void
gimp_data_editor_init (GimpDataEditor *editor)
{
editor->data_factory = NULL;
2003-03-10 22:07:22 +08:00
editor->data = NULL;
editor->data_editable = FALSE;
editor->name_entry = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (editor), editor->name_entry, FALSE, FALSE, 0);
gtk_widget_show (editor->name_entry);
gtk_widget_set_sensitive (editor->name_entry, FALSE);
g_signal_connect (editor->name_entry, "key-press-event",
G_CALLBACK (gimp_data_editor_name_key_press),
editor);
g_signal_connect (editor->name_entry, "activate",
G_CALLBACK (gimp_data_editor_name_activate),
editor);
g_signal_connect (editor->name_entry, "focus-out-event",
G_CALLBACK (gimp_data_editor_name_focus_out),
editor);
}
static void
gimp_data_editor_docked_iface_init (GimpDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
iface->set_aux_info = gimp_data_editor_set_aux_info;
iface->get_aux_info = gimp_data_editor_get_aux_info;
iface->get_title = gimp_data_editor_get_title;
}
static GObject *
gimp_data_editor_constructor (GType type,
guint n_params,
GObjectConstructParam *params)
{
GObject *object;
GimpDataEditor *editor;
object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
editor = GIMP_DATA_EDITOR (object);
g_assert (GIMP_IS_DATA_FACTORY (editor->data_factory));
gimp_data_editor_set_edit_active (editor, TRUE);
editor->save_button =
gimp_editor_add_button (GIMP_EDITOR (editor),
GTK_STOCK_SAVE,
_("Save"), NULL,
G_CALLBACK (gimp_data_editor_save_clicked),
NULL,
editor);
editor->revert_button =
gimp_editor_add_button (GIMP_EDITOR (editor),
GTK_STOCK_REVERT_TO_SAVED,
_("Revert"), NULL,
G_CALLBACK (gimp_data_editor_revert_clicked),
NULL,
editor);
/*
* Set insensitive because revert buttons are not yet implemented.
*/
gtk_widget_set_sensitive (editor->revert_button, FALSE);
return object;
}
static void
gimp_data_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
switch (property_id)
{
case PROP_DATA_FACTORY:
editor->data_factory = g_value_get_object (value);
break;
case PROP_DATA:
gimp_data_editor_set_data (editor,
(GimpData *) g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_data_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
switch (property_id)
{
case PROP_DATA_FACTORY:
g_value_set_object (value, editor->data_factory);
break;
case PROP_DATA:
g_value_set_object (value, editor->data);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_data_editor_dispose (GObject *object)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (object);
if (editor->data)
{
/* Save dirty data before we clear out */
gimp_data_editor_save_dirty (editor);
gimp_data_editor_set_data (editor, NULL);
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
#define AUX_INFO_EDIT_ACTIVE "edit-active"
#define AUX_INFO_CURRENT_DATA "current-data"
static void
gimp_data_editor_set_aux_info (GimpDocked *docked,
GList *aux_info)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
GList *list;
parent_docked_iface->set_aux_info (docked, aux_info);
for (list = aux_info; list; list = g_list_next (list))
{
GimpSessionInfoAux *aux = list->data;
if (! strcmp (aux->name, AUX_INFO_EDIT_ACTIVE))
{
gboolean edit_active;
edit_active = ! g_ascii_strcasecmp (aux->value, "true");
gimp_data_editor_set_edit_active (editor, edit_active);
}
else if (! strcmp (aux->name, AUX_INFO_CURRENT_DATA))
{
if (! editor->edit_active)
{
GimpData *data;
data = (GimpData *)
gimp_container_get_child_by_name (editor->data_factory->container,
aux->value);
if (data)
gimp_data_editor_set_data (editor, data);
}
}
}
}
static GList *
gimp_data_editor_get_aux_info (GimpDocked *docked)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
GList *aux_info;
GimpSessionInfoAux *aux;
aux_info = parent_docked_iface->get_aux_info (docked);
aux = gimp_session_info_aux_new (AUX_INFO_EDIT_ACTIVE,
editor->edit_active ? "true" : "false");
aux_info = g_list_append (aux_info, aux);
if (editor->data)
{
const gchar *value;
value = gimp_object_get_name (GIMP_OBJECT (editor->data));
aux = gimp_session_info_aux_new (AUX_INFO_CURRENT_DATA, value);
aux_info = g_list_append (aux_info, aux);
}
return aux_info;
}
static gchar *
gimp_data_editor_get_title (GimpDocked *docked)
{
GimpDataEditor *editor = GIMP_DATA_EDITOR (docked);
GimpDataEditorClass *editor_class = GIMP_DATA_EDITOR_GET_CLASS (editor);
if (editor->data_editable)
return g_strdup (editor_class->title);
else
return g_strdup_printf (_("%s (read only)"), editor_class->title);
}
static void
gimp_data_editor_real_set_data (GimpDataEditor *editor,
GimpData *data)
{
gboolean editable;
if (editor->data)
{
g_signal_handlers_disconnect_by_func (editor->data,
gimp_data_editor_data_name_changed,
editor);
g_object_unref (editor->data);
}
editor->data = data;
if (editor->data)
{
g_object_ref (editor->data);
g_signal_connect (editor->data, "name-changed",
G_CALLBACK (gimp_data_editor_data_name_changed),
editor);
gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
gimp_object_get_name (GIMP_OBJECT (editor->data)));
}
else
{
gtk_entry_set_text (GTK_ENTRY (editor->name_entry), "");
}
editable = (editor->data && editor->data->writable);
if (editor->data_editable != editable)
{
editor->data_editable = editable;
2003-03-10 22:07:22 +08:00
gtk_widget_set_sensitive (editor->name_entry, editable);
gimp_docked_title_changed (GIMP_DOCKED (editor));
}
}
void
gimp_data_editor_set_data (GimpDataEditor *editor,
GimpData *data)
{
g_return_if_fail (GIMP_IS_DATA_EDITOR (editor));
g_return_if_fail (data == NULL || GIMP_IS_DATA (data));
g_return_if_fail (data == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (data),
editor->data_factory->container->children_type));
if (editor->data != data)
{
GIMP_DATA_EDITOR_GET_CLASS (editor)->set_data (editor, data);
g_object_notify (G_OBJECT (editor), "data");
if (GIMP_EDITOR (editor)->ui_manager)
gimp_ui_manager_update (GIMP_EDITOR (editor)->ui_manager,
GIMP_EDITOR (editor)->popup_data);
}
}
GimpData *
gimp_data_editor_get_data (GimpDataEditor *editor)
{
g_return_val_if_fail (GIMP_IS_DATA_EDITOR (editor), NULL);
return editor->data;
}
void
gimp_data_editor_set_edit_active (GimpDataEditor *editor,
gboolean edit_active)
{
g_return_if_fail (GIMP_IS_DATA_EDITOR (editor));
if (editor->edit_active != edit_active)
{
GimpContext *user_context;
user_context = gimp_get_user_context (editor->data_factory->gimp);
editor->edit_active = edit_active;
if (editor->edit_active)
{
GType data_type;
GimpData *data;
data_type = editor->data_factory->container->children_type;
data = GIMP_DATA (gimp_context_get_by_type (user_context, data_type));
g_signal_connect_object (user_context,
gimp_context_type_to_signal_name (data_type),
G_CALLBACK (gimp_data_editor_set_data),
editor, G_CONNECT_SWAPPED);
gimp_data_editor_set_data (editor, data);
}
else
{
g_signal_handlers_disconnect_by_func (user_context,
gimp_data_editor_set_data,
editor);
}
}
}
gboolean
gimp_data_editor_get_edit_active (GimpDataEditor *editor)
{
g_return_val_if_fail (GIMP_IS_DATA_EDITOR (editor), FALSE);
return editor->edit_active;
}
/* private functions */
static gboolean
gimp_data_editor_name_key_press (GtkWidget *widget,
GdkEventKey *kevent,
GimpDataEditor *editor)
{
if (kevent->keyval == GDK_Escape)
{
gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
gimp_object_get_name (GIMP_OBJECT (editor->data)));
return TRUE;
}
return FALSE;
}
static void
gimp_data_editor_name_activate (GtkWidget *widget,
GimpDataEditor *editor)
{
if (editor->data)
gimp_object_set_name (GIMP_OBJECT (editor->data),
gtk_entry_get_text (GTK_ENTRY (widget)));
}
static gboolean
gimp_data_editor_name_focus_out (GtkWidget *widget,
GdkEvent *event,
GimpDataEditor *editor)
{
gimp_data_editor_name_activate (widget, editor);
return FALSE;
}
static void
gimp_data_editor_data_name_changed (GimpObject *object,
GimpDataEditor *editor)
{
gtk_entry_set_text (GTK_ENTRY (editor->name_entry),
gimp_object_get_name (object));
}
static void
gimp_data_editor_save_clicked (GtkWidget *widget,
GimpDataEditor *editor)
{
gimp_data_editor_save_dirty (editor);
}
static void
gimp_data_editor_revert_clicked (GtkWidget *widget,
GimpDataEditor *editor)
{
g_print ("TODO: implement revert\n");
}
static void
gimp_data_editor_save_dirty (GimpDataEditor *editor)
{
Added infrastructure to make sure we don't write to the global brush, 2004-01-28 Michael Natterer <mitch@gimp.org> Added infrastructure to make sure we don't write to the global brush, pattern etc. directories. Needed to make this configurable because we can't rely on the global directories being read-only, having certain names or being otherwise detectable at runtime in a sane way. Fixes bug #132214. * libgimpbase/gimpdatafiles.[ch]: added "const gchar *dirname" to the GimpDataFileData struct so callbacks don't need to call g_path_get_dirname() for each file. * libgimpwidgets/gimpfileentry.c: made it work with non UTF-8 encoded filenames. * libgimpwidgets/gimppatheditor.[ch]: ditto. Added GUI and API for setting/getting a second "writable_path". The widget makes sure that the writable_path is always a subset of the path. * app/config/gimpconfig-utils.[ch]: added new function gimp_config_build_writable_path(). * app/config/gimpcoreconfig.[ch]: added separate properties for the writable brush, pattern, gradient, palette and font paths. * app/config/gimprc-blurbs.h: added (still empty) blurbs for the new properties. * app/core/gimpdata.[ch] (gimp_data_set_filename): added parameter "gboolean writable". Set data->writable to FALSE by default. If "writable" is passed as TRUE, still check if we can write to the file before setting data->writable to TRUE. (gimp_data_create_filename): changed "data_path" parameter to "dest_dir" and assume dest_dir is writable. (gimp_data_duplicate): set data->dirty to TRUE to make sure duplicated things will be saved. * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpbrushpipe.c * app/core/gimpgradient.c * app/core/gimppalette.c * app/core/gimppattern.c: don't set the data's filename and don't touch data->dirty in the _load() functions because that's done by the data factory now. Don't touch data->dirty in the _duplicate() functions because that's done by gimp_data_duplicate() itself now. * app/core/gimpdatafactory.[ch] (gimp_data_factory_new): added "writable_property_name" and remember it. Added utility function gimp_data_factory_get_save_dir() which determines the directory to save new datas to. Added public function gimp_data_factory_data_save_single() which saves a single data object. Make sure new things get saved to the first writable directory as specified in preferences. * app/core/gimp.c (gimp_real_initialize): pass the writable_paths' property names to gimp_data_factory_new(). * app/widgets/gimpdataeditor.c (gimp_data_editor_save_dirty): use gimp_data_factory_data_save_single() instead of implementing saving here. * app/widgets/gimppropwidgets.[ch] (gimp_prop_path_editor_new): added "const gchar *writable_property_name" parameter (can be NULL). Added the needed callbacks to handle the writable_path and made the path_editor and file_entry code aware of non UTF-8 filename encodings. Some general cleanup. * app/gui/preferences-dialog.c: changed accordingly.
2004-01-29 05:53:50 +08:00
GimpData *data = editor->data;
if (data && data->dirty && data->writable)
Added infrastructure to make sure we don't write to the global brush, 2004-01-28 Michael Natterer <mitch@gimp.org> Added infrastructure to make sure we don't write to the global brush, pattern etc. directories. Needed to make this configurable because we can't rely on the global directories being read-only, having certain names or being otherwise detectable at runtime in a sane way. Fixes bug #132214. * libgimpbase/gimpdatafiles.[ch]: added "const gchar *dirname" to the GimpDataFileData struct so callbacks don't need to call g_path_get_dirname() for each file. * libgimpwidgets/gimpfileentry.c: made it work with non UTF-8 encoded filenames. * libgimpwidgets/gimppatheditor.[ch]: ditto. Added GUI and API for setting/getting a second "writable_path". The widget makes sure that the writable_path is always a subset of the path. * app/config/gimpconfig-utils.[ch]: added new function gimp_config_build_writable_path(). * app/config/gimpcoreconfig.[ch]: added separate properties for the writable brush, pattern, gradient, palette and font paths. * app/config/gimprc-blurbs.h: added (still empty) blurbs for the new properties. * app/core/gimpdata.[ch] (gimp_data_set_filename): added parameter "gboolean writable". Set data->writable to FALSE by default. If "writable" is passed as TRUE, still check if we can write to the file before setting data->writable to TRUE. (gimp_data_create_filename): changed "data_path" parameter to "dest_dir" and assume dest_dir is writable. (gimp_data_duplicate): set data->dirty to TRUE to make sure duplicated things will be saved. * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpbrushpipe.c * app/core/gimpgradient.c * app/core/gimppalette.c * app/core/gimppattern.c: don't set the data's filename and don't touch data->dirty in the _load() functions because that's done by the data factory now. Don't touch data->dirty in the _duplicate() functions because that's done by gimp_data_duplicate() itself now. * app/core/gimpdatafactory.[ch] (gimp_data_factory_new): added "writable_property_name" and remember it. Added utility function gimp_data_factory_get_save_dir() which determines the directory to save new datas to. Added public function gimp_data_factory_data_save_single() which saves a single data object. Make sure new things get saved to the first writable directory as specified in preferences. * app/core/gimp.c (gimp_real_initialize): pass the writable_paths' property names to gimp_data_factory_new(). * app/widgets/gimpdataeditor.c (gimp_data_editor_save_dirty): use gimp_data_factory_data_save_single() instead of implementing saving here. * app/widgets/gimppropwidgets.[ch] (gimp_prop_path_editor_new): added "const gchar *writable_property_name" parameter (can be NULL). Added the needed callbacks to handle the writable_path and made the path_editor and file_entry code aware of non UTF-8 filename encodings. Some general cleanup. * app/gui/preferences-dialog.c: changed accordingly.
2004-01-29 05:53:50 +08:00
gimp_data_factory_data_save_single (editor->data_factory, data);
}