mirror of
https://gitlab.gnome.org/GNOME/evince
synced 2024-07-04 16:48:55 +00:00
61152289ac
This adapts to the most recent used pattern and simplifies
the code.
It was required to modify EvZoomAction to fit the template model.
EvZoomAction needs the model (EvDocumentModel) to be fully-functional.
The said model is not available at the widget creation time when using
templates because the model is only available programmatically. Therefore,
we partially revert commit 55b21fe75e
by using again ev_zoom_action_set_model. In the process, the "document-model"
property is also removed, as it is not needed (never emmited, no consumers).
The G_PARAM_CONSTRUCT_ONLY condition of the property is partially kept by
making sure that we throw a critical if the model is already set.
369 lines
12 KiB
C
369 lines
12 KiB
C
/* ev-zoom-action.c
|
|
* this file is part of evince, a gnome document viewer
|
|
*
|
|
* Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org>
|
|
* Copyright (C) 2018 Germán Poo-Caamaño <gpoo@gnome.org>
|
|
*
|
|
* Evince 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.
|
|
*
|
|
* Evince 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "ev-zoom-action.h"
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
enum {
|
|
ACTIVATED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum {
|
|
ZOOM_MODES_SECTION,
|
|
ZOOM_FREE_SECTION
|
|
};
|
|
|
|
static const struct {
|
|
const gchar *name;
|
|
float level;
|
|
} zoom_levels[] = {
|
|
{ N_("50%"), 0.5 },
|
|
{ N_("70%"), 0.7071067811 },
|
|
{ N_("85%"), 0.8408964152 },
|
|
{ N_("100%"), 1.0 },
|
|
{ N_("125%"), 1.1892071149 },
|
|
{ N_("150%"), 1.4142135623 },
|
|
{ N_("175%"), 1.6817928304 },
|
|
{ N_("200%"), 2.0 },
|
|
{ N_("300%"), 2.8284271247 },
|
|
{ N_("400%"), 4.0 },
|
|
{ N_("800%"), 8.0 },
|
|
{ N_("1600%"), 16.0 },
|
|
{ N_("3200%"), 32.0 },
|
|
{ N_("6400%"), 64.0 }
|
|
};
|
|
|
|
typedef struct {
|
|
GtkWidget *entry;
|
|
|
|
EvDocumentModel *model;
|
|
GMenu *menu;
|
|
|
|
GMenuModel *zoom_free_section;
|
|
GtkPopover *popup;
|
|
} EvZoomActionPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (EvZoomAction, ev_zoom_action, GTK_TYPE_BOX)
|
|
|
|
#define GET_PRIVATE(o) ev_zoom_action_get_instance_private (o)
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
#define EPSILON 0.000001
|
|
|
|
static void
|
|
ev_zoom_action_set_zoom_level (EvZoomAction *zoom_action,
|
|
float zoom)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
gchar *zoom_str;
|
|
float zoom_perc;
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++) {
|
|
if (ABS (zoom - zoom_levels[i].level) < EPSILON) {
|
|
gtk_entry_set_text (GTK_ENTRY (priv->entry),
|
|
zoom_levels[i].name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
zoom_perc = zoom * 100.;
|
|
if (ABS ((gint)zoom_perc - zoom_perc) < 0.01)
|
|
zoom_str = g_strdup_printf ("%d%%", (gint)zoom_perc);
|
|
else
|
|
zoom_str = g_strdup_printf ("%.1f%%", zoom_perc);
|
|
gtk_entry_set_text (GTK_ENTRY (priv->entry), zoom_str);
|
|
g_free (zoom_str);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_update_zoom_level (EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
float zoom = ev_document_model_get_scale (priv->model);
|
|
|
|
zoom *= 72.0 / ev_document_misc_get_widget_dpi (GTK_WIDGET (zoom_action));
|
|
|
|
ev_zoom_action_set_zoom_level (zoom_action, zoom);
|
|
}
|
|
|
|
static void
|
|
zoom_changed_cb (EvDocumentModel *model,
|
|
GParamSpec *pspec,
|
|
EvZoomAction *zoom_action)
|
|
{
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
}
|
|
|
|
static void
|
|
document_changed_cb (EvDocumentModel *model,
|
|
GParamSpec *pspec,
|
|
EvZoomAction *zoom_action)
|
|
{
|
|
EvDocument *document = ev_document_model_get_document (model);
|
|
|
|
if (!document) {
|
|
gtk_widget_set_sensitive (GTK_WIDGET (zoom_action), FALSE);
|
|
return;
|
|
}
|
|
gtk_widget_set_sensitive (GTK_WIDGET (zoom_action), ev_document_get_n_pages (document) > 0);
|
|
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_set_width_chars (EvZoomAction *zoom_action,
|
|
gint width)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
/* width + 2 (one decimals and the comma) + 3 (for the icon) */
|
|
gtk_entry_set_width_chars (GTK_ENTRY (priv->entry), width + 2 + 3);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_populate_free_zoom_section (EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
gdouble max_scale;
|
|
guint i;
|
|
gint width = 0;
|
|
|
|
max_scale = ev_document_model_get_max_scale (priv->model);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++) {
|
|
GMenuItem *item;
|
|
gint length;
|
|
|
|
if (zoom_levels[i].level > max_scale)
|
|
break;
|
|
|
|
length = g_utf8_strlen (zoom_levels[i].name, -1);
|
|
if (length > width)
|
|
width = length;
|
|
|
|
item = g_menu_item_new (zoom_levels[i].name, NULL);
|
|
g_menu_item_set_action_and_target (item, "win.zoom",
|
|
"d", zoom_levels[i].level);
|
|
g_menu_append_item (G_MENU (priv->zoom_free_section), item);
|
|
g_object_unref (item);
|
|
}
|
|
|
|
ev_zoom_action_set_width_chars (zoom_action, width);
|
|
}
|
|
|
|
static void
|
|
max_zoom_changed_cb (EvDocumentModel *model,
|
|
GParamSpec *pspec,
|
|
EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
g_menu_remove_all (G_MENU (priv->zoom_free_section));
|
|
ev_zoom_action_populate_free_zoom_section (zoom_action);
|
|
}
|
|
|
|
|
|
static void
|
|
entry_activated_cb (GtkEntry *entry,
|
|
EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
double zoom_perc;
|
|
float zoom;
|
|
const gchar *text = gtk_entry_get_text (entry);
|
|
gchar *end_ptr = NULL;
|
|
|
|
if (!text || text[0] == '\0') {
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
g_signal_emit (zoom_action, signals[ACTIVATED], 0, NULL);
|
|
return;
|
|
}
|
|
|
|
zoom_perc = g_strtod (text, &end_ptr);
|
|
if (end_ptr && end_ptr[0] != '\0' && end_ptr[0] != '%') {
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
g_signal_emit (zoom_action, signals[ACTIVATED], 0, NULL);
|
|
return;
|
|
}
|
|
|
|
zoom = zoom_perc / 100.;
|
|
ev_document_model_set_sizing_mode (priv->model, EV_SIZING_FREE);
|
|
ev_document_model_set_scale (priv->model,
|
|
zoom * ev_document_misc_get_widget_dpi (GTK_WIDGET (zoom_action)) / 72.0);
|
|
g_signal_emit (zoom_action, signals[ACTIVATED], 0, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
focus_out_cb (EvZoomAction *zoom_action)
|
|
{
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
entry_icon_press_cb (GtkEntry *entry,
|
|
GtkEntryIconPosition icon_pos,
|
|
GdkEvent *event,
|
|
EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
guint button = 0;
|
|
GdkRectangle rect;
|
|
|
|
g_return_if_fail (priv->popup != NULL);
|
|
|
|
if (gdk_event_get_button (event, &button) &&
|
|
button != GDK_BUTTON_PRIMARY)
|
|
return;
|
|
|
|
/* This cannot be done during init, as window does not yet exist
|
|
* and therefore the rectangle is not yet available */
|
|
if (gtk_popover_get_pointing_to (priv->popup, &rect) == FALSE) {
|
|
gtk_entry_get_icon_area (GTK_ENTRY (priv->entry),
|
|
GTK_ENTRY_ICON_SECONDARY, &rect);
|
|
gtk_popover_set_pointing_to (priv->popup, &rect);
|
|
}
|
|
gtk_popover_popup (priv->popup);
|
|
}
|
|
|
|
void
|
|
ev_zoom_action_set_model (EvZoomAction *zoom_action,
|
|
EvDocumentModel *model)
|
|
{
|
|
EvZoomActionPrivate *priv;
|
|
|
|
g_return_if_fail (EV_IS_ZOOM_ACTION (zoom_action));
|
|
g_return_if_fail (EV_IS_DOCUMENT_MODEL (model));
|
|
|
|
priv = GET_PRIVATE (zoom_action);
|
|
|
|
g_return_if_fail (priv->model == NULL);
|
|
priv->model = model;
|
|
|
|
ev_zoom_action_populate_free_zoom_section (zoom_action);
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (priv->model),
|
|
(gpointer)&priv->model);
|
|
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
|
|
g_signal_connect_object (priv->model, "notify::document",
|
|
G_CALLBACK (document_changed_cb),
|
|
zoom_action, 0);
|
|
g_signal_connect_object (priv->model, "notify::scale",
|
|
G_CALLBACK (zoom_changed_cb),
|
|
zoom_action, 0);
|
|
g_signal_connect_object (priv->model, "notify::max-scale",
|
|
G_CALLBACK (max_zoom_changed_cb),
|
|
zoom_action, 0);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_finalize (GObject *object)
|
|
{
|
|
EvZoomAction *zoom_action = EV_ZOOM_ACTION (object);
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
if (priv->model) {
|
|
g_object_remove_weak_pointer (G_OBJECT (priv->model),
|
|
(gpointer)&priv->model);
|
|
}
|
|
|
|
g_clear_object (&priv->zoom_free_section);
|
|
|
|
G_OBJECT_CLASS (ev_zoom_action_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
setup_initial_entry_size (EvZoomAction *zoom_action)
|
|
{
|
|
gint width;
|
|
|
|
width = g_utf8_strlen (zoom_levels[G_N_ELEMENTS (zoom_levels) - 1].name, -1);
|
|
ev_zoom_action_set_width_chars (zoom_action, width);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_constructed (GObject *object)
|
|
{
|
|
EvZoomAction *zoom_action = EV_ZOOM_ACTION (object);
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
G_OBJECT_CLASS (ev_zoom_action_parent_class)->constructed (object);
|
|
|
|
priv->zoom_free_section =
|
|
g_menu_model_get_item_link (G_MENU_MODEL (priv->menu),
|
|
ZOOM_FREE_SECTION, G_MENU_LINK_SECTION);
|
|
|
|
setup_initial_entry_size (zoom_action);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_class_init (EvZoomActionClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
|
|
|
|
object_class->finalize = ev_zoom_action_finalize;
|
|
object_class->constructed = ev_zoom_action_constructed;
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class,
|
|
"/org/gnome/evince/ui/zoom-action.ui");
|
|
gtk_widget_class_bind_template_child_private (widget_class,
|
|
EvZoomAction,
|
|
entry);
|
|
gtk_widget_class_bind_template_child_private (widget_class, EvZoomAction, popup);
|
|
gtk_widget_class_bind_template_child_private (widget_class, EvZoomAction, menu);
|
|
gtk_widget_class_bind_template_callback (widget_class, entry_icon_press_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, entry_activated_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, focus_out_cb);
|
|
|
|
signals[ACTIVATED] =
|
|
g_signal_new ("activated",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
ev_zoom_action_init (EvZoomAction *zoom_action)
|
|
{
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (zoom_action));
|
|
|
|
/* TODO: In GTK4, GtkPopoverMenu has a menu-model property.
|
|
So this could be moved to the template instead after the
|
|
GTK4 move
|
|
*/
|
|
gtk_popover_bind_model (priv->popup, G_MENU_MODEL (priv->menu), NULL);
|
|
}
|