gimpstatusbar.c 45.8 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program Copyright (C) 1995
2
 * 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 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 */

#include "config.h"

20 21
#include <string.h>

22
#include <gegl.h>
23 24
#include <gtk/gtk.h>

25
#include "libgimpbase/gimpbase.h"
26
#include "libgimpmath/gimpmath.h"
27 28
#include "libgimpwidgets/gimpwidgets.h"

29 30
#include "display-types.h"

31 32
#include "config/gimpdisplayconfig.h"

33
#include "core/gimpimage.h"
34
#include "core/gimpprogress.h"
35

36
#include "widgets/gimpwidgets-utils.h"
37

38
#include "gimpdisplay.h"
39
#include "gimpdisplayshell.h"
40
#include "gimpdisplayshell-scale.h"
41
#include "gimpimagewindow.h"
42
#include "gimpscalecombobox.h"
43 44
#include "gimpstatusbar.h"

45
#include "gimp-intl.h"
46 47


48 49 50
/*  maximal width of the string holding the cursor-coordinates  */
#define CURSOR_LEN        256

51 52 53
/*  the spacing of the hbox                                     */
#define HBOX_SPACING        1

54 55
/*  spacing between the icon and the statusbar label            */
#define ICON_SPACING        2
56

57
/*  timeout (in milliseconds) for temporary statusbar messages  */
58
#define MESSAGE_TIMEOUT  8000
59 60


61 62 63 64 65
typedef struct _GimpStatusbarMsg GimpStatusbarMsg;

struct _GimpStatusbarMsg
{
  guint  context_id;
66
  gchar *stock_id;
67
  gchar *text;
68 69
};

70

71
static void     gimp_statusbar_progress_iface_init (GimpProgressInterface *iface);
72

73
static void     gimp_statusbar_dispose            (GObject           *object);
74
static void     gimp_statusbar_finalize           (GObject           *object);
75

76
static void     gimp_statusbar_hbox_size_request  (GtkWidget         *widget,
77 78
                                                   GtkRequisition    *requisition,
                                                   GimpStatusbar     *statusbar);
79

80
static GimpProgress *
81 82 83 84 85 86 87 88 89 90
                gimp_statusbar_progress_start     (GimpProgress      *progress,
                                                   const gchar       *message,
                                                   gboolean           cancelable);
static void     gimp_statusbar_progress_end       (GimpProgress      *progress);
static gboolean gimp_statusbar_progress_is_active (GimpProgress      *progress);
static void     gimp_statusbar_progress_set_text  (GimpProgress      *progress,
                                                   const gchar       *message);
static void     gimp_statusbar_progress_set_value (GimpProgress      *progress,
                                                   gdouble            percentage);
static gdouble  gimp_statusbar_progress_get_value (GimpProgress      *progress);
91
static void     gimp_statusbar_progress_pulse     (GimpProgress      *progress);
92 93 94 95 96
static gboolean gimp_statusbar_progress_message   (GimpProgress      *progress,
                                                   Gimp              *gimp,
                                                   GimpMessageSeverity severity,
                                                   const gchar       *domain,
                                                   const gchar       *message);
97 98 99
static void     gimp_statusbar_progress_canceled  (GtkWidget         *button,
                                                   GimpStatusbar     *statusbar);

100
static gboolean gimp_statusbar_label_expose       (GtkWidget         *widget,
101 102 103
                                                   GdkEventExpose    *event,
                                                   GimpStatusbar     *statusbar);

104 105 106 107 108
static void     gimp_statusbar_update             (GimpStatusbar     *statusbar);
static void     gimp_statusbar_unit_changed       (GimpUnitComboBox  *combo,
                                                   GimpStatusbar     *statusbar);
static void     gimp_statusbar_scale_changed      (GimpScaleComboBox *combo,
                                                   GimpStatusbar     *statusbar);
109 110
static void     gimp_statusbar_scale_activated    (GimpScaleComboBox *combo,
                                                   GimpStatusbar     *statusbar);
111 112
static void     gimp_statusbar_shell_scaled       (GimpDisplayShell  *shell,
                                                   GimpStatusbar     *statusbar);
113 114 115
static void     gimp_statusbar_shell_status_notify(GimpDisplayShell  *shell,
                                                   const GParamSpec  *pspec,
                                                   GimpStatusbar     *statusbar);
116 117
static guint    gimp_statusbar_get_context_id     (GimpStatusbar     *statusbar,
                                                   const gchar       *context);
118
static gboolean gimp_statusbar_temp_timeout       (GimpStatusbar     *statusbar);
119

120 121
static void     gimp_statusbar_msg_free           (GimpStatusbarMsg  *msg);

122
static gchar *  gimp_statusbar_vprintf            (const gchar       *format,
123
                                                   va_list            args) G_GNUC_PRINTF (1, 0);
124

125

126
G_DEFINE_TYPE_WITH_CODE (GimpStatusbar, gimp_statusbar, GTK_TYPE_STATUSBAR,
127
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS,
128
                                                gimp_statusbar_progress_iface_init))
129

130
#define parent_class gimp_statusbar_parent_class
131 132 133 134 135


static void
gimp_statusbar_class_init (GimpStatusbarClass *klass)
{
136
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
137

138 139
  object_class->dispose  = gimp_statusbar_dispose;
  object_class->finalize = gimp_statusbar_finalize;
140 141
}

