home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / gimpprogress.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  8.9 KB  |  336 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <gtk/gtk.h>
  22.  
  23. #include "apptypes.h"
  24.  
  25. #include "appenv.h"
  26. #include "gdisplay.h"
  27. #include "gimpprogress.h"
  28. #include "gimpui.h"
  29.  
  30. #include "libgimp/gimpintl.h"
  31.  
  32.  
  33. struct gimp_progress_pvt {
  34.   GDisplay      *gdisp;             /* gdisp in use, or NULL*/
  35.  
  36.   /* next four fields are only valid if gdisp is NULL */
  37.   GtkWidget     *dialog;        /* progress dialog, NULL if using gdisp */
  38.   GtkWidget     *dialog_label;
  39.   GtkWidget     *progressbar;
  40.   GtkWidget     *cancelbutton;
  41.  
  42.   GtkSignalFunc  cancel_callback;   /* callback to remove, or NULL */
  43.   gpointer       cancel_data;
  44. };
  45.  
  46. /* prototypes */
  47. static void progress_signal_setup (gimp_progress *, GtkSignalFunc, gpointer);
  48.  
  49.  
  50. /* These progress bar routines are re-entrant, and so should be
  51.  * thread-safe. */
  52.  
  53.  
  54. /* Start a progress bar on "gdisp" with reason "message".  If "gdisp"
  55.  * is NULL, the progress bar is presented in a new dialog box.  If
  56.  * "message" is NULL, then no message is used.
  57.  *
  58.  * If "cancel_callback" is not NULL, it is attached to the progress
  59.  * bar cancel button's "clicked" signal, with data "cancel_data".  The
  60.  * cancel button is only made sensitive if the callback is set.
  61.  *
  62.  * It is an error to progress_start() a bar on a "gdisp" for which
  63.  * there is already a progress bar active.
  64.  *
  65.  * Progress bars with "important" set to TRUE will be shown to the
  66.  * user in any possible way.  Unimportant progress bars will not be
  67.  * shown to the user if it would mean creating a new window.
  68.  */
  69. gimp_progress *
  70. progress_start (GDisplay      *gdisp,
  71.         const char    *message,
  72.         gboolean       important,
  73.         GtkSignalFunc  cancel_callback,
  74.         gpointer       cancel_data)
  75. {
  76.   gimp_progress *p;
  77.   guint cid;
  78.   GtkWidget *vbox;
  79.  
  80.   p = g_new (gimp_progress, 1);
  81.  
  82.   p->gdisp = gdisp;
  83.   p->dialog = NULL;
  84.   p->cancel_callback = NULL;
  85.   p->cancel_data = NULL;
  86.  
  87.   /* do we have a useful gdisplay and statusarea? */
  88.   if (gdisp && GTK_WIDGET_VISIBLE (gdisp->statusarea))
  89.     {
  90.       if (message)
  91.     {
  92.       cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar),
  93.                           "progress");
  94.  
  95.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), cid, message);
  96.     }
  97.  
  98.       /* really need image locking to stop multiple people going at
  99.        * the image */
  100.       if (gdisp->progressid)
  101.     g_warning("%d progress bars already active for display %p\n",
  102.           gdisp->progressid, gdisp);
  103.       gdisp->progressid++;
  104.     }
  105.   else
  106.     {
  107.       /* unimporant progress indications are occasionally failed */
  108.       if (!important)
  109.     {
  110.       g_free (p);
  111.       return NULL;
  112.     }
  113.  
  114.       p->gdisp = NULL;
  115.       p->dialog = gimp_dialog_new (_("Progress"), "plug_in_progress",
  116.                    NULL, NULL,
  117.                    GTK_WIN_POS_NONE,
  118.                    FALSE, TRUE, FALSE,
  119.  
  120.                    _("Cancel"), NULL,
  121.                    NULL, NULL, &p->cancelbutton, TRUE, TRUE,
  122.  
  123.                    NULL);
  124.  
  125.       vbox = gtk_vbox_new (FALSE, 2);
  126.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  127.       gtk_container_add (GTK_CONTAINER (GTK_DIALOG (p->dialog)->vbox), vbox);
  128.       gtk_widget_show (vbox);
  129.  
  130.       p->dialog_label = gtk_label_new (message ? message :
  131.                        _("Please wait..."));
  132.       gtk_misc_set_alignment (GTK_MISC (p->dialog_label), 0.0, 0.5);
  133.       gtk_box_pack_start (GTK_BOX (vbox), p->dialog_label, FALSE, TRUE, 0);
  134.       gtk_widget_show (p->dialog_label);
  135.  
  136.       p->progressbar = gtk_progress_bar_new ();
  137.       gtk_widget_set_usize (p->progressbar, 150, 20);
  138.       gtk_box_pack_start (GTK_BOX (vbox), p->progressbar, TRUE, TRUE, 0);
  139.       gtk_widget_show (p->progressbar);
  140.  
  141.       gtk_widget_show (p->dialog);
  142.     }
  143.  
  144.   progress_signal_setup (p, cancel_callback, cancel_data);
  145.  
  146.   return p;
  147. }
  148.  
  149.  
  150. /* Update the message and/or the callbacks for a progress and reset
  151.  * the bar to zero, with the minimum of disturbance to the user. */
  152. gimp_progress *
  153. progress_restart (gimp_progress *p,
  154.           const char    *message,
  155.           GtkSignalFunc  cancel_callback,
  156.           gpointer       cancel_data)
  157. {
  158.   int cid;
  159.   GtkWidget *bar;
  160.  
  161.   g_return_val_if_fail (p != NULL, p);
  162.  
  163.   /* change the message */
  164.   if (p->gdisp)
  165.     {
  166.       cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (p->gdisp->statusbar),
  167.                       "progress");
  168.       gtk_statusbar_pop (GTK_STATUSBAR (p->gdisp->statusbar), cid);
  169.  
  170.       if (message)
  171.     gtk_statusbar_push (GTK_STATUSBAR (p->gdisp->statusbar), cid, message);
  172.  
  173.       bar = p->gdisp->progressbar;
  174.     }
  175.   else
  176.     {
  177.       gtk_label_set_text (GTK_LABEL (p->dialog_label),
  178.               message ? message : _("Please wait..."));
  179.       bar = p->progressbar;
  180.     }
  181.  
  182.   /* reset the progress bar */
  183.   gtk_progress_bar_update (GTK_PROGRESS_BAR (bar), 0.0);
  184.  
  185.   /* do we need to change the callbacks? */
  186.   progress_signal_setup (p, cancel_callback, cancel_data);
  187.  
  188.   return p;
  189. }
  190.  
  191.  
  192. void
  193. progress_update (gimp_progress *progress,
  194.          float          percentage)
  195. {
  196.   GtkWidget *bar;
  197.  
  198.   g_return_if_fail (progress != NULL);
  199.  
  200.   if (!(percentage >= 0.0 && percentage <= 1.0))
  201.     return;
  202.  
  203.   /* do we have a dialog box, or are we using the statusbar? */
  204.   if (progress->gdisp)
  205.     bar = progress->gdisp->progressbar;
  206.   else
  207.     bar = progress->progressbar;
  208.  
  209.   gtk_progress_bar_update (GTK_PROGRESS_BAR (bar), percentage);
  210. }
  211.  
  212.  
  213. /* This function's prototype is conveniently the same as progress_func_t */
  214. void
  215. progress_update_and_flush (int       ymin,
  216.                int       ymax,
  217.                int       curr_y,
  218.                gpointer  data)
  219. {
  220.   progress_update ((gimp_progress *)data,
  221.            (float)(curr_y - ymin) / (float)(ymax - ymin));
  222.  
  223.   /* HACK until we do long-running operations in the gtk idle thread */
  224.   while (gtk_events_pending())
  225.     gtk_main_iteration();
  226. }
  227.  
  228.  
  229. /* Step the progress bar by one percent, wrapping at 100% */
  230. void
  231. progress_step (gimp_progress *progress)
  232. {
  233.   GtkWidget *bar;
  234.   float val;
  235.  
  236.   g_return_if_fail (progress != NULL);
  237.  
  238.   if (progress->gdisp)
  239.     bar = progress->gdisp->progressbar;
  240.   else
  241.     bar = progress->progressbar;
  242.  
  243.   val = gtk_progress_get_current_percentage (GTK_PROGRESS (bar)) + 0.01;
  244.   if (val > 1.0)
  245.     val = 0.0;
  246.  
  247.   progress_update (progress, val);
  248. }
  249.  
  250.  
  251. /* Finish using the progress bar "p" */
  252. void
  253. progress_end (gimp_progress *p)
  254. {
  255.   int cid;
  256.  
  257.   g_return_if_fail (p != NULL);
  258.  
  259.   /* remove all callbacks so they don't get called while we're
  260.    * destroying widgets */
  261.   progress_signal_setup (p, NULL, NULL);
  262.  
  263.   if (p->gdisp)
  264.     {
  265.       cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (p->gdisp->statusbar),
  266.                       "progress");
  267.       gtk_statusbar_pop (GTK_STATUSBAR (p->gdisp->statusbar), cid);
  268.  
  269.       gtk_progress_bar_update (GTK_PROGRESS_BAR (p->gdisp->progressbar), 0.0);
  270.  
  271.       if (p->gdisp->progressid > 0)
  272.     p->gdisp->progressid--;
  273.     }
  274.   else
  275.     {
  276.       gtk_widget_destroy (p->dialog);
  277.     }
  278.  
  279.   g_free (p);
  280. }
  281.  
  282.  
  283. /* Helper function to add or remove signals */
  284. static void
  285. progress_signal_setup (gimp_progress *p,
  286.                GtkSignalFunc  cancel_callback,
  287.                gpointer       cancel_data)
  288. {
  289.   GtkWidget *button;
  290.   GtkWidget *dialog;
  291.  
  292.   if (p->cancel_callback == cancel_callback && p->cancel_data == cancel_data)
  293.     return;
  294.  
  295.   /* are we using the statusbar or a freestanding dialog? */
  296.   if (p->gdisp)
  297.     {
  298.       dialog = NULL;
  299.       button = p->gdisp->cancelbutton;
  300.     }
  301.   else
  302.     {
  303.       dialog = p->dialog;
  304.       button = p->cancelbutton;
  305.     }
  306.  
  307.   /* remove any existing signal handlers */
  308.   if (p->cancel_callback)
  309.     {
  310.       gtk_signal_disconnect_by_func (GTK_OBJECT (button),
  311.                      p->cancel_callback, p->cancel_data);
  312.       if (dialog)
  313.     gtk_signal_disconnect_by_func (GTK_OBJECT (dialog),
  314.                        p->cancel_callback, p->cancel_data);
  315.     }
  316.  
  317.   /* add the new handlers */
  318.   if (cancel_callback)
  319.     {
  320.       gtk_signal_connect (GTK_OBJECT (button), "clicked",
  321.               cancel_callback, cancel_data);
  322.  
  323.       if (dialog)
  324.     gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  325.                 cancel_callback, cancel_data);
  326.     }
  327.  
  328.   gtk_widget_set_sensitive (GTK_WIDGET (button),
  329.                 cancel_callback ? TRUE : FALSE);
  330.  
  331.   p->cancel_callback = cancel_callback;
  332.   p->cancel_data     = cancel_data;
  333. }
  334.  
  335. /* End of gimpprogress.c */
  336.