gimpwidgets-utils.c 26.2 KB
Newer Older
1 2 3
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4 5
 * gimpwidgets-utils.c
 * Copyright (C) 1999-2003 Michael Natterer <mitch@gimp.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 */
Sven Neumann's avatar
Sven Neumann committed
21

22
#include "config.h"
Sven Neumann's avatar
Sven Neumann committed
23

24 25
#include <errno.h>
#include <fcntl.h>
26
#include <string.h>
27
#include <sys/types.h>
28

29 30 31 32
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

33
#include <glib/gstdio.h>
34

35 36
#include <glib.h>

37
#ifdef G_OS_WIN32
38
#include "libgimpbase/gimpwin32-io.h"
39
#endif
Sven Neumann's avatar
Sven Neumann committed
40

Sven Neumann's avatar
Sven Neumann committed
41 42
#include <gtk/gtk.h>

43 44 45 46 47 48 49 50
#ifdef GDK_WINDOWING_WIN32
#include <gdk/gdkwin32.h>
#endif

#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

51
#include "libgimpbase/gimpbase.h"
52
#include "libgimpcolor/gimpcolor.h"
53 54
#include "libgimpwidgets/gimpwidgets.h"

Michael Natterer's avatar
Michael Natterer committed
55
#include "widgets-types.h"
56

57
#include "gimperrordialog.h"
Michael Natterer's avatar
Michael Natterer committed
58
#include "gimpwidgets-utils.h"
59

60
#include "gimp-intl.h"
61

Sven Neumann's avatar
Sven Neumann committed
62

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/**
 * gimp_menu_position:
 * @menu: a #GtkMenu widget
 * @x: pointer to horizontal position
 * @y: pointer to vertical position
 *
 * Positions a #GtkMenu so that it pops up on screen.  This function
 * takes care of the preferred popup direction (taken from the widget
 * render direction) and it handles multiple monitors representing a
 * single #GdkScreen (Xinerama).
 *
 * You should call this function with @x and @y initialized to the
 * origin of the menu. This is typically the center of the widget the
 * menu is popped up from. gimp_menu_position() will then decide if
 * and how these initial values need to be changed.
 **/
Michael Natterer's avatar
Michael Natterer committed
79
void
80 81 82
gimp_menu_position (GtkMenu *menu,
		    gint    *x,
		    gint    *y)