142 143 144 145 146 147 148 149 150 151
static void
gimp_statusbar_progress_iface_init (GimpProgressInterface *iface)
{
  iface->start     = gimp_statusbar_progress_start;
  iface->end       = gimp_statusbar_progress_end;
  iface->is_active = gimp_statusbar_progress_is_active;
  iface->set_text  = gimp_statusbar_progress_set_text;
  iface->set_value = gimp_statusbar_progress_set_value;
  iface->get_value = gimp_statusbar_progress_get_value;
  iface->pulse     = gimp_statusbar_progress_pulse;
152
  iface->message   = gimp_statusbar_progress_message;
153 154
}

155 156 157
static void
gimp_statusbar_init (GimpStatusbar *statusbar)
{
158
  GtkWidget     *hbox;
159
  GtkWidget     *hbox2;
160
  GtkWidget     *image;
161
  GtkWidget     *label;
162
  GimpUnitStore *store;
163
  GList         *children;
164

165 166 167 168 169 170 171 172
  statusbar->shell          = NULL;
  statusbar->messages       = NULL;
  statusbar->context_ids    = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                     g_free, NULL);
  statusbar->seq_context_id = 1;

  statusbar->temp_context_id =
    gimp_statusbar_get_context_id (statusbar, "gimp-statusbar-temp");
173

174 175 176
  statusbar->cursor_format_str[0]   = '\0';
  statusbar->cursor_format_str_f[0] = '\0';
  statusbar->length_format_str[0]   = '\0';
177

178
  statusbar->progress_active      = FALSE;
179
  statusbar->progress_shown       = FALSE;
180

181 182 183
  /*  remove the message label from the message area  */
  hbox = gtk_statusbar_get_message_area (GTK_STATUSBAR (statusbar));
  gtk_box_set_spacing (GTK_BOX (hbox), HBOX_SPACING);
184

185 186 187
  children = gtk_container_get_children (GTK_CONTAINER (hbox));
  statusbar->label = g_object_ref (children->data);
  g_list_free (children);
188

189
  gtk_container_remove (GTK_CONTAINER (hbox), statusbar->label);
190

191 192 193 194
  g_signal_connect (hbox, "size-request",
                    G_CALLBACK (gimp_statusbar_hbox_size_request),
                    statusbar);

195
  statusbar->cursor_label = gtk_label_new ("8888, 8888");
196
  gtk_misc_set_alignment (GTK_MISC (statusbar->cursor_label), 0.5, 0.5);
197
  gtk_box_pack_start (GTK_BOX (hbox), statusbar->cursor_label, FALSE, FALSE, 0);
198 199
  gtk_widget_show (statusbar->cursor_label);

200
  store = gimp_unit_store_new (2);
201
  statusbar->unit_combo = gimp_unit_combo_box_new_with_model (store);
202 203
  g_object_unref (store);

204
  gtk_widget_set_can_focus (statusbar->unit_combo, FALSE);
205
  g_object_set (statusbar->unit_combo, "focus-on-click", FALSE, NULL);
206
  gtk_box_pack_start (GTK_BOX (hbox), statusbar->unit_combo, FALSE, FALSE, 0);
207
  gtk_widget_show (statusbar->unit_combo);
208

209
  g_signal_connect (statusbar->unit_combo, "changed",
210 211 212
                    G_CALLBACK (gimp_statusbar_unit_changed),
                    statusbar);

213
  statusbar->scale_combo = gimp_scale_combo_box_new ();
214
  gtk_widget_set_can_focus (statusbar->scale_combo, FALSE);
215
  g_object_set (statusbar->scale_combo, "focus-on-click", FALSE, NULL);
216
  gtk_box_pack_start (GTK_BOX (hbox), statusbar->scale_combo, FALSE, FALSE, 0);
217 218 219 220 221 222
  gtk_widget_show (statusbar->scale_combo);

  g_signal_connect (statusbar->scale_combo, "changed",
                    G_CALLBACK (gimp_statusbar_scale_changed),
                    statusbar);

223 224 225 226
  g_signal_connect (statusbar->scale_combo, "entry-activated",
                    G_CALLBACK (gimp_statusbar_scale_activated),
                    statusbar);

227 228
  /*  put the label back into the message area  */
  gtk_box_pack_start (GTK_BOX (hbox), statusbar->label, TRUE, TRUE, 1);
229

230
  g_object_unref (statusbar->label);
231

232
  g_signal_connect_after (statusbar->label, "expose-event",
233
                          G_CALLBACK (gimp_statusbar_label_expose),
234
                          statusbar);
235

236 237 238 239 240 241 242 243
  statusbar->progressbar = g_object_new (GTK_TYPE_PROGRESS_BAR,
                                         "text-xalign", 0.0,
                                         "text-yalign", 0.5,
                                         "ellipsize",   PANGO_ELLIPSIZE_END,
                                         NULL);
  gtk_box_pack_start (GTK_BOX (hbox), statusbar->progressbar, TRUE, TRUE, 0);
  /*  don't show the progress bar  */

244 245 246 247
  /*  construct the cancel button's contents manually because we
   *  always want image and label regardless of settings, and we want
   *  a menu size image.
   */
248
  statusbar->cancel_button = gtk_button_new ();
