2012-11-04 19:52:30 +00:00
|
|
|
/* ev-zoom-action.c
|
|
|
|
* this file is part of evince, a gnome document viewer
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org>
|
2018-11-13 15:05:14 +00:00
|
|
|
* Copyright (C) 2018 Germán Poo-Caamaño <gpoo@gnome.org>
|
2012-11-04 19:52:30 +00:00
|
|
|
*
|
|
|
|
* 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
|
2014-01-08 20:30:28 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2012-11-04 19:52:30 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "ev-zoom-action.h"
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
#include <glib/gi18n.h>
|
2012-11-04 19:52:30 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
ACTIVATED,
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
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 }
|
|
|
|
};
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
typedef struct {
|
2014-06-21 09:33:24 +00:00
|
|
|
GtkWidget *entry;
|
|
|
|
|
2012-11-04 19:52:30 +00:00
|
|
|
EvDocumentModel *model;
|
2014-06-21 09:33:24 +00:00
|
|
|
GMenu *menu;
|
|
|
|
|
|
|
|
GMenuModel *zoom_free_section;
|
2018-05-28 20:38:52 +00:00
|
|
|
GtkPopover *popup;
|
2018-11-13 15:05:14 +00:00
|
|
|
} EvZoomActionPrivate;
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (EvZoomAction, ev_zoom_action, GTK_TYPE_BOX)
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
#define GET_PRIVATE(o) ev_zoom_action_get_instance_private (o)
|
2012-11-04 19:52:30 +00:00
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
#define EPSILON 0.000001
|
|
|
|
|
|
|
|
static void
|
|
|
|
ev_zoom_action_set_zoom_level (EvZoomAction *zoom_action,
|
|
|
|
float zoom)
|
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2014-06-21 09:33:24 +00:00
|
|
|
gchar *zoom_str;
|
|
|
|
float zoom_perc;
|
|
|
|
guint i;
|
|
|
|
|
2014-07-24 16:35:59 +00:00
|
|
|
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++) {
|
2014-06-21 09:33:24 +00:00
|
|
|
if (ABS (zoom - zoom_levels[i].level) < EPSILON) {
|
2018-11-13 15:05:14 +00:00
|
|
|
gtk_entry_set_text (GTK_ENTRY (priv->entry),
|
2014-06-21 09:33:24 +00:00
|
|
|
zoom_levels[i].name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zoom_perc = zoom * 100.;
|
2018-07-03 17:23:49 +00:00
|
|
|
if (ABS ((gint)zoom_perc - zoom_perc) < 0.01)
|
2014-06-21 09:33:24 +00:00
|
|
|
zoom_str = g_strdup_printf ("%d%%", (gint)zoom_perc);
|
|
|
|
else
|
2018-07-03 17:23:49 +00:00
|
|
|
zoom_str = g_strdup_printf ("%.1f%%", zoom_perc);
|
2018-11-13 15:05:14 +00:00
|
|
|
gtk_entry_set_text (GTK_ENTRY (priv->entry), zoom_str);
|
2014-06-21 09:33:24 +00:00
|
|
|
g_free (zoom_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ev_zoom_action_update_zoom_level (EvZoomAction *zoom_action)
|
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
|
2019-10-24 14:28:21 +00:00
|
|
|
float zoom = ev_document_model_get_scale (priv->model);
|
|
|
|
|
|
|
|
zoom *= 72.0 / ev_document_misc_get_widget_dpi (GTK_WIDGET (zoom_action));
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-07-25 08:17:59 +00:00
|
|
|
static void
|
|
|
|
ev_zoom_action_set_width_chars (EvZoomAction *zoom_action,
|
|
|
|
gint width)
|
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
|
2018-07-03 17:23:49 +00:00
|
|
|
/* width + 2 (one decimals and the comma) + 3 (for the icon) */
|
2018-11-13 15:05:14 +00:00
|
|
|
gtk_entry_set_width_chars (GTK_ENTRY (priv->entry), width + 2 + 3);
|
2014-07-25 08:17:59 +00:00
|
|
|
}
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
static void
|
|
|
|
ev_zoom_action_populate_free_zoom_section (EvZoomAction *zoom_action)
|
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2014-06-21 09:33:24 +00:00
|
|
|
gdouble max_scale;
|
|
|
|
guint i;
|
2014-07-25 08:17:59 +00:00
|
|
|
gint width = 0;
|
2014-06-21 09:33:24 +00:00
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
max_scale = ev_document_model_get_max_scale (priv->model);
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++) {
|
|
|
|
GMenuItem *item;
|
2014-07-25 08:17:59 +00:00
|
|
|
gint length;
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
if (zoom_levels[i].level > max_scale)
|
|
|
|
break;
|
|
|
|
|
2014-07-25 08:17:59 +00:00
|
|
|
length = g_utf8_strlen (zoom_levels[i].name, -1);
|
|
|
|
if (length > width)
|
|
|
|
width = length;
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
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);
|
2018-11-13 15:05:14 +00:00
|
|
|
g_menu_append_item (G_MENU (priv->zoom_free_section), item);
|
2014-06-21 09:33:24 +00:00
|
|
|
g_object_unref (item);
|
|
|
|
}
|
2014-07-25 08:17:59 +00:00
|
|
|
|
|
|
|
ev_zoom_action_set_width_chars (zoom_action, width);
|
2014-06-21 09:33:24 +00:00
|
|
|
}
|
|
|
|
|
2012-11-04 19:52:30 +00:00
|
|
|
static void
|
2014-06-21 09:33:24 +00:00
|
|
|
max_zoom_changed_cb (EvDocumentModel *model,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
EvZoomAction *zoom_action)
|
2012-11-04 19:52:30 +00:00
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
|
|
|
|
g_menu_remove_all (G_MENU (priv->zoom_free_section));
|
2014-06-21 09:33:24 +00:00
|
|
|
ev_zoom_action_populate_free_zoom_section (zoom_action);
|
2012-11-04 19:52:30 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 20:23:05 +00:00
|
|
|
|
2012-11-04 19:52:30 +00:00
|
|
|
static void
|
2014-06-21 09:33:24 +00:00
|
|
|
entry_activated_cb (GtkEntry *entry,
|
|
|
|
EvZoomAction *zoom_action)
|
2012-11-04 19:52:30 +00:00
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
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.;
|
2018-11-13 15:05:14 +00:00
|
|
|
ev_document_model_set_sizing_mode (priv->model, EV_SIZING_FREE);
|
|
|
|
ev_document_model_set_scale (priv->model,
|
2019-10-24 14:28:21 +00:00
|
|
|
zoom * ev_document_misc_get_widget_dpi (GTK_WIDGET (zoom_action)) / 72.0);
|
2012-11-04 19:52:30 +00:00
|
|
|
g_signal_emit (zoom_action, signals[ACTIVATED], 0, NULL);
|
|
|
|
}
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
static gboolean
|
|
|
|
focus_out_cb (EvZoomAction *zoom_action)
|
|
|
|
{
|
|
|
|
ev_zoom_action_update_zoom_level (zoom_action);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-03-31 21:05:48 +00:00
|
|
|
entry_icon_press_cb (GtkEntry *entry,
|
|
|
|
GtkEntryIconPosition icon_pos,
|
|
|
|
GdkEvent *event,
|
|
|
|
EvZoomAction *zoom_action)
|
2014-06-21 09:33:24 +00:00
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2018-09-28 15:39:24 +00:00
|
|
|
guint button = 0;
|
2022-03-31 20:52:24 +00:00
|
|
|
GdkRectangle rect;
|
|
|
|
|
|
|
|
g_return_if_fail (priv->popup != NULL);
|
2018-09-28 15:39:24 +00:00
|
|
|
|
|
|
|
if (gdk_event_get_button (event, &button) &&
|
|
|
|
button != GDK_BUTTON_PRIMARY)
|
2014-06-21 09:33:24 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-31 20:52:24 +00:00
|
|
|
/* 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);
|
2014-06-21 09:33:24 +00:00
|
|
|
}
|
|
|
|
|
2022-03-29 16:11:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
static void
|
|
|
|
ev_zoom_action_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
EvZoomAction *zoom_action = EV_ZOOM_ACTION (object);
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2014-06-21 09:33:24 +00:00
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
if (priv->model) {
|
|
|
|
g_object_remove_weak_pointer (G_OBJECT (priv->model),
|
|
|
|
(gpointer)&priv->model);
|
2014-06-21 09:33:24 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
g_clear_object (&priv->zoom_free_section);
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (ev_zoom_action_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2012-11-04 19:52:30 +00:00
|
|
|
static void
|
2014-06-21 09:33:24 +00:00
|
|
|
setup_initial_entry_size (EvZoomAction *zoom_action)
|
2012-11-04 19:52:30 +00:00
|
|
|
{
|
2014-07-25 08:17:59 +00:00
|
|
|
gint width;
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2014-07-25 08:17:59 +00:00
|
|
|
width = g_utf8_strlen (zoom_levels[G_N_ELEMENTS (zoom_levels) - 1].name, -1);
|
|
|
|
ev_zoom_action_set_width_chars (zoom_action, width);
|
2014-06-29 09:35:39 +00:00
|
|
|
}
|
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
static void
|
|
|
|
ev_zoom_action_constructed (GObject *object)
|
|
|
|
{
|
|
|
|
EvZoomAction *zoom_action = EV_ZOOM_ACTION (object);
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2014-06-21 09:33:24 +00:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (ev_zoom_action_parent_class)->constructed (object);
|
|
|
|
|
2018-11-13 15:05:14 +00:00
|
|
|
priv->zoom_free_section =
|
|
|
|
g_menu_model_get_item_link (G_MENU_MODEL (priv->menu),
|
2014-06-21 09:33:24 +00:00
|
|
|
ZOOM_FREE_SECTION, G_MENU_LINK_SECTION);
|
|
|
|
|
|
|
|
setup_initial_entry_size (zoom_action);
|
2012-11-04 19:52:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-06-29 09:35:39 +00:00
|
|
|
ev_zoom_action_class_init (EvZoomActionClass *klass)
|
2012-11-04 19:52:30 +00:00
|
|
|
{
|
2014-06-29 09:35:39 +00:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2014-06-21 09:33:24 +00:00
|
|
|
object_class->finalize = ev_zoom_action_finalize;
|
|
|
|
object_class->constructed = ev_zoom_action_constructed;
|
|
|
|
|
2022-03-04 20:23:05 +00:00
|
|
|
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);
|
2022-03-31 20:52:24 +00:00
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, EvZoomAction, popup);
|
2022-03-31 21:31:27 +00:00
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, EvZoomAction, menu);
|
2022-03-31 21:05:48 +00:00
|
|
|
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);
|
2014-06-29 09:35:39 +00:00
|
|
|
|
2012-11-04 19:52:30 +00:00
|
|
|
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
|
2014-06-21 09:33:24 +00:00
|
|
|
ev_zoom_action_init (EvZoomAction *zoom_action)
|
2012-11-04 19:52:30 +00:00
|
|
|
{
|
2018-11-13 15:05:14 +00:00
|
|
|
EvZoomActionPrivate *priv = GET_PRIVATE (zoom_action);
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2022-03-04 20:23:05 +00:00
|
|
|
gtk_widget_init_template (GTK_WIDGET (zoom_action));
|
2012-11-04 19:52:30 +00:00
|
|
|
|
2022-03-31 20:52:24 +00:00
|
|
|
/* 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);
|
2012-11-04 19:52:30 +00:00
|
|
|
}
|