gimp/app/lc_dialog.c
Michael Natterer f283b957b1 app/session.[ch] removed the old dialog session management code...
2001-04-17  Michael Natterer  <mitch@gimp.org>

	* app/session.[ch]
	* app/gimprc.c: removed the old dialog session management code...

	* app/widgets/gimpdialogfactory.[ch]: ...and manage all dialogs here.

	* app/gui/dialogs-constructors.[ch]: dialog factory compliant
	constructors for all session managed toplevel dialogs.

	* app/brush_select.[ch]
	* app/devices.[ch]
	* app/docindex.[ch]
	* app/errorconsole.[ch]
	* app/gradient_select.[ch]
	* app/info_dialog.c
	* app/lc_dialog.[ch]
	* app/palette.[ch]
	* app/pattern_select.[ch]
	* app/toolbox.[ch]
	* app/tools/tool_options_dialog.[ch]: all dialog constructors have
	to return the dialog now (even the legacy ones that will go away).
	Removed the session management code as this is now done for the
	dialogs, not by them.

	* app/app_procs.c
	* app/color_select.c
	* app/commands.[ch]
	* app/indicator_area.c
	* app/menus.c
	* app/palette_select.c
	* app/preferences_dialog.c
	* app/gui/dialogs.c
	* app/gui/dialogs-commands.[ch]
	* app/gui/gui.c
	* app/tools/gimptool.c
	* app/widgets/gimpdock.c: changed accordingly.
2001-04-17 16:00:27 +00:00