249
  gtk_widget_set_can_focus (statusbar->cancel_button, FALSE);
250 251
  gtk_button_set_relief (GTK_BUTTON (statusbar->cancel_button),
                         GTK_RELIEF_NONE);
252
  gtk_widget_set_sensitive (statusbar->cancel_button, FALSE);
253 254
  gtk_box_pack_end (GTK_BOX (hbox),
                    statusbar->cancel_button, FALSE, FALSE, 0);
255
  /*  don't show the cancel button  */
256

257 258 259 260
  hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_container_add (GTK_CONTAINER (statusbar->cancel_button), hbox2);
  gtk_widget_show (hbox2);

261
  image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_MENU);
262
  gtk_box_pack_start (GTK_BOX (hbox2), image, FALSE, FALSE, 2);
263 264
  gtk_widget_show (image);

265 266 267 268
  label = gtk_label_new ("Cancel");
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 2);
  gtk_widget_show (label);

269 270 271 272 273
  g_signal_connect (statusbar->cancel_button, "clicked",
                    G_CALLBACK (gimp_statusbar_progress_canceled),
                    statusbar);
}

274 275 276 277 278 279 280 281 282 283 284 285 286 287
static void
gimp_statusbar_dispose (GObject *object)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (object);

  if (statusbar->temp_timeout_id)
    {
      g_source_remove (statusbar->temp_timeout_id);
      statusbar->temp_timeout_id = 0;
    }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

288
static void
289
gimp_statusbar_finalize (GObject *object)
290
{
291
  GimpStatusbar *statusbar = GIMP_STATUSBAR (object);
292

293 294 295 296 297 298
  if (statusbar->icon)
    {
      g_object_unref (statusbar->icon);
      statusbar->icon = NULL;
    }

299 300
  g_slist_free_full (statusbar->messages,
                     (GDestroyNotify) gimp_statusbar_msg_free);
301
  statusbar->messages = NULL;
302

303 304 305 306 307
  if (statusbar->context_ids)
    {
      g_hash_table_destroy (statusbar->context_ids);
      statusbar->context_ids = NULL;
    }
308

309
  G_OBJECT_CLASS (parent_class)->finalize (object);
310 311
}

312
static void
313 314 315
gimp_statusbar_hbox_size_request (GtkWidget      *widget,
                                  GtkRequisition *requisition,
                                  GimpStatusbar  *statusbar)
316
{
317 318
  GtkRequisition child_requisition;
  gint           width = 0;
319 320 321

  /*  also consider the children which can be invisible  */

322 323
  gtk_widget_size_request (statusbar->cursor_label, &child_requisition);
  width += child_requisition.width;
324
  requisition->height = MAX (requisition->height,
325
                             child_requisition.height);
326

327
  gtk_widget_size_request (statusbar->unit_combo, &child_requisition);
328
  width += child_requisition.width;
329
  requisition->height = MAX (requisition->height,
330
                             child_requisition.height);
331 332

  gtk_widget_size_request (statusbar->scale_combo, &child_requisition);
333
  width += child_requisition.width;
334
  requisition->height = MAX (requisition->height,
335
                             child_requisition.height);
336 337

  gtk_widget_size_request (statusbar->progressbar, &child_requisition);
338
  requisition->height = MAX (requisition->height,
339
                             child_requisition.height);
340

341 342 343 344
  gtk_widget_size_request (statusbar->label, &child_requisition);
  requisition->height = MAX (requisition->height,
                             child_requisition.height);

345
  gtk_widget_size_request (statusbar->cancel_button, &child_requisition);
346
  requisition->height = MAX (requisition->height,
347
                             child_requisition.height);
348

349
  requisition->width = MAX (requisition->width, width + 32);
350 351
}

352 353 354 355 356 357 358 359 360 361 362
static GimpProgress *
gimp_statusbar_progress_start (GimpProgress *progress,
                               const gchar  *message,
                               gboolean      cancelable)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (! statusbar->progress_active)
    {
      GtkWidget *bar = statusbar->progressbar;

363 364 365
      statusbar->progress_active = TRUE;
      statusbar->progress_value  = 0.0;

366
      gimp_statusbar_push (statusbar, "progress", NULL, "%s", message);
367 368 369
      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0);
      gtk_widget_set_sensitive (statusbar->cancel_button, cancelable);

370
      if (cancelable)
371 372 373 374 375 376 377 378 379 380 381 382
        {
          if (message)
            {
              gchar *tooltip = g_strdup_printf (_("Cancel <i>%s</i>"), message);

              gimp_help_set_help_data_with_markup (statusbar->cancel_button,
                                                   tooltip, NULL);
              g_free (tooltip);
            }

          gtk_widget_show (statusbar->cancel_button);
        }
383

384
      gtk_widget_show (statusbar->progressbar);
385
      gtk_widget_hide (statusbar->label);
386 387 388 389

      /*  This call is needed so that the progress bar is drawn in the
       *  correct place. Probably due a bug in GTK+.
       */
390
      gtk_container_resize_children (GTK_CONTAINER (statusbar));
391

392
      if (! gtk_widget_get_visible (GTK_WIDGET (statusbar)))
393 394 395 396 397
        {
          gtk_widget_show (GTK_WIDGET (statusbar));
          statusbar->progress_shown = TRUE;
        }

398
      gimp_widget_flush_expose (bar);
399

