gimp/app/gimpprogress.c
Michael Natterer 002aa905db app/Makefile.am app/gimphelp.[ch] new files
1999-09-27  Michael Natterer  <mitch@gimp.org>

	* app/Makefile.am
	* app/gimphelp.[ch]
	* app/gimpui.[ch]: new files

	* app/interface.[ch]
	* app/preferences_dialog.[ch]

	The GIMP Help System part 1: Press "F1" in any dialog to pop up
	the help page for this dialog.

	Moved the widget constructors from preferences_dialog.[ch] and the
	query boxes from interface.[ch] to gimpui.[ch].

	The dialog constructors take a help_func and a help_data
	parameter and install the "F1" accelerator which emits the new
	"help" signal.

	The "help" signal callback calls help_func(help_data) which finally
	has to call gimp_help() which in turn invokes the help browser.

	Still have to find a proper way to (1) prevent "F1" being assigned
	to some menu item and (2) to catch "F1" while browsing the menu
	trees in order to pop up the help for the selected item.

	* app/menus.c: a <Toolbox>/File/Help... menu item.
	* app/commands.[ch]: a command callback for the "Help..." menu item.

	* app/gimprc.[ch]: new boolean gimprc variable "use_help".

	* app/info_dialog.[ch]: pass a help function and data to the info
	dialog constructor.

	* app/tools.[ch]: store the tools help page names in the tool info
	structure. Export a special tools_help_func() which shows the help
	page for the active tool.

	* app/[all files calling a dialog constructor]: pass the dialog's
	help page to the constructor.

	Most dialogs are now created by gimp_dialog_new() which also sets
	up the action_area and the WM delete event callback, so I removed
	the resp. code from these files.

	Fixed some minor bugs and did some other stuff but didn't change
	any logic except dialog creation.

	* plug-ins/helpbrowser/helpbrowser.c: don't try to call a running
	help browser and don't install any menu path (all done in
	app/gimphelp.[ch] now).
1999-09-27 17:58:10 +00:00