Michael Natterer's avatar
Michael Natterer committed
83
{
84
  GtkWidget      *widget;
85
  GdkScreen      *screen;
86 87 88
  GtkRequisition  requisition;
  GdkRectangle    rect;
  gint            monitor;
89

90
  g_return_if_fail (GTK_IS_MENU (menu));
91 92
  g_return_if_fail (x != NULL);
  g_return_if_fail (y != NULL);
Michael Natterer's avatar
Michael Natterer committed
93

94
  widget = GTK_WIDGET (menu);
Michael Natterer's avatar
Michael Natterer committed
95

96
  screen = gtk_widget_get_screen (widget);
97

98 99
  monitor = gdk_screen_get_monitor_at_point (screen, *x, *y);
  gdk_screen_get_monitor_geometry (screen, monitor, &rect);
100

101
  gtk_menu_set_screen (menu, screen);
Michael Natterer's avatar
Michael Natterer committed
102

103
  gtk_widget_size_request (widget, &requisition);
104

105 106 107 108 109
  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
    {
      *x -= requisition.width;
      if (*x < rect.x)
        *x += requisition.width;
110 111 112
    }
  else
    {
113 114
      if (*x + requisition.width > rect.x + rect.width)
        *x -= requisition.width;
115 116
    }

117 118
  if (*x < rect.x)
    *x = rect.x;
119

120 121
  if (*y + requisition.height > rect.y + rect.height)
    *y -= requisition.height;
Michael Natterer's avatar
Michael Natterer committed
122

123 124
  if (*y < rect.y)
    *y = rect.y;
125 126
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/**
 * gimp_button_menu_position:
 * @button: a button widget to popup the menu from
 * @menu: the menu to position
 * @position: the preferred popup direction for the menu (left or right)
 * @x: return location for x coordinate
 * @y: return location for y coordinate
 *
 * Utility function to position a menu that pops up from a button.
 **/
void
gimp_button_menu_position (GtkWidget       *button,
                           GtkMenu         *menu,
                           GtkPositionType  position,
                           gint            *x,
                           gint            *y)
{
  GdkScreen      *screen;
  GtkRequisition  menu_requisition;
146 147
  GdkRectangle    rect;
  gint            monitor;
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

  g_return_if_fail (GTK_WIDGET_REALIZED (button));
  g_return_if_fail (GTK_IS_MENU (menu));
  g_return_if_fail (x != NULL);
  g_return_if_fail (y != NULL);

  if (gtk_widget_get_direction (button) == GTK_TEXT_DIR_RTL)
    {
      switch (position)
        {
        case GTK_POS_LEFT:   position = GTK_POS_RIGHT;  break;
        case GTK_POS_RIGHT:  position = GTK_POS_LEFT;   break;
        default:
          break;
        }
    }

  gdk_window_get_origin (button->window, x, y);

  gtk_widget_size_request (GTK_WIDGET (menu), &menu_requisition);

169 170 171 172 173 174 175
  screen = gtk_widget_get_screen (button);

  monitor = gdk_screen_get_monitor_at_point (screen, *x, *y);
  gdk_screen_get_monitor_geometry (screen, monitor, &rect);

  gtk_menu_set_screen (menu, screen);

176 177 178 179 180 181
  *x += button->allocation.x;

  switch (position)
    {
    case GTK_POS_LEFT:
      *x -= menu_requisition.width;
182
      if (*x < rect.x)
183 184 185 186 187
        *x += menu_requisition.width + button->allocation.width;
      break;

    case GTK_POS_RIGHT:
      *x += button->allocation.width;
188
      if (*x + menu_requisition.width > rect.x + rect.width)
189 190 191 192
        *x -= button->allocation.width + menu_requisition.width;
      break;

    default:
193
      g_warning ("%s: unhandled position (%d)", G_STRFUNC, position);
194 195 196 197 198
      break;
    }

  *y += button->allocation.y + button->allocation.height / 2;

199
  if (*y + menu_requisition.height > rect.y + rect.height)
200
    *y -= menu_requisition.height;
201 202
  if (*y < rect.y)
    *y = rect.y;
203 204
}

205 206 207
void
gimp_table_attach_stock (GtkTable    *table,
                         gint         row,
208
			 const gchar *stock_id,
209
                         GtkWidget   *widget,
210
			 gint         colspan,
211
                         gboolean     left_align)
212
{
213
  GtkWidget *image;
214 215

  g_return_if_fail (GTK_IS_TABLE (table));
216 217
  g_return_if_fail (stock_id != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));
218

219 220 221
  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
  gtk_misc_set_alignment (GTK_MISC (image), 1.0, 0.5);
  gtk_table_attach (table, image, 0, 1, row, row + 1,
222
		    GTK_FILL, GTK_FILL, 0, 0);
223
  gtk_widget_show (image);
224

225
  if (left_align)
226
    {
227
      GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
228

229
      gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
230
      gtk_widget_show (widget);
231

232
      widget = hbox;
233
    }
234

235 236 237
  gtk_table_attach (table, widget, 1, 1 + colspan, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gtk_widget_show (widget);
238
}
239

240 241 242 243 244
void
gimp_enum_radio_frame_add (GtkFrame  *frame,
                           GtkWidget *widget,
                           gint       enum_value)
{
245 246 247 248
  GtkWidget *vbox;
  GList     *children;
  GList     *list;
  gint       pos;
249 250 251 252

  g_return_if_fail (GTK_IS_FRAME (frame));
  g_return_if_fail (GTK_IS_WIDGET (widget));

253
  vbox = gtk_bin_get_child (GTK_BIN (frame));
254

255
  g_return_if_fail (GTK_IS_VBOX (vbox));
256

257
  children = gtk_container_get_children (GTK_CONTAINER (vbox));
258

259
  for (list = children, pos = 1;
260
       list;
261
       list = g_list_next (list), pos++)
262
    {
263 264
      if (GTK_IS_RADIO_BUTTON (list->data) &&
          GPOINTER_TO_INT (g_object_get_data (list->data, "gimp-item-data")) ==
265 266
          enum_value)
        {
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
          GtkWidget *radio = list->data;
          GtkWidget *hbox;
          GtkWidget *spacer;
          gint       indicator_size;
          gint       indicator_spacing;
          gint       focus_width;
          gint       focus_padding;

          gtk_widget_style_get (radio,
                                "indicator-size",    &indicator_size,
                                "indicator-spacing", &indicator_spacing,
                                "focus-line-width",  &focus_width,
                                "focus-padding",     &focus_padding,
                                NULL);

          hbox = gtk_hbox_new (FALSE, 0);

          spacer = gtk_vbox_new (FALSE, 0);
          gtk_widget_set_size_request (spacer,
                                       indicator_size +
                                       3 * indicator_spacing +
                                       focus_width +
                                       focus_padding +
                                       GTK_CONTAINER (radio)->border_width,
                                       -1);
          gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0);
          gtk_widget_show (spacer);

          gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
          gtk_widget_show (widget);

          g_object_set_data (G_OBJECT (radio), "set_sensitive", hbox);
          g_signal_connect (radio, "toggled",
300 301 302 303 304 305
                            G_CALLBACK (gimp_toggle_button_sensitive_update),
                            NULL);

          gtk_widget_set_sensitive (hbox,
                                    GTK_TOGGLE_BUTTON (list->data)->active);

306 307 308 309
          gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
          gtk_box_reorder_child (GTK_BOX (vbox), hbox, pos);
          gtk_widget_show (hbox);

310 311 312 313
          break;
        }
    }

314
  g_list_free (children);
315 316
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
GtkIconSize
gimp_get_icon_size (GtkWidget   *widget,
                    const gchar *stock_id,
                    GtkIconSize  max_size,
                    gint         width,
                    gint         height)
{
  GtkIconSet   *icon_set;
  GtkIconSize  *sizes;
  gint          n_sizes;
  gint          i;
  gint          width_diff  = 1024;
  gint          height_diff = 1024;
  gint          max_width;
  gint          max_height;
  GtkIconSize   icon_size = GTK_ICON_SIZE_MENU;
333
  GtkSettings  *settings;
334 335 336 337 338 339

  g_return_val_if_fail (GTK_IS_WIDGET (widget), icon_size);
  g_return_val_if_fail (stock_id != NULL, icon_size);
  g_return_val_if_fail (width > 0, icon_size);
  g_return_val_if_fail (height > 0, icon_size);

340 341 342 343 344
  icon_set = gtk_style_lookup_icon_set (widget->style, stock_id);

  if (! icon_set)
    return GTK_ICON_SIZE_INVALID;

345
  settings = gtk_widget_get_settings (widget);
346 347 348

  if (! gtk_icon_size_lookup_for_settings (settings, max_size,
                                           &max_width, &max_height))
349 350 351 352 353 354 355 356 357 358 359 360
    {
      max_width  = 1024;
      max_height = 1024;
    }

  gtk_icon_set_get_sizes (icon_set, &sizes, &n_sizes);

  for (i = 0; i < n_sizes; i++)
    {
      gint icon_width;
      gint icon_height;

361 362
      if (gtk_icon_size_lookup_for_settings (settings, sizes[i],
                                             &icon_width, &icon_height))
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
        {
          if (icon_width  <= width      &&
              icon_height <= height     &&
              icon_width  <= max_width  &&
              icon_height <= max_height &&
              ((width  - icon_width)  < width_diff ||
               (height - icon_height) < height_diff))
            {
              width_diff  = width  - icon_width;
              height_diff = height - icon_height;

              icon_size = sizes[i];
            }
        }
    }

  g_free (sizes);

  return icon_size;
}

384 385 386 387 388 389 390 391 392 393
const gchar *
gimp_get_mod_name_shift (void)
{
  static gchar *mod_name_shift = NULL;

  if (! mod_name_shift)
    {
      GtkAccelLabelClass *accel_label_class;

      accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
394
      mod_name_shift = g_strdup (accel_label_class->mod_name_shift);
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
      g_type_class_unref (accel_label_class);
    }

  return (const gchar *) mod_name_shift;
}

const gchar *
gimp_get_mod_name_control (void)
{
  static gchar *mod_name_control = NULL;

  if (! mod_name_control)
    {
      GtkAccelLabelClass *accel_label_class;

      accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
411
      mod_name_control = g_strdup (accel_label_class->mod_name_control);
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
      g_type_class_unref (accel_label_class);
    }

  return (const gchar *) mod_name_control;
}

const gchar *
gimp_get_mod_name_alt (void)
{
  static gchar *mod_name_alt = NULL;

  if (! mod_name_alt)
    {
      GtkAccelLabelClass *accel_label_class;

      accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
428
      mod_name_alt = g_strdup (accel_label_class->mod_name_alt);
429 430 431 432 433
      g_type_class_unref (accel_label_class);
    }

  return (const gchar *) mod_name_alt;
}
434

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
const gchar *
gimp_get_mod_separator (void)
{
  static gchar *mod_separator = NULL;

  if (! mod_separator)
    {
      GtkAccelLabelClass *accel_label_class;

      accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
      mod_separator = g_strdup (accel_label_class->mod_separator);
      g_type_class_unref (accel_label_class);
    }

  return (const gchar *) mod_separator;
}
451

452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
const gchar *
gimp_get_mod_string (GdkModifierType modifiers)
{
  static struct
  {
    GdkModifierType  modifiers;
    gchar           *name;
  }
  modifier_strings[] =
  {
    { GDK_SHIFT_MASK,                                    NULL },
    { GDK_CONTROL_MASK,                                  NULL },
    { GDK_MOD1_MASK,                                     NULL },
    { GDK_SHIFT_MASK | GDK_CONTROL_MASK,                 NULL },
    { GDK_SHIFT_MASK | GDK_MOD1_MASK,                    NULL },
    { GDK_CONTROL_MASK | GDK_MOD1_MASK,                  NULL },
    { GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK, NULL }
  };

  gint i;

  for (i = 0; i < G_N_ELEMENTS (modifier_strings); i++)
    {
      if (modifiers == modifier_strings[i].modifiers)
        {
          if (! modifier_strings[i].name)
            {
              GString *str = g_string_new ("");

              if (modifiers & GDK_SHIFT_MASK)
                {
                  g_string_append (str, gimp_get_mod_name_shift ());
                }

              if (modifiers & GDK_CONTROL_MASK)
                {
                  if (str->len)
                    g_string_append (str, gimp_get_mod_separator ());

                  g_string_append (str, gimp_get_mod_name_control ());
                }

              if (modifiers & GDK_MOD1_MASK)
                {
                  if (str->len)
                    g_string_append (str, gimp_get_mod_separator ());

                  g_string_append (str, gimp_get_mod_name_alt ());
                }

              modifier_strings[i].name = g_string_free (str, FALSE);
            }

          return modifier_strings[i].name;
        }
    }

509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
  return NULL;
}

static void
gimp_substitute_underscores (gchar *str)
{
  gchar *p;

  for (p = str; *p; p++)
    if (*p == '_')
      *p = ' ';
}

gchar *
gimp_get_accel_string (guint           key,
                       GdkModifierType modifiers)
{
  GtkAccelLabelClass *accel_label_class;
  GString            *gstring;
  gunichar            ch;

  accel_label_class = g_type_class_peek (GTK_TYPE_ACCEL_LABEL);

  gstring = g_string_new (gimp_get_mod_string (modifiers));

  if (gstring->len > 0)
    g_string_append (gstring, gimp_get_mod_separator ());

  ch = gdk_keyval_to_unicode (key);

  if (ch && (g_unichar_isgraph (ch) || ch == ' ') &&
      (ch < 0x80 || accel_label_class->latin1_to_char))
    {
      switch (ch)
        {
        case ' ':
          g_string_append (gstring, "Space");
          break;
        case '\\':
          g_string_append (gstring, "Backslash");
          break;
        default:
          g_string_append_unichar (gstring, g_unichar_toupper (ch));
          break;
        }
    }
  else
    {
      gchar *tmp;

      tmp = gtk_accelerator_name (key, 0);

      if (tmp[0] != 0 && tmp[1] == 0)
        tmp[0] = g_ascii_toupper (tmp[0]);

      gimp_substitute_underscores (tmp);
      g_string_append (gstring, tmp);
      g_free (tmp);
    }

  return g_string_free (gstring, FALSE);
570 571 572
}


573 574 575 576 577
/**
 * gimp_get_screen_resolution:
 * @screen: a #GdkScreen or %NULL
 * @xres: returns the horizontal screen resolution (in dpi)
 * @yres: returns the vertical screen resolution (in dpi)
578
 *
579 580 581
 * Retrieves the screen resolution from GDK. If @screen is %NULL, the
 * default screen is used.
 **/
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
void
gimp_get_screen_resolution (GdkScreen *screen,
                            gdouble   *xres,
                            gdouble   *yres)
{
  gint    width, height;
  gint    width_mm, height_mm;
  gdouble x = 0.0;
  gdouble y = 0.0;

  g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
  g_return_if_fail (xres != NULL);
  g_return_if_fail (yres != NULL);

  if (!screen)
    screen = gdk_screen_get_default ();

  width  = gdk_screen_get_width (screen);
  height = gdk_screen_get_height (screen);

  width_mm  = gdk_screen_get_width_mm (screen);
  height_mm = gdk_screen_get_height_mm (screen);

  /*
   * From xdpyinfo.c:
   *
   * there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
   *
   *     dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
   *         = N pixels / (M inch / 25.4)
   *         = N * 25.4 pixels / M inch
   */

  if (width_mm > 0 && height_mm > 0)
    {
      x = (width  * 25.4) / (gdouble) width_mm;
      y = (height * 25.4) / (gdouble) height_mm;
    }

  if (x < GIMP_MIN_RESOLUTION || x > GIMP_MAX_RESOLUTION ||
      y < GIMP_MIN_RESOLUTION || y > GIMP_MAX_RESOLUTION)
    {
      g_warning ("GDK returned bogus values for the screen resolution, "
                 "using 75 dpi instead.");

      x = 75.0;
      y = 75.0;
    }

  /*  round the value to full integers to give more pleasant results  */
  *xres = ROUND (x);
  *yres = ROUND (y);
}
635 636 637 638


/**
 * gimp_rgb_get_gdk_color:
639
 * @rgb: the source color as #GimpRGB
640
 * @gdk_color: pointer to a #GdkColor
641
 *
642 643 644 645
 * Initializes @gdk_color from a #GimpRGB. This function does not
 * allocate the color for you. Depending on how you want to use it,
 * you may have to call gdk_colormap_alloc_color().
 **/
646
void
647
gimp_rgb_get_gdk_color (const GimpRGB *rgb,
648 649 650 651
                        GdkColor      *gdk_color)
{
  guchar r, g, b;

652
  g_return_if_fail (rgb != NULL);
653
  g_return_if_fail (gdk_color != NULL);
654

655
  gimp_rgb_get_uchar (rgb, &r, &g, &b);
656

657 658 659 660
  gdk_color->red   = (r << 8) | r;
  gdk_color->green = (g << 8) | g;
  gdk_color->blue  = (b << 8) | b;
}
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684

/**
 * gimp_rgb_set_gdk_color:
 * @rgb: a #GimpRGB that is to be set
 * @gdk_color: pointer to the source #GdkColor
 *
 * Initializes @rgb from a #GdkColor. This function does not touch
 * the alpha value of @rgb.
 **/
void
gimp_rgb_set_gdk_color (GimpRGB        *rgb,
                        const GdkColor *gdk_color)
{
  guchar r, g, b;

  g_return_if_fail (rgb != NULL);
  g_return_if_fail (gdk_color != NULL);

  r = gdk_color->red   >> 8;
  g = gdk_color->green >> 8;
  b = gdk_color->blue  >> 8;

  gimp_rgb_set_uchar (rgb, r, g, b);
}
Sven Neumann's avatar
Sven Neumann committed
685

686 687 688
void
gimp_window_set_hint (GtkWindow      *window,
                      GimpWindowHint  hint)
Sven Neumann's avatar
Sven Neumann committed
689
{
690 691
  g_return_if_fail (GTK_IS_WINDOW (window));

Sven Neumann's avatar
Sven Neumann committed
692 693
  switch (hint)
    {
694 695 696
    case GIMP_WINDOW_HINT_NORMAL:
      gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_NORMAL);
      break;
Sven Neumann's avatar
Sven Neumann committed
697

698 699 700
    case GIMP_WINDOW_HINT_UTILITY:
      gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_UTILITY);
      break;
701 702 703 704

    case GIMP_WINDOW_HINT_KEEP_ABOVE:
      gtk_window_set_keep_above (window, TRUE);
      break;
Sven Neumann's avatar
Sven Neumann committed
705 706
    }
}
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
/**
 * gimp_window_get_native:
 * @window: a #GtkWindow
 *
 * This function is used to pass a window handle to plug-ins so that
 * they can set their dialog windows transient to the parent window.
 *
 * Return value: a native window handle of the window's #GdkWindow or 0
 *               if the window isn't realized yet
 */