400 401
      gimp_statusbar_override_window_title (statusbar);

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
      return progress;
    }

  return NULL;
}

static void
gimp_statusbar_progress_end (GimpProgress *progress)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (statusbar->progress_active)
    {
      GtkWidget *bar = statusbar->progressbar;

417 418 419 420 421 422
      if (statusbar->progress_shown)
        {
          gtk_widget_hide (GTK_WIDGET (statusbar));
          statusbar->progress_shown = FALSE;
        }

423
      statusbar->progress_active = FALSE;
424
      statusbar->progress_value  = 0.0;
425

426
      gtk_widget_hide (bar);
427
      gtk_widget_show (statusbar->label);
428

429
      gimp_statusbar_pop (statusbar, "progress");
430

431 432
      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0);
      gtk_widget_set_sensitive (statusbar->cancel_button, FALSE);
433
      gtk_widget_hide (statusbar->cancel_button);
434 435

      gimp_statusbar_restore_window_title (statusbar);
436 437 438
    }
}

439 440 441 442 443 444 445 446
static gboolean
gimp_statusbar_progress_is_active (GimpProgress *progress)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  return statusbar->progress_active;
}

447 448 449 450 451 452 453 454
static void
gimp_statusbar_progress_set_text (GimpProgress *progress,
                                  const gchar  *message)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (statusbar->progress_active)
    {
455 456
      GtkWidget *bar = statusbar->progressbar;

457
      gimp_statusbar_replace (statusbar, "progress", NULL, "%s", message);
458

459
      gimp_widget_flush_expose (bar);
460 461

      gimp_statusbar_override_window_title (statusbar);
462 463 464 465 466 467 468 469 470 471 472
    }
}

static void
gimp_statusbar_progress_set_value (GimpProgress *progress,
                                   gdouble       percentage)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (statusbar->progress_active)
    {
473 474 475 476
      GtkWidget     *bar = statusbar->progressbar;
      GtkAllocation  allocation;

      gtk_widget_get_allocation (bar, &allocation);
477

478
      statusbar->progress_value = percentage;
479

480
      /* only update the progress bar if this causes a visible change */
481
      if (fabs (allocation.width *
482 483 484 485 486
                (percentage -
                 gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (bar)))) > 1.0)
        {
          gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), percentage);

487
          gimp_widget_flush_expose (bar);
488
        }
489 490 491 492 493 494 495 496 497
    }
}

static gdouble
gimp_statusbar_progress_get_value (GimpProgress *progress)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (statusbar->progress_active)
498
    return statusbar->progress_value;
499 500 501 502

  return 0.0;
}

503 504 505 506 507 508 509 510 511 512 513
static void
gimp_statusbar_progress_pulse (GimpProgress *progress)
{
  GimpStatusbar *statusbar = GIMP_STATUSBAR (progress);

  if (statusbar->progress_active)
    {
      GtkWidget *bar = statusbar->progressbar;

      gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar));

514
      gimp_widget_flush_expose (bar);
515 516 517
    }
}

518 519 520 521 522 523 524
static gboolean
gimp_statusbar_progress_message (GimpProgress        *progress,
                                 Gimp                *gimp,
                                 GimpMessageSeverity  severity,
                                 const gchar         *domain,
                                 const gchar         *message)
{
525 526
  GimpStatusbar *statusbar  = GIMP_STATUSBAR (progress);
  PangoLayout   *layout;
527
  const gchar   *stock_id;
528
  gboolean       handle_msg = FALSE;
529

530 531
  /*  don't accept a message if we are already displaying a more severe one  */
  if (statusbar->temp_timeout_id && statusbar->temp_severity > severity)
532
    return FALSE;
533

534
  /*  we can only handle short one-liners  */
535
  layout = gtk_widget_create_pango_layout (statusbar->label, message);
536

537 538
  stock_id = gimp_get_message_stock_id (severity);

539 540
  if (pango_layout_get_line_count (layout) == 1)
    {
541 542 543
      GtkAllocation label_allocation;
      gint          width;

544
      gtk_widget_get_allocation (statusbar->label, &label_allocation);
545 546 547

      pango_layout_get_pixel_size (layout, &width, NULL);

548
      if (width < label_allocation.width)
549
        {
550 551 552 553
          if (stock_id)
            {
              GdkPixbuf *pixbuf;

554
              pixbuf = gtk_widget_render_icon (statusbar->label, stock_id,
555 556
                                               GTK_ICON_SIZE_MENU, NULL);

557
              width += ICON_SPACING + gdk_pixbuf_get_width (pixbuf);
558 559

              g_object_unref (pixbuf);
560

561
              handle_msg = (width < label_allocation.width);
562 563 564 565 566
            }
          else
            {
              handle_msg = TRUE;
            }
567 568
        }
    }
569

570 571 572
  g_object_unref (layout);

  if (handle_msg)
573
    gimp_statusbar_push_temp (statusbar, severity, stock_id, "%s", message);
574 575

  return handle_msg;
576 577
}

578 579 580 581 582 583 584 585
static void
gimp_statusbar_progress_canceled (GtkWidget     *button,
                                  GimpStatusbar *statusbar)
{
  if (statusbar->progress_active)
    gimp_progress_cancel (GIMP_PROGRESS (statusbar));
}

