Gimp/libgimpwidgets/gimpmemsizeentry.c
Jehan 792b27afe1 libgimpwidgets: do not free the GtkAdjustment when finalizing...
... GimpMemsizeEntry.
A GtkAdjustment is a GInitiallyUnowned, which means it is created as a
floating reference. GtkSpinButton assumes its ownership by calling
g_object_ref_sink() in gtk_spin_button_new() implementation. Thus it
will take care of freeing it and when we try to unref it in finalize(),
the object already doesn't exist.

Alternatively we could keep another ref (by calling g_object_ref_sink()
ourselves) but since the spin button is a child of the entry, it will
live all the way until the entry is freed. There is no need to increment
the references. So instead, simplify the code, and don't try to free an
object we don't own anymore.

This fixes CRITICAL assertions:
> g_object_unref: assertion 'G_IS_OBJECT (object)' failed
2018-06-24 23:28:50 +02:00

316 lines
8.7 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpmemsizeentry.c
* Copyright (C) 2000-2003 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpmemsizeentry.h"
#include "gimpwidgets.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpmemsizeentry
* @title: GimpMemSizeEntry
* @short_description: A composite widget to enter a memory size.
*
* Similar to a #GimpSizeEntry but instead of lengths, this widget is
* used to let the user enter memory sizes. A combo box allows one to
* switch between Kilobytes, Megabytes and Gigabytes. Used in the GIMP
* preferences dialog.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
struct _GimpMemsizeEntryPrivate
{
guint64 value;
guint64 lower;
guint64 upper;
guint shift;
/* adjustement is owned by spinbutton. Do not unref() it. */
GtkAdjustment *adjustment;
GtkWidget *spinbutton;
GtkWidget *menu;
};
#define GET_PRIVATE(obj) (((GimpMemsizeEntry *) (obj))->priv)
static void gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
GimpMemsizeEntry *entry);
static void gimp_memsize_entry_unit_callback (GtkWidget *widget,
GimpMemsizeEntry *entry);
G_DEFINE_TYPE (GimpMemsizeEntry, gimp_memsize_entry, GTK_TYPE_BOX)
#define parent_class gimp_memsize_entry_parent_class
static guint gimp_memsize_entry_signals[LAST_SIGNAL] = { 0 };
static void
gimp_memsize_entry_class_init (GimpMemsizeEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
klass->value_changed = NULL;
gimp_memsize_entry_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpMemsizeEntryClass, value_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (object_class, sizeof (GimpMemsizeEntryPrivate));
}
static void
gimp_memsize_entry_init (GimpMemsizeEntry *entry)
{
entry->priv = G_TYPE_INSTANCE_GET_PRIVATE (entry,
GIMP_TYPE_MEMSIZE_ENTRY,
GimpMemsizeEntryPrivate);
gtk_orientable_set_orientation (GTK_ORIENTABLE (entry),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (entry), 4);
}
static void
gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
GimpMemsizeEntry *entry)
{
GimpMemsizeEntryPrivate *private = GET_PRIVATE (entry);
guint64 size = gtk_adjustment_get_value (adj);
private->value = size << private->shift;
g_signal_emit (entry, gimp_memsize_entry_signals[VALUE_CHANGED], 0);
}
static void
gimp_memsize_entry_unit_callback (GtkWidget *widget,
GimpMemsizeEntry *entry)
{
GimpMemsizeEntryPrivate *private = GET_PRIVATE (entry);
guint shift;
gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) &shift);
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
if (shift != private->shift)
{
private->shift = shift;
gtk_adjustment_configure (private->adjustment,
CAST private->value >> shift,
CAST private->lower >> shift,
CAST private->upper >> shift,
gtk_adjustment_get_step_increment (private->adjustment),
gtk_adjustment_get_page_increment (private->adjustment),
gtk_adjustment_get_page_size (private->adjustment));
}
#undef CAST
}
/**
* gimp_memsize_entry_new:
* @value: the initial value (in Bytes)
* @lower: the lower limit for the value (in Bytes)
* @upper: the upper limit for the value (in Bytes)
*
* Creates a new #GimpMemsizeEntry which is a #GtkHBox with a #GtkSpinButton
* and a #GtkOptionMenu all setup to allow the user to enter memory sizes.
*
* Returns: Pointer to the new #GimpMemsizeEntry.
**/
GtkWidget *
gimp_memsize_entry_new (guint64 value,
guint64 lower,
guint64 upper)
{
GimpMemsizeEntry *entry;
GimpMemsizeEntryPrivate *private;
guint shift;
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
g_return_val_if_fail (value >= lower && value <= upper, NULL);
entry = g_object_new (GIMP_TYPE_MEMSIZE_ENTRY, NULL);
private = GET_PRIVATE (entry);
for (shift = 30; shift > 10; shift -= 10)
{
if (value > (G_GUINT64_CONSTANT (1) << shift) &&
value % (G_GUINT64_CONSTANT (1) << shift) == 0)
break;
}
private->value = value;
private->lower = lower;
private->upper = upper;
private->shift = shift;
private->adjustment = gtk_adjustment_new (CAST (value >> shift),
CAST (lower >> shift),
CAST (upper >> shift),
1, 8, 0);
private->spinbutton = gtk_spin_button_new (private->adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (private->spinbutton), TRUE);
#undef CAST
gtk_entry_set_width_chars (GTK_ENTRY (private->spinbutton), 7);
gtk_box_pack_start (GTK_BOX (entry), private->spinbutton, FALSE, FALSE, 0);
gtk_widget_show (private->spinbutton);
g_signal_connect (private->adjustment, "value-changed",
G_CALLBACK (gimp_memsize_entry_adj_callback),
entry);
private->menu = gimp_int_combo_box_new (_("Kilobytes"), 10,
_("Megabytes"), 20,
_("Gigabytes"), 30,
NULL);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->menu), shift);
g_signal_connect (private->menu, "changed",
G_CALLBACK (gimp_memsize_entry_unit_callback),
entry);
gtk_box_pack_start (GTK_BOX (entry), private->menu, FALSE, FALSE, 0);
gtk_widget_show (private->menu);
return GTK_WIDGET (entry);
}
/**
* gimp_memsize_entry_set_value:
* @entry: a #GimpMemsizeEntry
* @value: the new value (in Bytes)
*
* Sets the @entry's value. Please note that the #GimpMemsizeEntry rounds
* the value to full Kilobytes.
**/
void
gimp_memsize_entry_set_value (GimpMemsizeEntry *entry,
guint64 value)
{
GimpMemsizeEntryPrivate *private;
guint shift;
g_return_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry));
private = GET_PRIVATE (entry);
g_return_if_fail (value >= private->lower && value <= private->upper);
for (shift = 30; shift > 10; shift -= 10)
{
if (value > (G_GUINT64_CONSTANT (1) << shift) &&
value % (G_GUINT64_CONSTANT (1) << shift) == 0)
break;
}
if (shift != private->shift)
{
private->shift = shift;
private->value = value;
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (private->menu), shift);
}
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
gtk_adjustment_set_value (private->adjustment, CAST (value >> shift));
#undef CASE
}
/**
* gimp_memsize_entry_get_value:
* @entry: a #GimpMemsizeEntry
*
* Retrieves the current value from a #GimpMemsizeEntry.
*
* Returns: the current value of @entry (in Bytes).
**/
guint64
gimp_memsize_entry_get_value (GimpMemsizeEntry *entry)
{
g_return_val_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry), 0);
return GET_PRIVATE (entry)->value;
}
/**
* gimp_memsize_entry_get_spinbutton:
* @entry: a #GimpMemsizeEntry
*
* Returns: the entry's #GtkSpinbutton.
*
* Since: GIMP 3.0
**/
GtkWidget *
gimp_memsize_entry_get_spinbutton (GimpMemsizeEntry *entry)
{
g_return_val_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry), 0);
return GET_PRIVATE (entry)->spinbutton;
}