GdkNativeWindow
gimp_window_get_native (GtkWindow *window)
{
  g_return_val_if_fail (GTK_IS_WINDOW (window), 0);

#ifdef GDK_NATIVE_WINDOW_POINTER
#ifdef __GNUC__
#warning gimp_window_get_native() unimplementable for the target windowing system
#endif
#endif

#ifdef GDK_WINDOWING_WIN32
  if (window && GTK_WIDGET_REALIZED (window))
Hans Breuer's avatar
updated  
Hans Breuer committed
731
    return (GdkNativeWindow)GDK_WINDOW_HWND (GTK_WIDGET (window)->window);
732 733 734 735 736 737 738
#endif

#ifdef GDK_WINDOWING_X11
  if (window && GTK_WIDGET_REALIZED (window))
    return GDK_WINDOW_XID (GTK_WIDGET (window)->window);
#endif

Hans Breuer's avatar
updated  
Hans Breuer committed
739
  return (GdkNativeWindow)0;
740 741
}

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
void
gimp_dialog_set_sensitive (GtkDialog *dialog,
                           gboolean   sensitive)
{
  GList *children;
  GList *list;

  g_return_if_fail (GTK_IS_DIALOG (dialog));

  children = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));

  for (list = children; list; list = g_list_next (list))
    {
      /*  skip the last item (the action area) */
      if (! g_list_next (list))
        break;

      gtk_widget_set_sensitive (list->data, sensitive);
    }

  g_list_free (children);

  if (sensitive)
    gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, sensitive);

  gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, sensitive);
}
769