764 lines
19 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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 <gtk/gtk.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "apptypes.h"
#include "widgets/gimpdialogfactory.h"
#include "gui/dialogs.h"
#include "context_manager.h"
#include "dialog_handler.h"
#include "gdisplay.h"
#include "gimage.h"
#include "gimpcontainer.h"
#include "gimpcontext.h"
#include "gimprc.h"
#include "image_render.h"
#include "lc_dialog.h"
#include "lc_dialogP.h"
#include "layers_dialogP.h"
#include "temp_buf.h"
#include "libgimp/gimplimits.h"
#include "libgimp/gimpintl.h"
#define MENU_THUMBNAIL_SIZE 24
/* local function prototypes */
static void lc_dialog_update (GimpImage *gimage);
static void lc_dialog_image_menu_callback (GtkWidget *widget,
gpointer data);
static void lc_dialog_auto_callback (GtkWidget *widget,
gpointer data);
static gint lc_dialog_close_callback (GtkWidget *widget,
gpointer data);
static void lc_dialog_add_callback (GimpContainer *container,
GimpImage *gimage,
gpointer data);
static void lc_dialog_remove_callback (GimpContainer *container,
GimpImage *gimage,
gpointer data);
static void lc_dialog_change_image (GimpContext *context,
GimpImage *gimage,
gpointer data);
static void lc_dialog_help_func (const gchar *help_data);
static void lc_dialog_image_menu_preview_update_callback (GtkWidget *widget,
gpointer data);
static void lc_dialog_fill_preview_with_thumb (GtkWidget *widget,
GimpImage *gimage,
gint width,
gint height);
/* FIXME: move these to a better place */
static GtkWidget * lc_dialog_create_image_menu (GimpImage **def,
gint *default_index,
GtkSignalFunc callback);
static void lc_dialog_create_image_menu_callback (gpointer image,
gpointer data);
/* the main dialog structure */
LCDialog * lc_dialog = NULL;
/*********************************/
/* Public L&C dialog functions */
/*********************************/
GtkWidget *
lc_dialog_create (GimpImage *gimage)
{
GtkWidget *util_box;
GtkWidget *auto_button;
GtkWidget *label;
GtkWidget *separator;
gint default_index;
if (lc_dialog)
{
if (gimage)
lc_dialog_update (gimage);
lc_dialog_update_image_list ();
return lc_dialog->shell;
}
lc_dialog = g_new0 (LCDialog, 1);
lc_dialog->gimage = NULL;
lc_dialog->auto_follow_active = TRUE;
lc_dialog->shell = gimp_dialog_new (_("Layers, Channels & Paths"),
"layers_channels_paths",
lc_dialog_help_func,
"dialogs/layers_and_channels.html",
GTK_WIN_POS_NONE,
FALSE, TRUE, FALSE,
"_delete_event_", lc_dialog_close_callback,
lc_dialog, NULL, NULL, TRUE, TRUE,
NULL);
gtk_widget_hide (GTK_WIDGET (g_list_nth_data (gtk_container_children (GTK_CONTAINER (GTK_DIALOG (lc_dialog->shell)->vbox)), 0)));
gtk_widget_hide (GTK_DIALOG (lc_dialog->shell)->action_area);
/* Register the dialog */
dialog_register (lc_dialog->shell);
/* The toplevel vbox */
lc_dialog->subshell = gtk_vbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (lc_dialog->shell)->vbox),
lc_dialog->subshell);
/* The hbox to hold the image option menu box */
util_box = gtk_hbox_new (FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (util_box), 2);
gtk_box_pack_start (GTK_BOX (lc_dialog->subshell), util_box, FALSE, FALSE, 0);
/* The GIMP image option menu */
label = gtk_label_new (_("Image:"));
gtk_box_pack_start (GTK_BOX (util_box), label, FALSE, FALSE, 2);
gtk_widget_show (label);
lc_dialog->image_option_menu = gtk_option_menu_new ();
lc_dialog->image_menu =
lc_dialog_create_image_menu (&gimage, &default_index,
lc_dialog_image_menu_callback);
gtk_box_pack_start (GTK_BOX (util_box), lc_dialog->image_option_menu,
TRUE, TRUE, 0);
gimp_help_set_help_data (lc_dialog->image_option_menu, NULL, "#image_menu");
gtk_widget_show (lc_dialog->image_option_menu);
gtk_option_menu_set_menu (GTK_OPTION_MENU (lc_dialog->image_option_menu),
lc_dialog->image_menu);
if (default_index != -1)
gtk_option_menu_set_history
(GTK_OPTION_MENU (lc_dialog->image_option_menu), default_index);
/* The Auto-button */
auto_button = gtk_toggle_button_new_with_label (_("Auto"));
gtk_box_pack_start (GTK_BOX (util_box), auto_button, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (auto_button), "clicked",
(GtkSignalFunc) lc_dialog_auto_callback,
auto_button);
gimp_help_set_help_data (auto_button, NULL, "#auto_button");
gtk_widget_show (auto_button);
/* State will be set when the sub-dialogs exists (see below) */
gtk_widget_show (util_box);
separator = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (lc_dialog->subshell), separator,
FALSE, FALSE, 0);
gtk_widget_show (separator);
/* The notebook widget */
lc_dialog->notebook = gtk_notebook_new ();
gtk_container_set_border_width (GTK_CONTAINER (lc_dialog->notebook), 2);
gtk_box_pack_start (GTK_BOX (lc_dialog->subshell), lc_dialog->notebook,
TRUE, TRUE, 0);
label = gtk_label_new (_("Layers"));
gtk_notebook_append_page (GTK_NOTEBOOK (lc_dialog->notebook),
layers_dialog_create (), label);
gtk_widget_show (label);
label = gtk_label_new (_("Channels"));
gtk_notebook_append_page (GTK_NOTEBOOK (lc_dialog->notebook),
channels_dialog_create (), label);
gtk_widget_show (label);
label = gtk_label_new (_("Paths"));
gtk_notebook_append_page (GTK_NOTEBOOK (lc_dialog->notebook),
paths_dialog_create (), label);
gtk_widget_show (label);
/* Now all notebook pages exist, we can set the Auto-togglebutton */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_button),
lc_dialog->auto_follow_active);
gtk_widget_show (lc_dialog->notebook);
/* The action area */
gtk_container_set_border_width
(GTK_CONTAINER (GTK_DIALOG (lc_dialog->shell)->action_area), 1);
/* Make sure the channels page is realized */
gtk_notebook_set_page (GTK_NOTEBOOK (lc_dialog->notebook), 1);
gtk_notebook_set_page (GTK_NOTEBOOK (lc_dialog->notebook), 0);
gtk_signal_connect (GTK_OBJECT (image_context), "add",
GTK_SIGNAL_FUNC (lc_dialog_add_callback), NULL);
gtk_signal_connect (GTK_OBJECT (image_context), "remove",
GTK_SIGNAL_FUNC (lc_dialog_remove_callback), NULL);
gtk_signal_connect (GTK_OBJECT (gimp_context_get_user ()), "image_changed",
GTK_SIGNAL_FUNC (lc_dialog_change_image), NULL);
lc_dialog_update (gimage);
lc_dialog_update_image_list ();
gtk_widget_show (lc_dialog->subshell);
gdisplays_flush ();
return lc_dialog->shell;
}
void
lc_dialog_free (void)
{
if (lc_dialog == NULL)
return;
layers_dialog_free ();
channels_dialog_free ();
gtk_widget_destroy (lc_dialog->shell);
g_free (lc_dialog);
lc_dialog = NULL;
}
void
lc_dialog_rebuild (gint new_preview_size)
{
GimpImage *gimage;
gboolean flag;
gimage = NULL;
flag = FALSE;
if (lc_dialog)
{
flag = TRUE;
gimage = lc_dialog->gimage;
/* Unregister the dialog */
dialog_unregister (lc_dialog->shell);
lc_dialog_free ();
}
preview_size = new_preview_size;
render_setup (transparency_type, transparency_size);
if (flag)
gimp_dialog_factory_dialog_new (global_dialog_factory, "gimp:lc-dialog");
}
void
lc_dialog_flush (void)
{
if (! lc_dialog || lc_dialog->gimage == NULL)
return;
layers_dialog_flush ();
channels_dialog_flush ();
paths_dialog_flush ();
}
static gint
image_menu_preview_update_do (GimpImage *gimage)
{
if (lc_dialog)
{
gtk_container_foreach (GTK_CONTAINER (lc_dialog->image_menu),
lc_dialog_image_menu_preview_update_callback,
(gpointer) gimage);
}
return FALSE;
}
void
lc_dialog_menu_preview_dirty (GtkObject *obj,
gpointer data)
{
if (!preview_size)
return;
/* Update preview at a less busy time */
gtk_idle_add ((GtkFunction) image_menu_preview_update_do, obj);
}
void
lc_dialog_preview_update (GimpImage *gimage)
{
if (!preview_size)
return;
layers_dialog_invalidate_previews (gimage);
gtk_idle_add ((GtkFunction) image_menu_preview_update_do, gimage);
}
static void
lc_dialog_image_menu_preview_update_callback (GtkWidget *widget,
gpointer data)
{
GtkWidget *menu_preview;
GimpImage *gimage;
GimpImage *gimage_to_update;
gimage_to_update = (GimpImage *) data;
/* This is called via an idle function, so it is possible
* that the client_data no longer points to a GimpImage.. So don't
* pass it to the GIMP_IMAGE() cast function. We never deference
* it here anyways.
*/
menu_preview = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (widget),
"menu_preview");
gimage = (GimpImage *) gtk_object_get_data (GTK_OBJECT (widget),
"menu_preview_gimage");
if (menu_preview && gimage && gimage_to_update == gimage &&
gimp_container_have (image_context, (GimpObject *) gimage))
{
/* Must update the preview? */
lc_dialog_fill_preview_with_thumb (menu_preview,
gimage,
MENU_THUMBNAIL_SIZE,
MENU_THUMBNAIL_SIZE);
gtk_widget_queue_draw (GTK_WIDGET (menu_preview));
}
}
void
lc_dialog_update_image_list (void)
{
GimpImage *default_gimage;
gint default_index;
if (lc_dialog == NULL)
return;
default_gimage = lc_dialog->gimage;
lc_dialog->image_menu =
lc_dialog_create_image_menu (&default_gimage, &default_index,
lc_dialog_image_menu_callback);
gtk_option_menu_set_menu (GTK_OPTION_MENU (lc_dialog->image_option_menu),
lc_dialog->image_menu);
if (default_index != -1)
{
gtk_option_menu_set_history
(GTK_OPTION_MENU (lc_dialog->image_option_menu), default_index);
lc_dialog_update (default_gimage);
gdisplays_flush ();
if (! GTK_WIDGET_IS_SENSITIVE (lc_dialog->subshell))
gtk_widget_set_sensitive (lc_dialog->subshell, TRUE);
}
else
{
layers_dialog_clear ();
channels_dialog_clear ();
lc_dialog->gimage = NULL;
if (GTK_WIDGET_IS_SENSITIVE (lc_dialog->subshell))
gtk_widget_set_sensitive (lc_dialog->subshell, FALSE);
}
}
/**********************************/
/* Private L&C dialog functions */
/**********************************/
static void
lc_dialog_update (GimpImage *gimage)
{
if (! lc_dialog || lc_dialog->gimage == gimage)
return;
lc_dialog->gimage = gimage;
layers_dialog_update (gimage);
channels_dialog_update (gimage);
paths_dialog_update (gimage);
}
typedef struct
{
GimpImage **def;
gint *default_index;
GtkSignalFunc callback;
GtkWidget *menu;
gint num_items;
GimpImage *id;
} IMCBData;
static void
lc_dialog_fill_preview_with_thumb (GtkWidget *widget,
GimpImage *gimage,
gint width,
gint height)
{
guchar *drawable_data;
TempBuf *buf = NULL;
gint bpp;
gint x, y;
guchar *src;
gdouble r, g, b, a;
gdouble c0, c1;
guchar *p0, *p1, *even, *odd;
gdouble ratio;
bpp = 0; /* Only returned */
/* Get right aspect ratio */
if (gimage->width > gimage->height)
{
ratio = (gdouble) width / (gdouble) gimage->width;
}
else
{
ratio = (gdouble) height / (gdouble) gimage->height;
}
width = RINT (ratio * (gdouble) gimage->width);
height = RINT (ratio * (gdouble) gimage->height);
if (width < 1) width = 1;
if (height < 1) height = 1;
if (ratio > 1.0) /* Preview is scaling up! */
{
TempBuf *tmp;
tmp = gimp_viewable_get_new_preview (GIMP_VIEWABLE (gimage),
gimage->width,
gimage->height);
buf = temp_buf_scale (tmp, width, height);
temp_buf_free (tmp);
}
else
{
buf = gimp_viewable_get_new_preview (GIMP_VIEWABLE (gimage),
width,
height);
}
gtk_preview_size (GTK_PREVIEW (widget), width, height);
drawable_data = temp_buf_data (buf);
bpp = buf->bytes;
/* Draw the thumbnail with checks */
src = drawable_data;
even = g_malloc (width * 3);
odd = g_malloc (width * 3);
for (y = 0; y < height; y++)
{
p0 = even;
p1 = odd;
for (x = 0; x < width; x++)
{
if (bpp == 4)
{
r = ((gdouble) src[x * 4 + 0]) / 255.0;
g = ((gdouble) src[x * 4 + 1]) / 255.0;
b = ((gdouble) src[x * 4 + 2]) / 255.0;
a = ((gdouble) src[x * 4 + 3]) / 255.0;
}
else
{
r = ((gdouble) src[x * bpp + 0]) / 255.0;
g = b = r;
a = ((gdouble) src[x * bpp + 1]) / 255.0;
}
if ((x / GIMP_CHECK_SIZE_SM) & 1)
{
c0 = GIMP_CHECK_LIGHT;
c1 = GIMP_CHECK_DARK;
}
else
{
c0 = GIMP_CHECK_DARK;
c1 = GIMP_CHECK_LIGHT;
}
*p0++ = (c0 + (r - c0) * a) * 255.0;
*p0++ = (c0 + (g - c0) * a) * 255.0;
*p0++ = (c0 + (b - c0) * a) * 255.0;
*p1++ = (c1 + (r - c1) * a) * 255.0;
*p1++ = (c1 + (g - c1) * a) * 255.0;
*p1++ = (c1 + (b - c1) * a) * 255.0;
}
if ((y / GIMP_CHECK_SIZE_SM) & 1)
{
gtk_preview_draw_row (GTK_PREVIEW (widget),
(guchar *) odd, 0, y, width);
}
else
{
gtk_preview_draw_row (GTK_PREVIEW (widget),
(guchar *) even, 0, y, width);
}
src += width * bpp;
}
g_free (even);
g_free (odd);
temp_buf_free (buf);
}
static void
lc_dialog_create_image_menu_callback (gpointer im,
gpointer d)
{
GimpImage *gimage = GIMP_IMAGE (im);
IMCBData *data = (IMCBData *) d;
gchar *image_name;
gchar *menu_item_label;
GtkWidget *menu_item;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *wcolor_box;
GtkWidget *wlabel;
/* make sure the default index gets set to _something_, if possible */
if (*data->default_index == -1)
{
data->id = gimage;
*data->default_index = data->num_items;
}
if (gimage == *data->def)
{
data->id = *data->def;
*data->default_index = data->num_items;
}
image_name = g_basename (gimp_image_filename (gimage));
menu_item_label =
g_strdup_printf ("%s-%d", image_name, gimp_image_get_ID (gimage));
menu_item = gtk_menu_item_new();
gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
data->callback,
gimage);
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (menu_item), hbox);
gtk_widget_show (hbox);
if (preview_size)
{
vbox = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
wcolor_box = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_set_dither (GTK_PREVIEW (wcolor_box), GDK_RGB_DITHER_MAX);
gtk_widget_set_usize (GTK_WIDGET (wcolor_box),
MENU_THUMBNAIL_SIZE, MENU_THUMBNAIL_SIZE);
lc_dialog_fill_preview_with_thumb (wcolor_box,
gimage,
MENU_THUMBNAIL_SIZE,
MENU_THUMBNAIL_SIZE);
gtk_container_add (GTK_CONTAINER (vbox), wcolor_box);
gtk_widget_show (wcolor_box);
if (gtk_object_get_data (GTK_OBJECT (gimage),
"menu_preview_dirty") == NULL)
{
/* Only add this signal once */
gtk_object_set_data (GTK_OBJECT (gimage), "menu_preview_dirty",
(gpointer) 1);
gtk_signal_connect_after (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC (lc_dialog_menu_preview_dirty),
NULL);
}
gtk_object_set_data (GTK_OBJECT (menu_item), "menu_preview",
wcolor_box);
gtk_object_set_data (GTK_OBJECT (menu_item), "menu_preview_gimage",
gimage);
}
gtk_container_add (GTK_CONTAINER (data->menu), menu_item);
wlabel = gtk_label_new (menu_item_label);
gtk_misc_set_alignment (GTK_MISC (wlabel), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), wlabel, TRUE, TRUE, 4);
gtk_widget_show (wlabel);
gtk_widget_show (menu_item);
g_free (menu_item_label);
data->num_items++;
}
static GtkWidget *
lc_dialog_create_image_menu (GimpImage **def,
gint *default_index,
GtkSignalFunc callback)
{
IMCBData data;
data.def = def;
data.default_index = default_index;
data.callback = callback;
data.menu = gtk_menu_new ();
data.num_items = 0;
data.id = NULL;
*default_index = -1;
gimp_container_foreach (image_context,
lc_dialog_create_image_menu_callback,
&data);
if (! data.num_items)
{
GtkWidget *menu_item;
menu_item = gtk_menu_item_new_with_label (_("none"));
gtk_container_add (GTK_CONTAINER (data.menu), menu_item);
gtk_widget_show (menu_item);
}
*def = data.id;
return data.menu;
}
static void
lc_dialog_image_menu_callback (GtkWidget *widget,
gpointer data)
{
if (! lc_dialog)
return;
lc_dialog_update (GIMP_IMAGE (data));
gdisplays_flush ();
}
static void
lc_dialog_auto_callback (GtkWidget *widget,
gpointer data)
{
GimpContext *context;
if (! lc_dialog)
return;
lc_dialog->auto_follow_active =
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
context = gimp_context_get_user ();
if (lc_dialog->auto_follow_active)
lc_dialog_change_image (context,
gimp_context_get_image (context),
NULL);
}
static gint
lc_dialog_close_callback (GtkWidget *widget,
gpointer data)
{
if (! lc_dialog)
return TRUE;
lc_dialog->gimage = NULL;
gtk_widget_hide (lc_dialog->shell);
return TRUE;
}
static void
lc_dialog_add_callback (GimpContainer *container,
GimpImage *gimage,
gpointer data)
{
if (! lc_dialog)
return;
lc_dialog_update_image_list ();
}
static void
lc_dialog_remove_callback (GimpContainer *container,
GimpImage *gimage,
gpointer data)
{
if (! lc_dialog)
return;
lc_dialog_update_image_list ();
}
static void
lc_dialog_change_image (GimpContext *context,
GimpImage *gimage,
gpointer data)
{
if (! lc_dialog || ! lc_dialog->auto_follow_active)
return;
if (gimage && gimp_container_have (image_context, GIMP_OBJECT (gimage)))
{
lc_dialog_update (gimage);
lc_dialog_update_image_list ();
}
}
static void
lc_dialog_help_func (const gchar *help_data)
{
gchar *help_page;
gint page_num;
static gchar* dialog_names[] = { "layers", "channels", "paths" };
page_num =
gtk_notebook_get_current_page (GTK_NOTEBOOK (lc_dialog->notebook));
if (page_num > 2)
return;
help_page = g_strconcat (dialog_names[page_num], "/",
"dialogs/",
dialog_names[page_num], ".html",
NULL);
gimp_standard_help_func (help_page);
g_free (help_page);
}