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
ev-link-accessible.c
Go to the documentation of this file.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2013 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <config.h>
22 
23 #include "ev-link-accessible.h"
24 #include "ev-view-private.h"
25 
26 typedef struct _EvHyperlink EvHyperlink;
28 
33 
35 
36  gchar *name;
38  gint end_index;
39 };
40 
41 struct _EvHyperlink {
42  AtkHyperlink parent;
43 
45 };
46 
48  AtkHyperlinkClass parent_class;
49 };
50 
51 #define EV_TYPE_HYPERLINK (ev_hyperlink_get_type ())
52 #define EV_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_HYPERLINK, EvHyperlink))
53 
54 static GType ev_hyperlink_get_type (void);
55 
56 G_DEFINE_TYPE (EvHyperlink, ev_hyperlink, ATK_TYPE_HYPERLINK)
57 
58 static gchar *
59 ev_hyperlink_get_uri (AtkHyperlink *atk_hyperlink,
60  gint i)
61 {
62  EvHyperlink *hyperlink = EV_HYPERLINK (atk_hyperlink);
63  EvLinkAccessiblePrivate *impl_priv;
64  EvLinkAction *action;
65 
66  if (!hyperlink->link_impl)
67  return NULL;
68 
69  impl_priv = hyperlink->link_impl->priv;
70  action = ev_link_get_action (impl_priv->link);
71 
72  return action ? g_strdup (ev_link_action_get_uri (action)) : NULL;
73 }
74 
75 static gint
76 ev_hyperlink_get_n_anchors (AtkHyperlink *atk_hyperlink)
77 {
78  return 1;
79 }
80 
81 static gboolean
82 ev_hyperlink_is_valid (AtkHyperlink *atk_hyperlink)
83 {
84  return TRUE;
85 }
86 
87 static AtkObject *
88 ev_hyperlink_get_object (AtkHyperlink *atk_hyperlink,
89  gint i)
90 {
91  EvHyperlink *hyperlink = EV_HYPERLINK (atk_hyperlink);
92 
93  return hyperlink->link_impl ? ATK_OBJECT (hyperlink->link_impl) : NULL;
94 }
95 
96 static gint
97 ev_hyperlink_get_start_index (AtkHyperlink *atk_hyperlink)
98 {
99  EvHyperlink *hyperlink = EV_HYPERLINK (atk_hyperlink);
100  EvLinkAccessiblePrivate *impl_priv;
101  EvView *view;
102  EvRectangle *areas = NULL;
103  guint n_areas = 0;
104  guint i;
105 
106  if (!hyperlink->link_impl)
107  return -1;
108 
109  impl_priv = hyperlink->link_impl->priv;
110  if (impl_priv->start_index != -1)
111  return impl_priv->start_index;
112 
113  view = ev_page_accessible_get_view (impl_priv->page);
114  if (!view->page_cache)
115  return -1;
116 
118  ev_page_accessible_get_page (impl_priv->page),
119  &areas, &n_areas);
120  if (!areas)
121  return -1;
122 
123  for (i = 0; i < n_areas; i++) {
124  EvRectangle *rect = areas + i;
125  gdouble c_x, c_y;
126 
127  c_x = rect->x1 + (rect->x2 - rect->x1) / 2.;
128  c_y = rect->y1 + (rect->y2 - rect->y1) / 2.;
129  if (c_x >= impl_priv->area.x1 && c_x <= impl_priv->area.x2 &&
130  c_y >= impl_priv->area.y1 && c_y <= impl_priv->area.y2) {
131  impl_priv->start_index = i;
132  return i;
133  }
134  }
135 
136  return -1;
137 }
138 
139 static gint
140 ev_hyperlink_get_end_index (AtkHyperlink *atk_hyperlink)
141 {
142  EvHyperlink *hyperlink = EV_HYPERLINK (atk_hyperlink);
143  EvLinkAccessiblePrivate *impl_priv;
144  EvView *view;
145  EvRectangle *areas = NULL;
146  guint n_areas = 0;
147  guint i;
148  gint start_index;
149 
150  if (!hyperlink->link_impl)
151  return -1;
152 
153  impl_priv = hyperlink->link_impl->priv;
154  if (impl_priv->end_index != -1)
155  return impl_priv->end_index;
156 
157  start_index = ev_hyperlink_get_start_index (atk_hyperlink);
158  if (start_index == -1)
159  return -1;
160 
161  view = ev_page_accessible_get_view (impl_priv->page);
162  if (!view->page_cache)
163  return -1;
164 
166  ev_page_accessible_get_page (impl_priv->page),
167  &areas, &n_areas);
168  if (!areas)
169  return -1;
170 
171  for (i = start_index + 1; i < n_areas; i++) {
172  EvRectangle *rect = areas + i;
173  gdouble c_x, c_y;
174 
175  c_x = rect->x1 + (rect->x2 - rect->x1) / 2.;
176  c_y = rect->y1 + (rect->y2 - rect->y1) / 2.;
177  if (c_x < impl_priv->area.x1 || c_x > impl_priv->area.x2 ||
178  c_y < impl_priv->area.y1 || c_y > impl_priv->area.y2) {
179  impl_priv->end_index = i;
180  return i;
181  }
182  }
183 
184  return -1;
185 }
186 
187 static void
189 {
190  AtkHyperlinkClass *atk_link_class = ATK_HYPERLINK_CLASS (klass);
191 
192  atk_link_class->get_uri = ev_hyperlink_get_uri;
193  atk_link_class->get_n_anchors = ev_hyperlink_get_n_anchors;
194  atk_link_class->is_valid = ev_hyperlink_is_valid;
195  atk_link_class->get_object = ev_hyperlink_get_object;
196  atk_link_class->get_start_index = ev_hyperlink_get_start_index;
197  atk_link_class->get_end_index = ev_hyperlink_get_end_index;
198 }
199 
200 static void
202 {
203 }
204 
205 static void ev_link_accessible_hyperlink_impl_iface_init (AtkHyperlinkImplIface *iface);
206 static void ev_link_accessible_action_interface_init (AtkActionIface *iface);
207 static void ev_link_accessible_component_iface_init (AtkComponentIface *iface);
208 
209 G_DEFINE_TYPE_WITH_CODE (EvLinkAccessible, ev_link_accessible, ATK_TYPE_OBJECT,
210  G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERLINK_IMPL, ev_link_accessible_hyperlink_impl_iface_init)
211  G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, ev_link_accessible_action_interface_init)
212  G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, ev_link_accessible_component_iface_init))
213 
214 static const gchar *
215 ev_link_accessible_get_name (AtkObject *atk_object)
216 {
218  gint start_index;
219  gint end_index;
220 
221  priv = EV_LINK_ACCESSIBLE (atk_object)->priv;
222  if (priv->name)
223  return priv->name;
224 
225  start_index = ev_hyperlink_get_start_index (ATK_HYPERLINK (priv->hyperlink));
226  end_index = ev_hyperlink_get_end_index (ATK_HYPERLINK (priv->hyperlink));
227  priv->name = atk_text_get_text (ATK_TEXT (atk_object_get_parent (atk_object)), start_index, end_index);
228 
229  return priv->name;
230 }
231 
232 static AtkObject *
233 ev_link_accessible_get_parent (AtkObject *atk_object)
234 {
235  EvLinkAccessiblePrivate *priv = EV_LINK_ACCESSIBLE (atk_object)->priv;
236 
237  return ATK_OBJECT (priv->page);
238 }
239 
240 static AtkStateSet *
241 ev_link_accessible_ref_state_set (AtkObject *atk_object)
242 {
243  AtkStateSet *state_set;
244  AtkStateSet *copy_set;
245  AtkStateSet *page_accessible_state_set;
246  EvLinkAccessible *self;
247  EvViewAccessible *view_accessible;
248  EvView *view;
249  gint page;
250 
251  self = EV_LINK_ACCESSIBLE (atk_object);
252  state_set = ATK_OBJECT_CLASS (ev_link_accessible_parent_class)->ref_state_set (atk_object);
253  atk_state_set_clear_states (state_set);
254 
255  page_accessible_state_set = atk_object_ref_state_set (ATK_OBJECT (self->priv->page));
256  copy_set = atk_state_set_or_sets (state_set, page_accessible_state_set);
257 
258  view_accessible = ev_page_accessible_get_view_accessible (self->priv->page);
259  page = ev_page_accessible_get_page (self->priv->page);
260  if (!ev_view_accessible_is_doc_rect_showing (view_accessible, page, &self->priv->area))
261  atk_state_set_remove_state (copy_set, ATK_STATE_SHOWING);
262 
263  view = ev_page_accessible_get_view (self->priv->page);
264  if (!view->focused_element || view->focused_element->data != self->priv->link)
265  atk_state_set_remove_state (copy_set, ATK_STATE_FOCUSED);
266 
267  g_object_unref (state_set);
268  g_object_unref (page_accessible_state_set);
269 
270  return copy_set;
271 }
272 
273 static void
275 {
276  EvLinkAccessible *link = EV_LINK_ACCESSIBLE (object);
277 
278  g_clear_object (&link->priv->hyperlink);
279  g_free (link->priv->name);
280 
281  G_OBJECT_CLASS (ev_link_accessible_parent_class)->finalize (object);
282 }
283 
284 static void
286 {
287  GObjectClass *object_class = G_OBJECT_CLASS (klass);
288  AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
289 
290  object_class->finalize = ev_link_accessible_finalize;
291 
292  g_type_class_add_private (klass, sizeof (EvLinkAccessiblePrivate));
293 
294  atk_class->get_parent = ev_link_accessible_get_parent;
295  atk_class->get_name = ev_link_accessible_get_name;
296  atk_class->ref_state_set = ev_link_accessible_ref_state_set;
297 }
298 
299 static void
301 {
302  atk_object_set_role (ATK_OBJECT (link), ATK_ROLE_LINK);
303  link->priv = G_TYPE_INSTANCE_GET_PRIVATE (link, EV_TYPE_LINK_ACCESSIBLE, EvLinkAccessiblePrivate);
304  link->priv->start_index = -1;
305  link->priv->end_index = -1;
306 }
307 
308 static AtkHyperlink *
309 ev_link_accessible_get_hyperlink (AtkHyperlinkImpl *hyperlink_impl)
310 {
311  EvLinkAccessible *link = EV_LINK_ACCESSIBLE (hyperlink_impl);
312 
313  if (link->priv->hyperlink)
314  return ATK_HYPERLINK (link->priv->hyperlink);
315 
316  link->priv->hyperlink = g_object_new (EV_TYPE_HYPERLINK, NULL);
317 
318  link->priv->hyperlink->link_impl = link;
319  g_object_add_weak_pointer (G_OBJECT (link), (gpointer *)&link->priv->hyperlink->link_impl);
320 
321  return ATK_HYPERLINK (link->priv->hyperlink);
322 }
323 
324 static void
325 ev_link_accessible_hyperlink_impl_iface_init (AtkHyperlinkImplIface *iface)
326 {
327  iface->get_hyperlink = ev_link_accessible_get_hyperlink;
328 }
329 
330 static gboolean
331 ev_link_accessible_action_do_action (AtkAction *atk_action,
332  gint i)
333 {
334  EvLinkAccessiblePrivate *priv = EV_LINK_ACCESSIBLE (atk_action)->priv;
335  EvView *view;
336 
337  view = ev_page_accessible_get_view (priv->page);
338  if (!ev_link_get_action (priv->link))
339  return FALSE;
340 
341  ev_view_handle_link (view, priv->link);
342 
343  return TRUE;
344 }
345 
346 static gint
348 {
349  return 1;
350 }
351 
352 static const gchar *
354  gint i)
355 {
356  /* TODO */
357  return NULL;
358 }
359 
360 static const gchar *
361 ev_link_accessible_action_get_name (AtkAction *atk_action,
362  gint i)
363 {
364  return i == 0 ? "activate" : NULL;
365 }
366 
367 static void
369 {
370  iface->do_action = ev_link_accessible_action_do_action;
371  iface->get_n_actions = ev_link_accessible_action_get_n_actions;
372  iface->get_description = ev_link_accessible_action_get_description;
373  iface->get_name = ev_link_accessible_action_get_name;
374 }
375 
376 static void
377 ev_link_accessible_get_extents (AtkComponent *atk_component,
378  gint *x,
379  gint *y,
380  gint *width,
381  gint *height,
382  AtkCoordType coord_type)
383 {
384  EvLinkAccessible *self;
385  EvViewAccessible *view_accessible;
386  gint page;
387  EvRectangle atk_rect;
388 
389  self = EV_LINK_ACCESSIBLE (atk_component);
390  view_accessible = ev_page_accessible_get_view_accessible (self->priv->page);
391  page = ev_page_accessible_get_page (self->priv->page);
392  _transform_doc_rect_to_atk_rect (view_accessible, page, &self->priv->area, &atk_rect, coord_type);
393  *x = atk_rect.x1;
394  *y = atk_rect.y1;
395  *width = atk_rect.x2 - atk_rect.x1;
396  *height = atk_rect.y2 - atk_rect.y1;
397 }
398 
399 static gboolean
400 ev_link_accessible_grab_focus (AtkComponent *atk_component)
401 {
402  EvLinkAccessible *self;
403  EvView *view;
404  EvMappingList *link_mapping;
405  EvMapping *mapping;
406  gint page;
407 
408  self = EV_LINK_ACCESSIBLE (atk_component);
409  view = ev_page_accessible_get_view (self->priv->page);
410  page = ev_page_accessible_get_page (self->priv->page);
411  link_mapping = ev_page_cache_get_link_mapping (view->page_cache, page);
412  mapping = ev_mapping_list_find (link_mapping, self->priv->link);
413  _ev_view_set_focused_element (view, mapping, page);
414 
415  return TRUE;
416 }
417 
418 static void
419 ev_link_accessible_component_iface_init (AtkComponentIface *iface)
420 {
421  iface->get_extents = ev_link_accessible_get_extents;
422  iface->grab_focus = ev_link_accessible_grab_focus;
423 }
424 
427  EvLink *link,
428  EvRectangle *area)
429 {
430  EvLinkAccessible *atk_link;
431 
432  atk_link = g_object_new (EV_TYPE_LINK_ACCESSIBLE, NULL);
433  atk_link->priv->page = page;
434  atk_link->priv->link = g_object_ref (link);
435  atk_link->priv->area = *area;
436 
437  return EV_LINK_ACCESSIBLE (atk_link);
438 }