586 587
static void
gimp_statusbar_set_text (GimpStatusbar *statusbar,
588
                         const gchar   *stock_id,
589 590 591
                         const gchar   *text)
{
  if (statusbar->progress_active)
592 593 594 595
    {
      gtk_progress_bar_set_text (GTK_PROGRESS_BAR (statusbar->progressbar),
                                 text);
    }
596
  else
597 598 599 600 601
    {
      if (statusbar->icon)
        g_object_unref (statusbar->icon);

      if (stock_id)
602
        statusbar->icon = gtk_widget_render_icon (statusbar->label,
603 604 605 606 607 608 609 610 611 612 613 614 615
                                                  stock_id,
                                                  GTK_ICON_SIZE_MENU, NULL);
      else
        statusbar->icon = NULL;

      if (statusbar->icon)
        {
          PangoAttrList  *attrs;
          PangoAttribute *attr;
          PangoRectangle  rect;
          gchar          *tmp;

          tmp = g_strconcat (" ", text, NULL);
616
          gtk_label_set_text (GTK_LABEL (statusbar->label), tmp);
617 618 619 620 621
          g_free (tmp);

          rect.x      = 0;
          rect.y      = 0;
          rect.width  = PANGO_SCALE * (gdk_pixbuf_get_width (statusbar->icon) +
622
                                       ICON_SPACING);
623
          rect.height = 0;
624 625 626 627 628 629 630 631

          attrs = pango_attr_list_new ();

          attr = pango_attr_shape_new (&rect, &rect);
          attr->start_index = 0;
          attr->end_index   = 1;
          pango_attr_list_insert (attrs, attr);

632
          gtk_label_set_attributes (GTK_LABEL (statusbar->label), attrs);
633 634 635 636
          pango_attr_list_unref (attrs);
        }
      else
        {
637 638
          gtk_label_set_text (GTK_LABEL (statusbar->label), text);
          gtk_label_set_attributes (GTK_LABEL (statusbar->label), NULL);
639 640
        }
    }
641 642
}

643
static void
644
gimp_statusbar_update (GimpStatusbar *statusbar)
645
{
646
  GimpStatusbarMsg *msg = NULL;
647 648 649

  if (statusbar->messages)
    {
650
      msg = statusbar->messages->data;
651

652 653 654 655 656
      /*  only allow progress messages while the progress is active  */
      if (statusbar->progress_active)
        {
          guint context_id = gimp_statusbar_get_context_id (statusbar,
                                                            "progress");
657

658 659 660
          if (context_id != msg->context_id)
            return;
        }
661 662
    }

663
  if (msg && msg->text)
664
    {
665
      gimp_statusbar_set_text (statusbar, msg->stock_id, msg->text);
666 667 668
    }
  else
    {
669
      gimp_statusbar_set_text (statusbar, NULL, "");
670
    }
671 672
}

673 674 675

/*  public functions  */

676
GtkWidget *
677
gimp_statusbar_new (void)
678
{
679
  return g_object_new (GIMP_TYPE_STATUSBAR, NULL);
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

void
gimp_statusbar_set_shell (GimpStatusbar    *statusbar,
                          GimpDisplayShell *shell)
{
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));

  if (shell == statusbar->shell)
    return;

  if (statusbar->shell)
    {
      g_signal_handlers_disconnect_by_func (statusbar->shell,
                                            gimp_statusbar_shell_scaled,
                                            statusbar);
697 698 699 700

      g_signal_handlers_disconnect_by_func (statusbar->shell,
                                            gimp_statusbar_shell_status_notify,
                                            statusbar);
701 702
    }

703
  statusbar->shell = shell;
704

705
  g_signal_connect_object (statusbar->shell, "scaled",
706 707
                           G_CALLBACK (gimp_statusbar_shell_scaled),
                           statusbar, 0);
708 709 710 711

  g_signal_connect_object (statusbar->shell, "notify::status",
                           G_CALLBACK (gimp_statusbar_shell_status_notify),
                           statusbar, 0);
712 713
}

714 715 716 717 718 719 720 721
gboolean
gimp_statusbar_get_visible (GimpStatusbar *statusbar)
{
  g_return_val_if_fail (GIMP_IS_STATUSBAR (statusbar), FALSE);

  if (statusbar->progress_shown)
    return FALSE;

722
  return gtk_widget_get_visible (GTK_WIDGET (statusbar));
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
}

void
gimp_statusbar_set_visible (GimpStatusbar *statusbar,
                            gboolean       visible)
{
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

  if (statusbar->progress_shown)
    {
      if (visible)
        {
          statusbar->progress_shown = FALSE;
          return;
        }
    }

740
  gtk_widget_set_visible (GTK_WIDGET (statusbar), visible);
741 742
}

743 744 745 746 747
void
gimp_statusbar_empty (GimpStatusbar *statusbar)
{
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

748
  gtk_widget_hide (statusbar->cursor_label);
749 750 751 752 753 754 755 756 757
  gtk_widget_hide (statusbar->unit_combo);
  gtk_widget_hide (statusbar->scale_combo);
}