330 lines
8.6 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 "appenv.h"
#include "gdisplay.h"
#include "libgimp/gimpintl.h"
#include "gimpprogress.h"
#include "gimpui.h"
struct gimp_progress_pvt {
GDisplay *gdisp; /* gdisp in use, or NULL*/
/* next four fields are only valid if gdisp is NULL */
GtkWidget *dialog; /* progress dialog, NULL if using gdisp */
GtkWidget *dialog_label;
GtkWidget *progressbar;
GtkWidget *cancelbutton;
GtkSignalFunc cancel_callback; /* callback to remove, or NULL */
gpointer cancel_data;
};
#define DEFAULT_PROGRESS_MESSAGE _("Please wait...")
/* prototypes */
static void progress_signal_setup (gimp_progress *, GtkSignalFunc, gpointer);
/* These progress bar routines are re-entrant, and so should be
* thread-safe. */
/* Start a progress bar on "gdisp" with reason "message". If "gdisp"
* is NULL, the progress bar is presented in a new dialog box. If
* "message" is NULL, then no message is used.
*
* If "cancel_callback" is not NULL, it is attached to the progress
* bar cancel button's "clicked" signal, with data "cancel_data". The
* cancel button is only made sensitive if the callback is set.
*
* It is an error to progress_start() a bar on a "gdisp" for which
* there is already a progress bar active.
*
* Progress bars with "important" set to TRUE will be shown to the
* user in any possible way. Unimportant progress bars will not be
* shown to the user if it would mean creating a new window.
*/
gimp_progress *
progress_start (GDisplay *gdisp,
const char *message,
gboolean important,
GtkSignalFunc cancel_callback,
gpointer cancel_data)
{
gimp_progress *p;
guint cid;
GtkWidget *vbox;
p = g_new (gimp_progress, 1);
p->gdisp = gdisp;
p->dialog = NULL;
p->cancel_callback = NULL;
p->cancel_data = NULL;
/* do we have a useful gdisplay and statusarea? */
if (gdisp && GTK_WIDGET_VISIBLE (gdisp->statusarea))
{
if (message)
{
cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar),
"progress");
gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), cid, message);
}
/* really need image locking to stop multiple people going at
* the image */
if (gdisp->progressid)
g_warning("%d progress bars already active for display %p\n",
gdisp->progressid, gdisp);
gdisp->progressid++;
}
else
{
/* unimporant progress indications are occasionally failed */
if (!important)
{
g_free (p);
return NULL;
}
p->gdisp = NULL;
p->dialog = gimp_dialog_new (_("Progress"), "plug_in_progress",
NULL, NULL,
GTK_WIN_POS_NONE,
FALSE, TRUE, FALSE,
_("Cancel"), NULL,
NULL, &p->cancelbutton, TRUE, TRUE,
NULL);
vbox = gtk_vbox_new (FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (p->dialog)->vbox), vbox);
gtk_widget_show (vbox);
p->dialog_label = gtk_label_new (message ? message :
DEFAULT_PROGRESS_MESSAGE);
gtk_misc_set_alignment (GTK_MISC (p->dialog_label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), p->dialog_label, FALSE, TRUE, 0);
gtk_widget_show (p->dialog_label);
p->progressbar = gtk_progress_bar_new ();
gtk_widget_set_usize (p->progressbar, 150, 20);
gtk_box_pack_start (GTK_BOX (vbox), p->progressbar, TRUE, TRUE, 0);
gtk_widget_show (p->progressbar);
gtk_widget_show (p->dialog);
}
progress_signal_setup (p, cancel_callback, cancel_data);
return p;
}
/* Update the message and/or the callbacks for a progress and reset
* the bar to zero, with the minimum of disturbance to the user. */
gimp_progress *
progress_restart (gimp_progress *p,
const char *message,
GtkSignalFunc cancel_callback,
gpointer cancel_data)
{
int cid;
GtkWidget *bar;
g_return_val_if_fail (p != NULL, p);
/* change the message */
if (p->gdisp)
{
cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (p->gdisp->statusbar),
"progress");
gtk_statusbar_pop (GTK_STATUSBAR (p->gdisp->statusbar), cid);
if (message)
gtk_statusbar_push (GTK_STATUSBAR (p->gdisp->statusbar), cid, message);
bar = p->gdisp->progressbar;
}
else
{
gtk_label_set_text (GTK_LABEL (p->dialog_label),
message ? message : DEFAULT_PROGRESS_MESSAGE);
bar = p->progressbar;
}
/* reset the progress bar */
gtk_progress_bar_update (GTK_PROGRESS_BAR (bar), 0.0);
/* do we need to change the callbacks? */
progress_signal_setup (p, cancel_callback, cancel_data);
return p;
}
void
progress_update (gimp_progress *progress,
float percentage)
{
GtkWidget *bar;
g_return_if_fail (progress != NULL);
if (!(percentage >= 0.0 && percentage <= 1.0))
return;
/* do we have a dialog box, or are we using the statusbar? */
if (progress->gdisp)
bar = progress->gdisp->progressbar;
else
bar = progress->progressbar;
gtk_progress_bar_update (GTK_PROGRESS_BAR (bar), percentage);
}
/* This function's prototype is conveniently the same as progress_func_t */
void
progress_update_and_flush (int ymin,
int ymax,
int curr_y,
gpointer data)
{
progress_update ((gimp_progress *)data,
(float)(curr_y - ymin) / (float)(ymax - ymin));
/* HACK until we do long-running operations in the gtk idle thread */
while (gtk_events_pending())
gtk_main_iteration();
}
/* Step the progress bar by one percent, wrapping at 100% */
void
progress_step (gimp_progress *progress)
{
GtkWidget *bar;
float val;
g_return_if_fail (progress != NULL);
if (progress->gdisp)
bar = progress->gdisp->progressbar;
else
bar = progress->progressbar;
val = gtk_progress_get_current_percentage (GTK_PROGRESS (bar)) + 0.01;
if (val > 1.0)
val = 0.0;
progress_update (progress, val);
}
/* Finish using the progress bar "p" */
void
progress_end (gimp_progress *p)
{
int cid;
g_return_if_fail (p != NULL);
/* remove all callbacks so they don't get called while we're
* destroying widgets */
progress_signal_setup (p, NULL, NULL);
if (p->gdisp)
{
cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (p->gdisp->statusbar),
"progress");
gtk_statusbar_pop (GTK_STATUSBAR (p->gdisp->statusbar), cid);
gtk_progress_bar_update (GTK_PROGRESS_BAR (p->gdisp->progressbar), 0.0);
if (p->gdisp->progressid > 0)
p->gdisp->progressid--;
}
else
{
gtk_widget_destroy (p->dialog);
}
g_free (p);
}
/* Helper function to add or remove signals */
static void
progress_signal_setup (gimp_progress *p,
GtkSignalFunc cancel_callback,
gpointer cancel_data)
{
GtkWidget *button;
GtkWidget *dialog;
if (p->cancel_callback == cancel_callback && p->cancel_data == cancel_data)
return;
/* are we using the statusbar or a freestanding dialog? */
if (p->gdisp)
{
dialog = NULL;
button = p->gdisp->cancelbutton;
}
else
{
dialog = p->dialog;
button = p->cancelbutton;
}
/* remove any existing signal handlers */
if (p->cancel_callback)
{
gtk_signal_disconnect_by_func (GTK_OBJECT (button),
p->cancel_callback, p->cancel_data);
if (dialog)
gtk_signal_disconnect_by_func (GTK_OBJECT (dialog),
p->cancel_callback, p->cancel_data);
}
/* add the new handlers */
if (cancel_callback)
{
gtk_signal_connect (GTK_OBJECT (button), "clicked",
cancel_callback, cancel_data);
if (dialog)
gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
cancel_callback, cancel_data);
}
gtk_widget_set_sensitive (GTK_WIDGET (button),
cancel_callback ? TRUE : FALSE);
p->cancel_callback = cancel_callback;
p->cancel_data = cancel_data;
}
/* End of gimpprogress.c */