770 771 772 773 774 775 776 777 778 779 780 781 782 783
gboolean
gimp_text_buffer_load (GtkTextBuffer  *buffer,
                       const gchar    *filename,
                       GError        **error)
{
  FILE        *file;
  gchar        buf[2048];
  gint         remaining = 0;
  GtkTextIter  iter;

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (filename != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

784
  file = g_fopen (filename, "r");
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807

  if (! file)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   g_strerror (errno));
      return FALSE;
    }

  gtk_text_buffer_set_text (buffer, "", 0);
  gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);

  while (! feof (file))
    {
      const char *leftover;
      gint        count;
      gint        to_read = sizeof (buf) - remaining - 1;

      count = fread (buf + remaining, 1, to_read, file);
      buf[count + remaining] = '\0';

      g_utf8_validate (buf, count + remaining, &leftover);

      gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
808
      gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823

      remaining = (buf + remaining + count) - leftover;
      g_memmove (buf, leftover, remaining);

      if (remaining > 6 || count < to_read)
        break;
    }

  if (remaining)
    g_message (_("Invalid UTF-8 data in file '%s'."),
	       gimp_filename_to_utf8 (filename));

  return TRUE;
}

824 825 826 827 828 829
gboolean
gimp_text_buffer_save (GtkTextBuffer  *buffer,
                       const gchar    *filename,
                       gboolean        selection_only,
                       GError        **error)
{
830
  GtkTextIter  start_iter;
831 832 833 834 835 836 837 838
  GtkTextIter  end_iter;
  gint         fd;
  gchar	      *text_contents;

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (filename != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

839
  fd = g_open (filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882

  if (fd == -1)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   g_strerror (errno));
      return FALSE;
    }

  if (selection_only)
    gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter);
  else
    gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);

  text_contents = gtk_text_buffer_get_text (buffer,
					    &start_iter, &end_iter, TRUE);

  if (text_contents)
    {
      gint text_length = strlen (text_contents);

      if (text_length > 0)
        {
          gint bytes_written;

          bytes_written = write (fd, text_contents, text_length);

          if (bytes_written != text_length)
            {
              g_free (text_contents);
              close (fd);
              g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                           g_strerror (errno));
              return FALSE;
            }
        }

      g_free (text_contents);
    }

  close (fd);

  return TRUE;
}
883 884 885 886 887 888 889 890 891 892 893 894 895 896