void
gimp_statusbar_fill (GimpStatusbar *statusbar)
{
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

758
  gtk_widget_show (statusbar->cursor_label);
759 760 761 762
  gtk_widget_show (statusbar->unit_combo);
  gtk_widget_show (statusbar->scale_combo);
}

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
void
gimp_statusbar_override_window_title (GimpStatusbar *statusbar)
{
  GtkWidget *toplevel;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (statusbar));

  if (gimp_image_window_is_iconified (GIMP_IMAGE_WINDOW (toplevel)))
    {
      const gchar *message = gimp_statusbar_peek (statusbar, "progress");

      if (message)
        gtk_window_set_title (GTK_WINDOW (toplevel), message);
    }
}

void
gimp_statusbar_restore_window_title (GimpStatusbar *statusbar)
{
  GtkWidget *toplevel;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (statusbar));

  if (gimp_image_window_is_iconified (GIMP_IMAGE_WINDOW (toplevel)))
    {
792
      g_object_notify (G_OBJECT (statusbar->shell), "title");
793 794 795
    }
}

796
void
797
gimp_statusbar_push (GimpStatusbar *statusbar,
798
                     const gchar   *context,
799
                     const gchar   *stock_id,
800 801
                     const gchar   *format,
                     ...)
802 803 804 805 806 807 808 809
{
  va_list args;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
  g_return_if_fail (context != NULL);
  g_return_if_fail (format != NULL);

  va_start (args, format);
810
  gimp_statusbar_push_valist (statusbar, context, stock_id, format, args);
811 812 813 814 815 816
  va_end (args);
}

void
gimp_statusbar_push_valist (GimpStatusbar *statusbar,
                            const gchar   *context,
817
                            const gchar   *stock_id,
818 819
                            const gchar   *format,
                            va_list        args)
820
{
821 822 823
  GimpStatusbarMsg *msg;
  guint             context_id;
  GSList           *list;
824
  gchar            *message;
825 826

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
827
  g_return_if_fail (context != NULL);
828 829
  g_return_if_fail (format != NULL);

830
  message = gimp_statusbar_vprintf (format, args);
831 832 833

  context_id = gimp_statusbar_get_context_id (statusbar, context);

834 835 836 837 838 839 840 841 842 843
  if (statusbar->messages)
    {
      msg = statusbar->messages->data;

      if (msg->context_id == context_id && strcmp (msg->text, message) == 0)
        {
          g_free (message);
          return;
        }
    }
844

845
  for (list = statusbar->messages; list; list = g_slist_next (list))
846
    {
847
      msg = list->data;
848

849
      if (msg->context_id == context_id)
850
        {
851
          statusbar->messages = g_slist_remove (statusbar->messages, msg);
852
          gimp_statusbar_msg_free (msg);
853 854

          break;
855 856
        }
    }
857

858
  msg = g_slice_new (GimpStatusbarMsg);
859

860
  msg->context_id = context_id;
861
  msg->stock_id   = g_strdup (stock_id);
862
  msg->text       = message;
863

864 865 866 867
  if (statusbar->temp_timeout_id)
    statusbar->messages = g_slist_insert (statusbar->messages, msg, 1);
  else
    statusbar->messages = g_slist_prepend (statusbar->messages, msg);
868

869
  gimp_statusbar_update (statusbar);
870 871
}

872
void
873 874 875 876 877 878 879 880 881
gimp_statusbar_push_coords (GimpStatusbar       *statusbar,
                            const gchar         *context,
                            const gchar         *stock_id,
                            GimpCursorPrecision  precision,
                            const gchar         *title,
                            gdouble              x,
                            const gchar         *separator,
                            gdouble              y,
                            const gchar         *help)
882
{
883
  GimpDisplayShell *shell;
884

885
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
886 887
  g_return_if_fail (title != NULL);
  g_return_if_fail (separator != NULL);
888

889
  if (help == NULL)
890
    help = "";
891

892
  shell = statusbar->shell;
Sven Neumann's avatar
Sven Neumann committed
893

894 895 896
  switch (precision)
    {
    case GIMP_CURSOR_PRECISION_PIXEL_CENTER:
897 898
      x = (gint) x;
      y = (gint) y;
899 900 901 902 903 904 905 906 907 908 909
      break;

    case GIMP_CURSOR_PRECISION_PIXEL_BORDER:
      x = RINT (x);
      y = RINT (y);
      break;

    case GIMP_CURSOR_PRECISION_SUBPIXEL:
      break;
    }

910
  if (shell->unit == GIMP_UNIT_PIXEL)
911
    {
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
      if (precision == GIMP_CURSOR_PRECISION_SUBPIXEL)
        {
          gimp_statusbar_push (statusbar, context,
                               stock_id,
                               statusbar->cursor_format_str_f,
                               title,
                               x,
                               separator,
                               y,
                               help);
        }
      else
        {
          gimp_statusbar_push (statusbar, context,
                               stock_id,
                               statusbar->cursor_format_str,
                               title,
                               (gint) RINT (x),
                               separator,
                               (gint) RINT (y),
                               help);
        }
934 935 936
    }
  else /* show real world units */
    {
937 938
      gdouble xres;
      gdouble yres;
939

940 941
      gimp_image_get_resolution (gimp_display_get_image (shell->display),
                                 &xres, &yres);
942

943
      gimp_statusbar_push (statusbar, context,
944
                           stock_id,
945 946
                           statusbar->cursor_format_str,
                           title,
947
                           gimp_pixels_to_units (x, shell->unit, xres),
948
                           separator,
949
                           gimp_pixels_to_units (y, shell->unit, yres),
950
                           help);
951
    }
952 953
}

