Gimp/app/display/gimpdisplayshell-scale-dialog.c
Jehan d71cd9d277 Issue #9604: "view-zoom-*" actions should not be radio actions.
A first attempt at fixing this was going through the idea of changing the
concept of radio actions, such as allowing an active action in an action group
to be called again. Or having some action in the radio group which can be called
but never set active.

But in fact, I just realized that these zoom actions are simply not meant to be
radio actions. They are not stateful actions, nor are they exhaustive.

I also updated the `other_scale` storage logic. Instead of updating it each time
the zoom changed (which was even broken in some cases, like when changing zoom
through another action), I simply save the last custom zoom value. This is the
one which is reused across calls. I don't think always resetting to the current
zoom value is very useful (if you call this dialog, it's not to zoom to the
current zoom!). Also there was some concept of flagging this stored zoom value
as "dirty" by making it negative, but this was never actually used, which makes
me believe that current logic was not the original intent anyway.
Saving the previous custom zoom explicitly set seems to be a good enough
behavior, so let's go with it.
2023-09-24 23:51:14 +02:00

321 lines
11 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "display-types.h"
#include "core/gimp.h"
#include "core/gimpviewable.h"
#include "widgets/gimpaction.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpviewabledialog.h"
#include "gimpdisplay.h"
#include "gimpdisplayshell.h"
#include "gimpdisplayshell-scale.h"
#include "gimpdisplayshell-scale-dialog.h"
#include "gimp-intl.h"
#define SCALE_EPSILON 0.0001
#define SCALE_EQUALS(a,b) (fabs ((a) - (b)) < SCALE_EPSILON)
typedef struct
{
GimpDisplayShell *shell;
GimpZoomModel *model;
GtkAdjustment *scale_adj;
GtkAdjustment *num_adj;
GtkAdjustment *denom_adj;
gdouble prev_scale;
gdouble *other_scale;
} ScaleDialogData;
/* local function prototypes */
static void gimp_display_shell_scale_dialog_response (GtkWidget *widget,
gint response_id,
ScaleDialogData *dialog);
static void gimp_display_shell_scale_dialog_free (ScaleDialogData *dialog);
static void update_zoom_values (GtkAdjustment *adj,
ScaleDialogData *dialog);
/* public functions */
/**
* gimp_display_shell_scale_dialog:
* @shell: the #GimpDisplayShell
*
* Constructs and displays a dialog allowing the user to enter a
* custom display scale.
**/
void
gimp_display_shell_scale_dialog (GimpDisplayShell *shell)
{
/* scale factor entered in Zoom->Other*/
static gdouble other_scale = 0.0;
ScaleDialogData *data;
GimpImage *image;
GtkWidget *toplevel;
GtkWidget *hbox;
GtkWidget *grid;
GtkWidget *spin;
GtkWidget *label;
gint num, denom, row;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
if (shell->scale_dialog)
{
gtk_window_present (GTK_WINDOW (shell->scale_dialog));
return;
}
data = g_slice_new (ScaleDialogData);
data->prev_scale = other_scale;
data->other_scale = &other_scale;
if (SCALE_EQUALS (other_scale, 0.0))
{
/* other_scale not yet initialized */
other_scale = gimp_zoom_model_get_factor (shell->zoom);
}
image = gimp_display_get_image (shell->display);
data->shell = shell;
data->model = g_object_new (GIMP_TYPE_ZOOM_MODEL,
"value", fabs (other_scale),
NULL);
g_set_weak_pointer
(&shell->scale_dialog,
gimp_viewable_dialog_new (g_list_prepend (NULL, image),
gimp_get_user_context (shell->display->gimp),
_("Zoom Ratio"), "display_scale",
"zoom-original",
_("Select Zoom Ratio"),
GTK_WIDGET (shell),
gimp_standard_help_func,
GIMP_HELP_VIEW_ZOOM_OTHER,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL));
gimp_dialog_set_alternative_button_order (GTK_DIALOG (shell->scale_dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
g_object_weak_ref (G_OBJECT (shell->scale_dialog),
(GWeakNotify) gimp_display_shell_scale_dialog_free, data);
g_object_weak_ref (G_OBJECT (shell->scale_dialog),
(GWeakNotify) g_object_unref, data->model);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
gtk_window_set_transient_for (GTK_WINDOW (shell->scale_dialog),
GTK_WINDOW (toplevel));
gtk_window_set_destroy_with_parent (GTK_WINDOW (shell->scale_dialog), TRUE);
g_signal_connect (shell->scale_dialog, "response",
G_CALLBACK (gimp_display_shell_scale_dialog_response),
data);
grid = gtk_grid_new ();
gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell->scale_dialog))),
grid, TRUE, TRUE, 0);
gtk_widget_show (grid);
row = 0;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
_("Zoom ratio:"), 0.0, 0.5,
hbox, 1);
gimp_zoom_model_get_fraction (data->model, &num, &denom);
data->num_adj = gtk_adjustment_new (num, 1, 256, 1, 8, 0);
spin = gimp_spin_button_new (data->num_adj, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
gtk_widget_show (spin);
label = gtk_label_new (":");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
data->denom_adj = gtk_adjustment_new (denom, 1, 256, 1, 8, 0);
spin = gimp_spin_button_new (data->denom_adj, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
gtk_widget_show (spin);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
_("Zoom:"), 0.0, 0.5,
hbox, 1);
data->scale_adj = gtk_adjustment_new (other_scale * 100,
100.0 / 256.0, 25600.0,
10, 50, 0);
spin = gimp_spin_button_new (data->scale_adj, 1.0, 2);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin), TRUE);
gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
gtk_widget_show (spin);
label = gtk_label_new ("%");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_signal_connect (data->scale_adj, "value-changed",
G_CALLBACK (update_zoom_values), data);
g_signal_connect (data->num_adj, "value-changed",
G_CALLBACK (update_zoom_values), data);
g_signal_connect (data->denom_adj, "value-changed",
G_CALLBACK (update_zoom_values), data);
gtk_widget_show (shell->scale_dialog);
}
static void
gimp_display_shell_scale_dialog_response (GtkWidget *widget,
gint response_id,
ScaleDialogData *dialog)
{
if (response_id == GTK_RESPONSE_OK)
{
GAction *action;
gchar *label;
gchar *zoom_str;
gdouble scale;
scale = gtk_adjustment_get_value (dialog->scale_adj);
gimp_display_shell_scale (dialog->shell,
GIMP_ZOOM_TO,
scale / 100.0,
GIMP_ZOOM_FOCUS_BEST_GUESS);
g_object_get (dialog->shell->zoom,
"percentage", &zoom_str,
NULL);
/* Change the "view-zoom-other" label. */
action = g_action_map_lookup_action (G_ACTION_MAP (dialog->shell->display->gimp->app),
"view-zoom-other");
label = g_strdup_printf (_("Othe_r (%s)..."), zoom_str);
gimp_action_set_short_label (GIMP_ACTION (action), label);
g_free (label);
label = g_strdup_printf (_("Custom Zoom (%s)..."), zoom_str);
gimp_action_set_label (GIMP_ACTION (action), label);
g_free (label);
g_free (zoom_str);
}
else
{
/* need to emit "scaled" to get the menu updated */
gimp_display_shell_scaled (dialog->shell);
*(dialog->other_scale) = dialog->prev_scale;
}
gtk_widget_destroy (dialog->shell->scale_dialog);
}
static void
gimp_display_shell_scale_dialog_free (ScaleDialogData *dialog)
{
g_slice_free (ScaleDialogData, dialog);
}
static void
update_zoom_values (GtkAdjustment *adj,
ScaleDialogData *dialog)
{
gint num, denom;
gdouble scale;
g_signal_handlers_block_by_func (dialog->scale_adj,
update_zoom_values,
dialog);
g_signal_handlers_block_by_func (dialog->num_adj,
update_zoom_values,
dialog);
g_signal_handlers_block_by_func (dialog->denom_adj,
update_zoom_values,
dialog);
if (adj == dialog->scale_adj)
{
scale = gtk_adjustment_get_value (dialog->scale_adj);
gimp_zoom_model_zoom (dialog->model, GIMP_ZOOM_TO, scale / 100.0);
gimp_zoom_model_get_fraction (dialog->model, &num, &denom);
gtk_adjustment_set_value (dialog->num_adj, num);
gtk_adjustment_set_value (dialog->denom_adj, denom);
*(dialog->other_scale) = scale / 100.0;
}
else /* fraction adjustments */
{
scale = (gtk_adjustment_get_value (dialog->num_adj) /
gtk_adjustment_get_value (dialog->denom_adj));
gtk_adjustment_set_value (dialog->scale_adj, scale * 100);
*(dialog->other_scale) = scale;
}
g_signal_handlers_unblock_by_func (dialog->scale_adj,
update_zoom_values,
dialog);
g_signal_handlers_unblock_by_func (dialog->num_adj,
update_zoom_values,
dialog);
g_signal_handlers_unblock_by_func (dialog->denom_adj,
update_zoom_values,
dialog);
}