Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
EvBrowserPluginToolbar.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  *
4  * Evince is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * Evince is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "config.h"
20 #include "EvBrowserPluginToolbar.h"
21 
22 #include "ev-page-action-widget.h"
23 #include "ev-search-box.h"
24 #include <glib/gi18n-lib.h>
25 
26 enum {
29 };
30 
33 
35  GtkWidget *dualToggleButton;
39  GtkWidget *searchToggleButton;
40  GtkWidget *searchPopover;
41 };
42 
43 G_DEFINE_TYPE(EvBrowserPluginToolbar, ev_browser_plugin_toolbar, GTK_TYPE_TOOLBAR)
44 
46 {
47  toolbar->priv->plugin->goToPreviousPage();
48 }
49 
50 static void goToNextPage(EvBrowserPluginToolbar *toolbar)
51 {
52  toolbar->priv->plugin->goToNextPage();
53 }
54 
55 static void activateLink(EvBrowserPluginToolbar *toolbar, EvLink *link)
56 {
57  toolbar->priv->plugin->activateLink(link);
58 }
59 
61 {
62  toolbar->priv->plugin->toggleContinuous();
63 }
64 
65 static void toggleDual(EvBrowserPluginToolbar *toolbar)
66 {
67  toolbar->priv->plugin->toggleDual();
68 }
69 
70 static void zoomIn(EvBrowserPluginToolbar *toolbar)
71 {
72  toolbar->priv->plugin->zoomIn();
73 }
74 
75 static void zoomOut(EvBrowserPluginToolbar *toolbar)
76 {
77  toolbar->priv->plugin->zoomOut();
78 }
79 
81 {
82  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomFitPageRadioButton)))
84 }
85 
87 {
88  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomFitWidthRadioButton)))
90 }
91 
93 {
94  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomAutomaticRadioButton)))
96 }
97 
99 {
100  toolbar->priv->plugin->print();
101 }
102 
104 {
105  toolbar->priv->plugin->download();
106 }
107 
109 {
110  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->priv->searchToggleButton)))
111  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->priv->searchToggleButton), FALSE);
112 }
113 
114 static void searchStarted(EvBrowserPluginToolbar *toolbar, EvJobFind *job)
115 {
116  toolbar->priv->plugin->search(job);
117 }
118 
120 {
121  toolbar->priv->plugin->clearSearch();
122 }
123 
124 static void searchNext(EvBrowserPluginToolbar *toolbar)
125 {
126  toolbar->priv->plugin->search(EvBrowserPlugin::SearchDirection::Next);
127 }
128 
130 {
131  toolbar->priv->plugin->search(EvBrowserPlugin::SearchDirection::Previous);
132 }
133 
135 {
136  if (!toolbar->priv->searchPopover) {
137  toolbar->priv->searchPopover = gtk_popover_new(toolbar->priv->searchToggleButton);
138  gtk_popover_set_position (GTK_POPOVER (toolbar->priv->searchPopover), GTK_POS_BOTTOM);
139  g_signal_connect_swapped(toolbar->priv->searchPopover, "closed", G_CALLBACK(searchPopoverClosed), toolbar);
140  GtkWidget *searchBox = ev_search_box_new(toolbar->priv->plugin->model());
141  g_signal_connect_swapped(searchBox, "started", G_CALLBACK(searchStarted), toolbar);
142  g_signal_connect_swapped(searchBox, "cleared", G_CALLBACK(searchCleared), toolbar);
143  g_signal_connect_swapped(searchBox, "next", G_CALLBACK(searchNext), toolbar);
144  g_signal_connect_swapped(searchBox, "previous", G_CALLBACK(searchPrevious), toolbar);
145  gtk_container_add(GTK_CONTAINER(toolbar->priv->searchPopover), searchBox);
146  gtk_widget_show(searchBox);
147  }
148 
149  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->priv->searchToggleButton))) {
150  gtk_widget_show(toolbar->priv->searchPopover);
151  toolbar->priv->plugin->setSearchModeEnabled(true);
152 
153  GtkSearchEntry *entry = ev_search_box_get_entry(EV_SEARCH_BOX(gtk_bin_get_child(GTK_BIN(toolbar->priv->searchPopover))));
154  const char *searchString = gtk_entry_get_text(GTK_ENTRY(entry));
155  if (searchString && searchString[0])
156  toolbar->priv->plugin->restartSearch();
157  } else {
158  gtk_widget_hide(toolbar->priv->searchPopover);
159  toolbar->priv->plugin->setSearchModeEnabled(false);
160  }
161 }
162 
164 public:
165  SignalBlocker(gpointer instance, void (* closure)(EvBrowserPluginToolbar *), gpointer data)
166  : m_instance(instance)
167  , m_closure(reinterpret_cast<gpointer>(closure))
168  , m_data(data)
169  {
170  g_signal_handlers_block_by_func(m_instance, m_closure, m_data);
171  }
172 
174  {
175  g_signal_handlers_unblock_by_func(m_instance, m_closure, m_data);
176  }
177 
178 private:
179  gpointer m_instance;
180  gpointer m_closure;
181  gpointer m_data;
182 };
183 
184 static void continuousChanged(EvDocumentModel *model, GParamSpec *, EvBrowserPluginToolbar *toolbar)
185 {
186  SignalBlocker blocker(toolbar->priv->continuousToggleButton, toggleContinuous, toolbar);
187  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->priv->continuousToggleButton),
188  toolbar->priv->plugin->isContinuous());
189 }
190 
191 static void dualPageChanged(EvDocumentModel *model, GParamSpec *, EvBrowserPluginToolbar *toolbar)
192 {
193  SignalBlocker blocker(toolbar->priv->dualToggleButton, toggleDual, toolbar);
194  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->priv->dualToggleButton),
195  toolbar->priv->plugin->isDual());
196 }
197 
198 static void sizingModeChanged(EvDocumentModel *model, GParamSpec *, EvBrowserPluginToolbar *toolbar)
199 {
200  {
201  SignalBlocker fitPageBlocker(toolbar->priv->zoomFitPageRadioButton, zoomFitPageToggled, toolbar);
202  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomFitPageRadioButton),
203  toolbar->priv->plugin->sizingMode() == EV_SIZING_FIT_PAGE);
204  }
205 
206  {
207  SignalBlocker fitWidthBlocker(toolbar->priv->zoomFitPageRadioButton, zoomFitWidthToggled, toolbar);
208  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomFitWidthRadioButton),
209  toolbar->priv->plugin->sizingMode() == EV_SIZING_FIT_WIDTH);
210  }
211 
212  {
213  SignalBlocker automaticBlocker(toolbar->priv->zoomAutomaticRadioButton, zoomAutomaticToggled, toolbar);
214  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(toolbar->priv->zoomAutomaticRadioButton),
215  toolbar->priv->plugin->sizingMode() == EV_SIZING_AUTOMATIC);
216  }
217 }
218 
219 static void evBrowserPluginToolbarSetProperty(GObject *object, guint propID, const GValue *value, GParamSpec *paramSpec)
220 {
222 
223  switch (propID) {
224  case PROP_PLUGIN:
225  toolbar->priv->plugin = static_cast<EvBrowserPlugin *>(g_value_get_pointer(value));
226  break;
227  default:
228  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
229  }
230 }
231 
232 static GtkWidget *createButton(EvBrowserPluginToolbar *toolbar, const char *iconName, const char *description, GCallback callback)
233 {
234  GtkWidget *button = gtk_button_new();
235 
236  gtk_widget_set_valign(button, GTK_ALIGN_CENTER);
237  gtk_widget_set_tooltip_text(button, description);
238  gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_icon_name(iconName, GTK_ICON_SIZE_MENU));
239  gtk_button_set_label(GTK_BUTTON(button), nullptr);
240  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
241  g_signal_connect_swapped(button, "clicked", callback, toolbar);
242 
243  return button;
244 }
245 
246 static GtkWidget *createToggleButton(EvBrowserPluginToolbar *toolbar, const char *iconName, const char *description, bool initialState, GCallback callback)
247 {
248  GtkWidget *button = gtk_toggle_button_new();
249 
250  gtk_widget_set_valign(button, GTK_ALIGN_CENTER);
251  gtk_widget_set_tooltip_text(button, description);
252  gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_icon_name(iconName, GTK_ICON_SIZE_MENU));
253  gtk_button_set_label(GTK_BUTTON(button), nullptr);
254  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
255  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), initialState);
256  g_signal_connect_swapped(button, "toggled", callback, toolbar);
257 
258  return button;
259 }
260 
261 static GtkWidget *createMenuButton(EvBrowserPluginToolbar *toolbar, const gchar *iconName, GtkWidget *menu, GtkAlign menuAlign)
262 {
263  GtkWidget *button = gtk_menu_button_new();
264 
265  gtk_widget_set_valign(button, GTK_ALIGN_CENTER);
266  gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_icon_name(iconName, GTK_ICON_SIZE_MENU));
267  gtk_widget_set_halign(menu, menuAlign);
268  gtk_menu_button_set_popup(GTK_MENU_BUTTON(button), menu);
269 
270  return button;
271 }
272 
273 static GtkWidget *createButtonGroup(EvBrowserPluginToolbar *toolbar)
274 {
275  GtkWidget *box = gtk_box_new(gtk_orientable_get_orientation(GTK_ORIENTABLE(toolbar)), 0);
276 
277  GtkStyleContext *styleContext = gtk_widget_get_style_context(box);
278  gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_RAISED);
279  gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_LINKED);
280 
281  return box;
282 }
283 
284 static GtkWidget *createSizingModeMenu(EvBrowserPluginToolbar *toolbar)
285 {
286  GtkWidget *menu = gtk_menu_new();
287 
288  GtkWidget *menuItem = gtk_check_menu_item_new_with_mnemonic(_("Fit Pa_ge"));
289  toolbar->priv->zoomFitPageRadioButton = menuItem;
290  gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(menuItem), TRUE);
291  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuItem),
292  toolbar->priv->plugin->sizingMode() == EV_SIZING_FIT_PAGE);
293  g_signal_connect_swapped(menuItem, "toggled", G_CALLBACK(zoomFitPageToggled), toolbar);
294  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
295  gtk_widget_show(menuItem);
296 
297  menuItem = gtk_check_menu_item_new_with_mnemonic(_("Fit _Width"));
298  toolbar->priv->zoomFitWidthRadioButton = menuItem;
299  gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(menuItem), TRUE);
300  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuItem),
301  toolbar->priv->plugin->sizingMode() == EV_SIZING_FIT_WIDTH);
302  g_signal_connect_swapped(menuItem, "toggled", G_CALLBACK(zoomFitWidthToggled), toolbar);
303  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
304  gtk_widget_show(menuItem);
305 
306  menuItem = gtk_check_menu_item_new_with_mnemonic(_("_Automatic"));
307  toolbar->priv->zoomAutomaticRadioButton = menuItem;
308  gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(menuItem), TRUE);
309  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuItem),
310  toolbar->priv->plugin->sizingMode() == EV_SIZING_AUTOMATIC);
311  g_signal_connect_swapped(menuItem, "toggled", G_CALLBACK(zoomAutomaticToggled), toolbar);
312  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
313  gtk_widget_show(menuItem);
314 
315  g_signal_connect(toolbar->priv->plugin->model(), "notify::sizing-mode",
316  G_CALLBACK(sizingModeChanged), toolbar);
317 
318  return menu;
319 }
320 
321 static void evBrowserPluginToolbarConstructed(GObject *object)
322 {
323  G_OBJECT_CLASS(ev_browser_plugin_toolbar_parent_class)->constructed(object);
324 
326  bool rtl = gtk_widget_get_direction(GTK_WIDGET(toolbar)) == GTK_TEXT_DIR_RTL;
327 
328  GtkWidget *hbox = createButtonGroup(toolbar);
329 
330  // Navigation buttons
331  GtkWidget *button = createButton(toolbar, "go-up-symbolic", _("Go to the previous page"), G_CALLBACK(goToPreviousPage));
332  gtk_container_add(GTK_CONTAINER(hbox), button);
333  gtk_widget_show(button);
334 
335  button = createButton(toolbar, "go-down-symbolic", _("Go to the next page"), G_CALLBACK(goToNextPage));
336  gtk_container_add(GTK_CONTAINER(hbox), button);
337  gtk_widget_show(button);
338 
339  GtkWidget *toolItem = GTK_WIDGET(gtk_tool_item_new());
340  if (rtl)
341  gtk_widget_set_margin_left(toolItem, 12);
342  else
343  gtk_widget_set_margin_right(toolItem, 12);
344  gtk_container_add(GTK_CONTAINER(toolItem), hbox);
345  gtk_widget_show(hbox);
346 
347  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
348  gtk_widget_show(toolItem);
349 
350  // Page Selector
351  toolItem = GTK_WIDGET(g_object_new(EV_TYPE_PAGE_ACTION_WIDGET, nullptr));
353  g_signal_connect_swapped(toolItem, "activate-link", G_CALLBACK(activateLink), toolbar);
354  if (rtl)
355  gtk_widget_set_margin_left(toolItem, 12);
356  else
357  gtk_widget_set_margin_right(toolItem, 12);
358  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
359  gtk_widget_show(toolItem);
360 
361  // Search.
362  button = createToggleButton(toolbar, "edit-find-symbolic", _("Find a word or phrase in the document"),
363  false, G_CALLBACK(toggleSearch));
364  toolbar->priv->searchToggleButton = button;
365  toolItem = GTK_WIDGET(gtk_tool_item_new());
366  gtk_container_add(GTK_CONTAINER(toolItem), button);
367  gtk_widget_show(button);
368 
369  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
370  gtk_widget_show(toolItem);
371 
372  // Separator
373  toolItem = GTK_WIDGET(gtk_tool_item_new());
374  gtk_tool_item_set_expand(GTK_TOOL_ITEM(toolItem), TRUE);
375  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
376  gtk_widget_show(toolItem);
377 
378  // View mode
379  hbox = createButtonGroup(toolbar);
380 
381  button = createToggleButton(toolbar, "view-continuous-symbolic", _("Show the entire document"),
382  toolbar->priv->plugin->isContinuous(), G_CALLBACK(toggleContinuous));
383  toolbar->priv->continuousToggleButton = button;
384  g_signal_connect(toolbar->priv->plugin->model(), "notify::continuous",
385  G_CALLBACK(continuousChanged), toolbar);
386  gtk_container_add(GTK_CONTAINER(hbox), button);
387  gtk_widget_show(button);
388 
389  button = createToggleButton(toolbar, "view-dual-symbolic", _("Show two pages at once"),
390  toolbar->priv->plugin->isDual(), G_CALLBACK(toggleDual));
391  toolbar->priv->dualToggleButton = button;
392  g_signal_connect(toolbar->priv->plugin->model(), "notify::dual-page",
393  G_CALLBACK(dualPageChanged), toolbar);
394  gtk_container_add(GTK_CONTAINER(hbox), button);
395  gtk_widget_show(button);
396 
397  toolItem = GTK_WIDGET(gtk_tool_item_new());
398  if (rtl)
399  gtk_widget_set_margin_left(toolItem, 12);
400  else
401  gtk_widget_set_margin_right(toolItem, 12);
402  gtk_container_add(GTK_CONTAINER(toolItem), hbox);
403  gtk_widget_show(hbox);
404 
405  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
406  gtk_widget_show(toolItem);
407 
408  // Zoom
409  hbox = createButtonGroup(toolbar);
410 
411  button = createButton(toolbar, "zoom-in-symbolic", _("Enlarge the document"), G_CALLBACK(zoomIn));
412  gtk_container_add(GTK_CONTAINER(hbox), button);
413  gtk_widget_show(button);
414 
415  button = createButton(toolbar, "zoom-out-symbolic", _("Shrink the document"), G_CALLBACK(zoomOut));
416  gtk_container_add(GTK_CONTAINER(hbox), button);
417  gtk_widget_show(button);
418 
419  button = createMenuButton(toolbar, "go-down-symbolic", createSizingModeMenu(toolbar), GTK_ALIGN_END);
420  gtk_container_add(GTK_CONTAINER(hbox), button);
421  gtk_widget_show(button);
422 
423  toolItem = GTK_WIDGET(gtk_tool_item_new());
424  if (rtl)
425  gtk_widget_set_margin_left(toolItem, 12);
426  else
427  gtk_widget_set_margin_right(toolItem, 12);
428  gtk_container_add(GTK_CONTAINER(toolItem), hbox);
429  gtk_widget_show(hbox);
430 
431  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
432  gtk_widget_show(toolItem);
433 
434  // Actions
435  // Only add download button if browser is Epiphany for now.
436  if (toolbar->priv->plugin->canDownload()) {
437  button = createButton(toolbar, "folder-download-symbolic", _("Download document"), G_CALLBACK(downloadDocument));
438  toolItem = GTK_WIDGET(gtk_tool_item_new());
439  gtk_container_add(GTK_CONTAINER(toolItem), button);
440  gtk_widget_show(button);
441  if (rtl)
442  gtk_widget_set_margin_left(toolItem, 6);
443  else
444  gtk_widget_set_margin_right(toolItem, 6);
445 
446  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
447  gtk_widget_show(toolItem);
448  }
449 
450  button = createButton(toolbar, "printer-symbolic", _("Print document"), G_CALLBACK(printDocument));
451  toolItem = GTK_WIDGET(gtk_tool_item_new());
452  gtk_container_add(GTK_CONTAINER(toolItem), button);
453  gtk_widget_show(button);
454 
455  gtk_container_add(GTK_CONTAINER(toolbar), toolItem);
456  gtk_widget_show(toolItem);
457 }
458 
459 static void evBrowserPluginToolbarDispose(GObject *object)
460 {
462  if (toolbar->priv->searchPopover) {
463  gtk_widget_destroy(toolbar->priv->searchPopover);
464  toolbar->priv->searchPopover = nullptr;
465  }
466 
467  G_OBJECT_CLASS(ev_browser_plugin_toolbar_parent_class)->dispose(object);
468 }
469 
471 {
472  GObjectClass *gObjectClass = G_OBJECT_CLASS(klass);
473  gObjectClass->set_property = evBrowserPluginToolbarSetProperty;
474  gObjectClass->constructed = evBrowserPluginToolbarConstructed;
475  gObjectClass->dispose = evBrowserPluginToolbarDispose;
476 
477  g_object_class_install_property(gObjectClass,
478  PROP_PLUGIN,
479  g_param_spec_pointer("plugin",
480  "Plugin",
481  "The plugin",
482  static_cast<GParamFlags>(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
483 
484  g_type_class_add_private(gObjectClass, sizeof(EvBrowserPluginToolbarPrivate));
485 }
486 
488 {
489  toolbar->priv = G_TYPE_INSTANCE_GET_PRIVATE(toolbar, EV_TYPE_BROWSER_PLUGIN_TOOLBAR, EvBrowserPluginToolbarPrivate);
490 }
491 
493 {
494  return GTK_WIDGET(g_object_new(EV_TYPE_BROWSER_PLUGIN_TOOLBAR, "plugin", plugin, nullptr));
495 }