954 955 956
void
gimp_statusbar_push_length (GimpStatusbar       *statusbar,
                            const gchar         *context,
957
                            const gchar         *stock_id,
958 959
                            const gchar         *title,
                            GimpOrientationType  axis,
960 961
                            gdouble              value,
                            const gchar         *help)
962 963 964 965 966
{
  GimpDisplayShell *shell;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
  g_return_if_fail (title != NULL);
967

968
  if (help == NULL)
969
    help = "";
970 971 972 973 974

  shell = statusbar->shell;

  if (shell->unit == GIMP_UNIT_PIXEL)
    {
975
      gimp_statusbar_push (statusbar, context,
976
                           stock_id,
977 978
                           statusbar->length_format_str,
                           title,
979 980
                           (gint) RINT (value),
                           help);
981 982 983
    }
  else /* show real world units */
    {
984 985 986
      gdouble xres;
      gdouble yres;
      gdouble resolution;
987

988 989
      gimp_image_get_resolution (gimp_display_get_image (shell->display),
                                 &xres, &yres);
990

991 992 993
      switch (axis)
        {
        case GIMP_ORIENTATION_HORIZONTAL:
994
          resolution = xres;
995 996 997
          break;

        case GIMP_ORIENTATION_VERTICAL:
998
          resolution = yres;
999 1000 1001 1002 1003 1004 1005
          break;

        default:
          g_return_if_reached ();
          break;
        }

1006
      gimp_statusbar_push (statusbar, context,
1007
                           stock_id,
1008 1009
                           statusbar->length_format_str,
                           title,
1010
                           gimp_pixels_to_units (value, shell->unit, resolution),
1011
                           help);
1012 1013 1014
    }
}

1015 1016 1017
void
gimp_statusbar_replace (GimpStatusbar *statusbar,
                        const gchar   *context,
1018
                        const gchar   *stock_id,
1019 1020
                        const gchar   *format,
                        ...)
1021 1022 1023 1024 1025 1026 1027 1028
{
  va_list args;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
  g_return_if_fail (context != NULL);
  g_return_if_fail (format != NULL);

  va_start (args, format);
1029
  gimp_statusbar_replace_valist (statusbar, context, stock_id, format, args);
1030 1031 1032 1033 1034 1035
  va_end (args);
}

void
gimp_statusbar_replace_valist (GimpStatusbar *statusbar,
                               const gchar   *context,
1036
                               const gchar   *stock_id,
1037 1038
                               const gchar   *format,
                               va_list        args)
1039 1040 1041
{
  GimpStatusbarMsg *msg;
  GSList           *list;
Sven Neumann's avatar
Sven Neumann committed
1042
  guint             context_id;
1043
  gchar            *message;
1044 1045

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
1046
  g_return_if_fail (context != NULL);
1047
  g_return_if_fail (format != NULL);
1048

1049
  message = gimp_statusbar_vprintf (format, args);
1050 1051 1052 1053 1054 1055 1056 1057 1058

  context_id = gimp_statusbar_get_context_id (statusbar, context);

  for (list = statusbar->messages; list; list = g_slist_next (list))
    {
      msg = list->data;

      if (msg->context_id == context_id)
        {
1059 1060 1061 1062 1063 1064
          if (strcmp (msg->text, message) == 0)
            {
              g_free (message);
              return;
            }

1065 1066 1067
          g_free (msg->stock_id);
          msg->stock_id = g_strdup (stock_id);

1068
          g_free (msg->text);
1069 1070 1071 1072
          msg->text = message;

          if (list == statusbar->messages)
            gimp_statusbar_update (statusbar);
1073 1074 1075 1076 1077

          return;
        }
    }

1078
  msg = g_slice_new (GimpStatusbarMsg);
1079 1080

  msg->context_id = context_id;
1081
  msg->stock_id   = g_strdup (stock_id);
1082
  msg->text       = message;
1083

1084 1085 1086 1087
  if (statusbar->temp_timeout_id)
    statusbar->messages = g_slist_insert (statusbar->messages, msg, 1);
  else
    statusbar->messages = g_slist_prepend (statusbar->messages, msg);
1088 1089

  gimp_statusbar_update (statusbar);
1090 1091
}

1092 1093 1094 1095 1096 1097 1098
const gchar *
gimp_statusbar_peek (GimpStatusbar *statusbar,
                     const gchar   *context)
{
  GSList *list;
  guint   context_id;

Sven Neumann's avatar
Sven Neumann committed
1099 1100
  g_return_val_if_fail (GIMP_IS_STATUSBAR (statusbar), NULL);
  g_return_val_if_fail (context != NULL, NULL);
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116

  context_id = gimp_statusbar_get_context_id (statusbar, context);

  for (list = statusbar->messages; list; list = list->next)
    {
      GimpStatusbarMsg *msg = list->data;

      if (msg->context_id == context_id)
        {
          return msg->text;
        }
    }

  return NULL;
}