void
gimp_toggle_button_set_visible (GtkToggleButton *toggle,
                                GtkWidget       *widget)
{
  g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle));
  g_return_if_fail (GTK_IS_WIDGET (widget));

  if (gtk_toggle_button_get_active (toggle))
    gtk_widget_show (widget);
  else
    gtk_widget_hide (widget);
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
static gboolean
gimp_widget_accel_find_func (GtkAccelKey *key,
                             GClosure    *closure,
                             gpointer     data)
{
  return (GClosure *) data == closure;
}

static void
gimp_widget_accel_changed (GtkAccelGroup   *accel_group,
                           guint            unused1,
                           GdkModifierType  unused2,
                           GClosure        *accel_closure,
                           GtkWidget       *widget)
{
  GClosure *widget_closure;

  widget_closure = g_object_get_data (G_OBJECT (widget), "gimp-accel-closure");

  if (accel_closure == widget_closure)
    {
      GtkAction   *action;
      GtkAccelKey *accel_key;
      gchar       *orig_tooltip;
      gchar       *tooltip;
      const gchar *help_id;

      action = g_object_get_data (G_OBJECT (widget), "gimp-accel-action");

      g_object_get (action, "tooltip", &orig_tooltip, NULL);
      help_id = g_object_get_qdata (G_OBJECT (action), GIMP_HELP_ID);

      accel_key = gtk_accel_group_find (accel_group,
                                        gimp_widget_accel_find_func,
                                        accel_closure);

      if (accel_key            &&
          accel_key->accel_key &&
          accel_key->accel_flags & GTK_ACCEL_VISIBLE)
        {
937 938 939 940 941 942
          gchar *tmp = gimp_get_accel_string (accel_key->accel_key,
                                              accel_key->accel_mods);

          tooltip = g_strconcat (orig_tooltip, "     ", tmp, NULL);

          g_free (tmp);
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        }
      else
        {
          tooltip = g_strdup (orig_tooltip);
        }

      gimp_help_set_help_data (widget, tooltip, help_id);

      g_free (tooltip);
      g_free (orig_tooltip);
    }
}

void
gimp_widget_set_accel_help (GtkWidget *widget,
                            GtkAction *action)
{
  GClosure *accel_closure = NULL;

962
  accel_closure = gtk_action_get_accel_closure (action);
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989

  if (accel_closure)
    {
      GtkAccelGroup *accel_group;

      g_object_set_data (G_OBJECT (widget), "gimp-accel-closure",
                         accel_closure);
      g_object_set_data (G_OBJECT (widget), "gimp-accel-action",
                         action);

      accel_group = gtk_accel_group_from_accel_closure (accel_closure);

      g_signal_connect_object (accel_group, "accel-changed",
                               G_CALLBACK (gimp_widget_accel_changed),
                               widget, 0);

      gimp_widget_accel_changed (accel_group,
                                 0, 0,
                                 accel_closure,
                                 widget);
    }
  else
    {
      gchar *tooltip;
      gchar *help_id;

      g_object_get (action, "tooltip", &tooltip, NULL);
Michael Natterer's avatar
Michael Natterer committed
990
      help_id = g_object_get_qdata (G_OBJECT (action), GIMP_HELP_ID);
991 992 993 994 995 996

      gimp_help_set_help_data (widget, tooltip, help_id);

      g_free (tooltip);
    }
}