gimpimagecombobox.c 10.9 KB
Newer Older
1 2 3 4 5 6
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimpimagecombobox.c
 * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
 *
7
 * This library is free software: you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 3 of the License, or (at your option) any later version.
11 12 13 14 15 16 17
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
19
 * <https://www.gnu.org/licenses/>.
20 21 22 23
 */

#include "config.h"

24 25
#include <stdlib.h>

26
#include <gegl.h>
27 28 29 30 31 32
#include <gtk/gtk.h>

#include "libgimpwidgets/gimpwidgets.h"

#include "gimp.h"

33
#include "gimpuitypes.h"
34 35 36 37
#include "gimpimagecombobox.h"
#include "gimppixbuf.h"


38 39 40 41 42 43 44 45 46
/**
 * SECTION: gimpimagecombobox
 * @title: GimpImageComboBox
 * @short_description: A widget providing a popup menu of images.
 *
 * A widget providing a popup menu of images.
 **/


47 48
#define THUMBNAIL_SIZE   24
#define WIDTH_REQUEST   200
49 50


51 52 53 54
typedef struct _GimpImageComboBoxClass GimpImageComboBoxClass;

struct _GimpImageComboBox
{
55 56 57 58
  GimpIntComboBox          parent_instance;

  GimpImageConstraintFunc  constraint;
  gpointer                 data;
59
  GDestroyNotify           data_destroy;
60 61

  GimpImageConstraintDeprecatedFunc  constraint_d;
62 63 64 65 66 67 68 69
};

struct _GimpImageComboBoxClass
{
  GimpIntComboBoxClass  parent_class;
};


70 71
static void  gimp_image_combo_box_finalize  (GObject                 *object);

72
static void  gimp_image_combo_box_populate  (GimpImageComboBox       *combo_box);
73
static void  gimp_image_combo_box_model_add (GtkListStore            *store,
74
                                             GList                   *images,
75
                                             GimpImageConstraintFunc  constraint,
76 77
                                             GimpImageConstraintDeprecatedFunc
                                                                      constraint_d,
78
                                             gpointer                 data);
79

80 81 82 83 84 85 86 87
static void  gimp_image_combo_box_drag_data_received (GtkWidget        *widget,
                                                      GdkDragContext   *context,
                                                      gint              x,
                                                      gint              y,
                                                      GtkSelectionData *selection,
                                                      guint             info,
                                                      guint             time);

88 89
static void  gimp_image_combo_box_changed   (GimpImageComboBox *combo_box);

90 91 92

static const GtkTargetEntry target = { "application/x-gimp-image-id", 0 };

93

94 95 96 97
G_DEFINE_TYPE (GimpImageComboBox, gimp_image_combo_box,
               GIMP_TYPE_INT_COMBO_BOX)

#define parent_class gimp_image_combo_box_parent_class
98

99 100 101 102

static void
gimp_image_combo_box_class_init (GimpImageComboBoxClass *klass)
{
103
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
Michael Natterer's avatar
Michael Natterer committed
104
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
105

106 107
  object_class->finalize           = gimp_image_combo_box_finalize;

108 109 110 111 112 113 114 115 116 117 118 119 120 121
  widget_class->drag_data_received = gimp_image_combo_box_drag_data_received;
}

static void
gimp_image_combo_box_init (GimpImageComboBox *combo_box)
{
  gtk_drag_dest_set (GTK_WIDGET (combo_box),
                     GTK_DEST_DEFAULT_HIGHLIGHT |
                     GTK_DEST_DEFAULT_MOTION |
                     GTK_DEST_DEFAULT_DROP,
                     &target, 1,
                     GDK_ACTION_COPY);
}

122 123 124 125 126 127 128 129 130 131 132
static void
gimp_image_combo_box_finalize (GObject *object)
{
  GimpImageComboBox *combo = GIMP_IMAGE_COMBO_BOX (object);

  if (combo->data_destroy)
    combo->data_destroy (combo->data);

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

133 134
/**
 * gimp_image_combo_box_new:
135 136 137
 * @constraint:   a #GimpImageConstraintFunc or %NULL
 * @data:         a pointer that is passed to @constraint
 * @data_destroy: Destroy function for @data.
138 139 140 141 142 143
 *
 * Creates a new #GimpIntComboBox filled with all currently opened
 * images. If a @constraint function is specified, it is called for
 * each image and only if the function returns %TRUE, the image is
 * added to the combobox.
 *
144 145 146 147
 * You should use gimp_int_combo_box_connect() to initialize and
 * connect the combo. Use gimp_int_combo_box_set_active() to get the
 * active image ID and gimp_int_combo_box_get_active() to retrieve the
 * ID of the selected image.
148
 *
149
 * Returns: a new #GimpIntComboBox.
150
 *
151
 * Since: 2.2
152
 **/
153 154
GtkWidget *
gimp_image_combo_box_new (GimpImageConstraintFunc constraint,
155 156
                          gpointer                data,
                          GDestroyNotify          data_destroy)
157
{
158
  GimpImageComboBox *combo_box;
159

160
  combo_box = g_object_new (GIMP_TYPE_IMAGE_COMBO_BOX,
161 162
                            "width-request", WIDTH_REQUEST,
                            "ellipsize",     PANGO_ELLIPSIZE_MIDDLE,
163
                            NULL);
164

165 166 167
  combo_box->constraint   = constraint;
  combo_box->data         = data;
  combo_box->data_destroy = data_destroy;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

  gimp_image_combo_box_populate (combo_box);

  g_signal_connect (combo_box, "changed",
                    G_CALLBACK (gimp_image_combo_box_changed),
                    NULL);

  return GTK_WIDGET (combo_box);
}

static void
gimp_image_combo_box_populate (GimpImageComboBox *combo_box)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;
183
  GList        *images;
184

185 186
  model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));