1117 1118 1119 1120 1121
void
gimp_statusbar_pop (GimpStatusbar *statusbar,
                    const gchar   *context)
{
  GSList *list;
Sven Neumann's avatar
Sven Neumann committed
1122
  guint   context_id;
1123 1124

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
1125
  g_return_if_fail (context != NULL);
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

  context_id = gimp_statusbar_get_context_id (statusbar, context);

  for (list = statusbar->messages; list; list = list->next)
    {
      GimpStatusbarMsg *msg = list->data;

      if (msg->context_id == context_id)
        {
          statusbar->messages = g_slist_remove (statusbar->messages, msg);
1136
          gimp_statusbar_msg_free (msg);
1137 1138 1139 1140 1141 1142 1143

          break;
        }
    }

  gimp_statusbar_update (statusbar);
}
1144

1145
void
1146 1147 1148 1149
gimp_statusbar_push_temp (GimpStatusbar       *statusbar,
                          GimpMessageSeverity  severity,
                          const gchar         *stock_id,
                          const gchar         *format,
1150
                          ...)
1151 1152 1153 1154
{
  va_list args;

  va_start (args, format);
1155
  gimp_statusbar_push_temp_valist (statusbar, severity, stock_id, format, args);
1156 1157 1158 1159
  va_end (args);
}

void
1160 1161 1162 1163 1164
gimp_statusbar_push_temp_valist (GimpStatusbar       *statusbar,
                                 GimpMessageSeverity  severity,
                                 const gchar         *stock_id,
                                 const gchar         *format,
                                 va_list              args)
1165 1166 1167 1168 1169
{
  GimpStatusbarMsg *msg = NULL;
  gchar            *message;

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));
1170
  g_return_if_fail (severity <= GIMP_MESSAGE_WARNING);
1171 1172
  g_return_if_fail (format != NULL);

1173 1174 1175 1176
  /*  don't accept a message if we are already displaying a more severe one  */
  if (statusbar->temp_timeout_id && statusbar->temp_severity > severity)
    return;

1177
  message = gimp_statusbar_vprintf (format, args);
1178 1179 1180 1181 1182

  if (statusbar->temp_timeout_id)
    g_source_remove (statusbar->temp_timeout_id);

  statusbar->temp_timeout_id =
1183 1184
    g_timeout_add (MESSAGE_TIMEOUT,
                   (GSourceFunc) gimp_statusbar_temp_timeout, statusbar);
1185

1186 1187
  statusbar->temp_severity = severity;

1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
  if (statusbar->messages)
    {
      msg = statusbar->messages->data;

      if (msg->context_id == statusbar->temp_context_id)
        {
          if (strcmp (msg->text, message) == 0)
            {
              g_free (message);
              return;
            }

1200 1201 1202
          g_free (msg->stock_id);
          msg->stock_id = g_strdup (stock_id);

1203 1204 1205 1206 1207 1208 1209 1210
          g_free (msg->text);
          msg->text = message;

          gimp_statusbar_update (statusbar);
          return;
        }
    }

1211
  msg = g_slice_new (GimpStatusbarMsg);
1212 1213

  msg->context_id = statusbar->temp_context_id;
1214
  msg->stock_id   = g_strdup (stock_id);
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
  msg->text       = message;

  statusbar->messages = g_slist_prepend (statusbar->messages, msg);

  gimp_statusbar_update (statusbar);
}

void
gimp_statusbar_pop_temp (GimpStatusbar *statusbar)
{
  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

  if (statusbar->temp_timeout_id)
    {
      g_source_remove (statusbar->temp_timeout_id);
      statusbar->temp_timeout_id = 0;
    }

  if (statusbar->messages)
    {
      GimpStatusbarMsg *msg = statusbar->messages->data;

      if (msg->context_id == statusbar->temp_context_id)
        {
          statusbar->messages = g_slist_remove (statusbar->messages, msg);
1240
          gimp_statusbar_msg_free (msg);
1241 1242 1243 1244 1245 1246

          gimp_statusbar_update (statusbar);
        }
    }
}

1247
void
1248 1249 1250 1251
gimp_statusbar_update_cursor (GimpStatusbar       *statusbar,
                              GimpCursorPrecision  precision,
                              gdouble              x,
                              gdouble              y)
1252
{
1253
  GimpDisplayShell *shell;
1254
  GimpImage        *image;
1255
  gchar             buffer[CURSOR_LEN];
1256 1257 1258

  g_return_if_fail (GIMP_IS_STATUSBAR (statusbar));

1259
  shell = statusbar->shell;
1260
  image = gimp_display_get_image (shell->display);
1261

1262 1263 1264 1265 1266
  if (! image                            ||
      x <  0                             ||
      y <  0                             ||
      x >= gimp_image_get_width  (image) ||
      y >= gimp_image_get_height (image))
1267 1268 1269 1270 1271 1272 1273 1274
    {
      gtk_widget_set_sensitive (statusbar->cursor_label, FALSE);
    }
  else
    {
      gtk_widget_set_sensitive (statusbar->cursor_label, TRUE);
    }

1275 1276 1277
  switch (precision)
    {
    case GIMP_CURSOR_PRECISION_PIXEL_CENTER:
1278 1279
      x = (gint) x;
      y = (gint) y;
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
      break;

    case GIMP_CURSOR_PRECISION_PIXEL_BORDER:
      x = RINT (x);
      y = RINT (y);
      break;

    case GIMP_CURSOR_PRECISION_SUBPIXEL:
      break;
    }
1290

1291
  if (shell->unit == GIMP_UNIT_PIXEL)
1292
    {
1293 1294 1295 1296 1297 1298 1299 1300