187
  images = gimp_list_images ();
188

189
  gimp_image_combo_box_model_add (GTK_LIST_STORE (model), images,
190
                                  combo_box->constraint,
191
                                  combo_box->constraint_d,
192 193
                                  combo_box->data);

194
  g_list_free (images);
195 196 197 198 199 200 201

  if (gtk_tree_model_get_iter_first (model, &iter))
    gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
}

static void
gimp_image_combo_box_model_add (GtkListStore            *store,
202
                                GList                   *images,
203
                                GimpImageConstraintFunc  constraint,
204 205
                                GimpImageConstraintDeprecatedFunc
                                                         constraint_d,
206 207 208
                                gpointer                 data)
{
  GtkTreeIter  iter;
209
  GList       *list;
210

211
  for (list = images; list; list = g_list_next (list))
212
    {
213 214
      GimpImage *image    = list->data;
      gint32     image_id = gimp_image_get_id (image);
215

216 217
      if ((! constraint && ! constraint_d)             ||
          (constraint && (* constraint) (image, data)) ||
218
          (constraint_d && (* constraint_d) (image_id, data)))
219
        {
220
          gchar     *image_name = gimp_image_get_name (image);
221 222 223
          gchar     *label;
          GdkPixbuf *thumb;

224
          label = g_strdup_printf ("%s-%d", image_name, image_id);
225 226 227

          g_free (image_name);

228
          thumb = gimp_image_get_thumbnail (image,
229
                                            THUMBNAIL_SIZE, THUMBNAIL_SIZE,
230 231 232 233
                                            GIMP_PIXBUF_SMALL_CHECKS);

          gtk_list_store_append (store, &iter);
          gtk_list_store_set (store, &iter,
234
                              GIMP_INT_STORE_VALUE,  image_id,
235 236 237 238 239 240 241 242 243 244 245
                              GIMP_INT_STORE_LABEL,  label,
                              GIMP_INT_STORE_PIXBUF, thumb,
                              -1);

          if (thumb)
            g_object_unref (thumb);

          g_free (label);
        }
    }
}
246 247 248 249 250 251 252 253 254 255

static void
gimp_image_combo_box_drag_data_received (GtkWidget        *widget,
                                         GdkDragContext   *context,
                                         gint              x,
                                         gint              y,
                                         GtkSelectionData *selection,
                                         guint             info,
                                         guint             time)
{
256
  gint   length = gtk_selection_data_get_length (selection);
Michael Natterer's avatar
Michael Natterer committed
257
  gchar *str;
258

259
  if (gtk_selection_data_get_format (selection) != 8 || length < 1)
260
    {
261
      g_warning ("%s: received invalid image ID data", G_STRFUNC);
262 263 264
      return;
    }

265 266
  str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
                   length);
Michael Natterer's avatar
Michael Natterer committed
267 268 269 270 271 272 273 274 275 276 277 278

  if (g_utf8_validate (str, -1, NULL))
    {
      gint pid;
      gint ID;

      if (sscanf (str, "%i:%i", &pid, &ID) == 2 &&
          pid == gimp_getpid ())
        {
          gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget), ID);
        }
    }
279

Michael Natterer's avatar
Michael Natterer committed
280
  g_free (str);
281
}
282 283 284 285 286 287 288 289 290

static void
gimp_image_combo_box_changed (GimpImageComboBox *combo_box)
{
  gint image_ID;

  if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo_box),
                                     &image_ID))
    {
291
      if (! gimp_image_get_by_id (image_ID))
292 293 294 295 296 297 298 299 300 301 302 303
        {
          GtkTreeModel *model;

          model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));

          g_signal_stop_emission_by_name (combo_box, "changed");

          gtk_list_store_clear (GTK_LIST_STORE (model));
          gimp_image_combo_box_populate (combo_box);
        }
    }
}
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352


/* Deprecated API. */


/**
 * gimp_image_combo_box_new_deprecated: (skip)
 * @constraint:   a #GimpImageConstraintDeprecatedFunc or %NULL
 * @data:         a pointer that is passed to @constraint
 * @data_destroy: Destroy function for @data.
 *
 * Creates a new #GimpIntComboBox filled with all currently opened
 * images. If a @constraint function is specified, it is called for
 * each image and only if the function returns %TRUE, the image is
 * added to the combobox.
 *
 * You should use gimp_int_combo_box_connect() to initialize and
 * connect the combo. Use gimp_int_combo_box_set_active() to get the
 * active image ID and gimp_int_combo_box_get_active() to retrieve the
 * ID of the selected image.
 *
 * Returns: a new #GimpIntComboBox.
 *
 * Since: 2.2
 **/
GtkWidget *
gimp_image_combo_box_new_deprecated (GimpImageConstraintDeprecatedFunc constraint,
                                     gpointer                          data,
                                     GDestroyNotify                    data_destroy)
{
  GimpImageComboBox *combo_box;

  combo_box = g_object_new (GIMP_TYPE_IMAGE_COMBO_BOX,
                            "width-request", WIDTH_REQUEST,
                            "ellipsize",     PANGO_ELLIPSIZE_MIDDLE,
                            NULL);

  combo_box->constraint_d = constraint;
  combo_box->data         = data;
  combo_box->data_destroy = data_destroy;

  gimp_image_combo_box_populate (combo_box);

  g_signal_connect (combo_box, "changed",
                    G_CALLBACK (gimp_image_combo_box_changed),
                    NULL);

  return GTK_WIDGET (combo_box);
}