1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * hlink.c: hyperlink support
6 * Copyright (C) 2000-2005 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
27 #include "hlink-impl.h"
28 #include "command-context.h"
29 #include "workbook-control.h"
30 #include "workbook-view.h"
31 #include "selection.h"
33 #include "sheet-view.h"
34 #include "sheet-style.h"
37 #include "expr-name.h"
42 #include <goffice/goffice.h>
43 #include <gsf/gsf-impl-utils.h>
45 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GNM_HLINK_TYPE, GnmHLinkClass)
47 static GObjectClass
*gnm_hlink_parent_class
;
50 * WARNING WARNING WARNING
52 * The type names are used in the xml persistence DO NOT CHANGE THEM
57 * @wbcg: the wbcg that activated the link
59 * Returns: %TRUE if the link successfully activated.
62 gnm_hlink_activate (GnmHLink
*lnk
, WBCGtk
*wbcg
)
64 g_return_val_if_fail (GNM_IS_HLINK (lnk
), FALSE
);
66 return GET_CLASS (lnk
)->Activate (lnk
, wbcg
);
70 * gnm_sheet_hlink_find:
74 * Returns: (transfer none) (nullable): the found #GnmHLink.
77 gnm_sheet_hlink_find (Sheet
const *sheet
, GnmCellPos
const *pos
)
79 GnmStyle
const *style
= sheet_style_get (sheet
, pos
->col
, pos
->row
);
80 return gnm_style_get_hlink (style
);
84 gnm_hlink_finalize (GObject
*obj
)
86 GnmHLink
*lnk
= (GnmHLink
*)obj
;
94 gnm_hlink_parent_class
->finalize (obj
);
98 gnm_hlink_base_set_sheet (GnmHLink
*lnk
, Sheet
*sheet
)
104 gnm_hlink_base_set_target (GnmHLink
*lnk
, gchar
const *target
)
106 gchar
*tmp
= g_strdup (target
);
107 g_free (lnk
->target
);
112 gnm_hlink_base_get_target (GnmHLink
const *lnk
)
118 gnm_hlink_class_init (GObjectClass
*object_class
)
120 GnmHLinkClass
*hlink_class
= (GnmHLinkClass
*)object_class
;
122 gnm_hlink_parent_class
= g_type_class_peek_parent (object_class
);
124 object_class
->finalize
= gnm_hlink_finalize
;
125 hlink_class
->set_sheet
= gnm_hlink_base_set_sheet
;
126 hlink_class
->set_target
= gnm_hlink_base_set_target
;
127 hlink_class
->get_target
= gnm_hlink_base_get_target
;
131 gnm_hlink_init (GObject
*obj
)
133 GnmHLink
*lnk
= (GnmHLink
* )obj
;
138 GSF_CLASS_ABSTRACT (GnmHLink
, gnm_hlink
,
139 gnm_hlink_class_init
, gnm_hlink_init
, G_TYPE_OBJECT
)
142 * gnm_hlink_get_target:
145 * Returns: (transfer none): @lnk's target.
148 gnm_hlink_get_target (GnmHLink
const *lnk
)
150 g_return_val_if_fail (GNM_IS_HLINK (lnk
), NULL
);
152 return GET_CLASS (lnk
)->get_target (lnk
);
156 gnm_hlink_set_target (GnmHLink
*lnk
, gchar
const *target
)
158 g_return_if_fail (GNM_IS_HLINK (lnk
));
160 GET_CLASS (lnk
)->set_target (lnk
, target
);
167 * Returns: (transfer none): @lnk's tooltip.
170 gnm_hlink_get_tip (GnmHLink
const *lnk
)
172 g_return_val_if_fail (GNM_IS_HLINK (lnk
), NULL
);
177 gnm_hlink_set_tip (GnmHLink
*lnk
, gchar
const *tip
)
181 g_return_if_fail (GNM_IS_HLINK (lnk
));
183 tmp
= g_strdup (tip
);
189 * gnm_hlink_get_sheet:
192 * Returns: (transfer none): the sheet
195 gnm_hlink_get_sheet (GnmHLink
*lnk
)
197 g_return_val_if_fail (GNM_IS_HLINK (lnk
), NULL
);
202 gnm_hlink_set_sheet (GnmHLink
*lnk
, Sheet
*sheet
)
204 g_return_if_fail (GNM_IS_HLINK (lnk
));
205 GET_CLASS (lnk
)->set_sheet (lnk
, sheet
);
209 gnm_hlink_new (GType typ
, Sheet
*sheet
)
213 g_return_val_if_fail (typ
!= 0, NULL
);
214 g_return_val_if_fail (g_type_is_a (typ
, GNM_HLINK_TYPE
), NULL
);
215 g_return_val_if_fail (!G_TYPE_IS_ABSTRACT (typ
), NULL
);
216 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
218 lnk
= g_object_new (typ
, NULL
);
219 gnm_hlink_set_sheet (lnk
, sheet
);
225 * @lnk: Existing link
227 * Returns: (transfer full): A duplicate link.
230 gnm_hlink_dup (GnmHLink
*lnk
)
232 GnmHLink
*new_lnk
= g_object_new (G_OBJECT_TYPE (lnk
), NULL
);
234 gnm_hlink_set_sheet (new_lnk
, lnk
->sheet
);
235 gnm_hlink_set_target (new_lnk
, gnm_hlink_get_target (lnk
));
236 gnm_hlink_set_tip (new_lnk
, lnk
->tip
);
245 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
247 * Returns: %TRUE, if links are equal
250 gnm_hlink_equal (GnmHLink
const *a
, GnmHLink
const *b
, gboolean relax_sheet
)
252 g_return_val_if_fail (GNM_IS_HLINK (a
), FALSE
);
253 g_return_val_if_fail (GNM_IS_HLINK (b
), FALSE
);
258 if (!relax_sheet
&& a
->sheet
!= b
->sheet
)
261 return (g_strcmp0 (a
->target
, b
->target
) == 0 &&
262 g_strcmp0 (a
->tip
, b
->tip
) == 0);
265 /***************************************************************************/
266 /* Link to named regions within the current workbook */
267 typedef struct { GnmHLinkClass hlink
; } GnmHLinkCurWBClass
;
273 #define GNM_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), gnm_hlink_cur_wb_get_type (), GnmHLinkCurWB))
275 #define GNM_IS_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), gnm_hlink_cur_wb_get_type ()))
278 static GObjectClass
*gnm_hlink_cur_wb_parent_class
;
281 gnm_hlink_cur_wb_activate (GnmHLink
*lnk
, WBCGtk
*wbcg
)
283 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
287 if (!gnm_hlink_get_range_target (lnk
, &sr
)) {
288 go_cmd_context_error_invalid
289 (GO_CMD_CONTEXT (wbcg
),
291 lnk
->target
? lnk
->target
: "-");
295 sv
= sheet_get_view (sr
.sheet
, wb_control_view (wbc
));
296 sv_selection_set (sv
, &sr
.range
.start
,
297 sr
.range
.start
.col
, sr
.range
.start
.row
,
298 sr
.range
.end
.col
, sr
.range
.end
.row
);
299 gnm_sheet_view_make_cell_visible (sv
, sr
.range
.start
.col
, sr
.range
.start
.row
, FALSE
);
300 if (wbcg_cur_sheet (wbcg
) != sr
.sheet
)
301 wb_view_sheet_focus (wb_control_view (wbc
), sr
.sheet
);
307 gnm_hlink_cur_wb_set_sheet (GnmHLink
*lnk
, Sheet
*sheet
)
309 GnmHLinkCurWB
*hlcwb
= (GnmHLinkCurWB
*)lnk
;
310 ((GnmHLinkClass
*)gnm_hlink_cur_wb_parent_class
)
311 ->set_sheet (lnk
, sheet
);
312 dependent_managed_set_sheet (&hlcwb
->dep
, sheet
);
316 gnm_hlink_cur_wb_set_target (GnmHLink
*lnk
, const char *target
)
318 GnmHLinkCurWB
*hlcwb
= (GnmHLinkCurWB
*)lnk
;
319 GnmExprTop
const *texpr
= NULL
;
321 ((GnmHLinkClass
*)gnm_hlink_cur_wb_parent_class
)
322 ->set_target (lnk
, NULL
);
324 if (target
&& lnk
->sheet
) {
326 GnmExprParseFlags flags
= GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_INVALID
;
327 GnmConventions
const *convs
= lnk
->sheet
->convs
;
329 parse_pos_init_sheet (&pp
, lnk
->sheet
);
330 texpr
= gnm_expr_parse_str (target
, &pp
, flags
, convs
, NULL
);
332 if (texpr
== NULL
|| gnm_expr_top_is_err (texpr
, GNM_ERROR_REF
)) {
334 } else if (gnm_expr_get_name (texpr
->expr
)) {
335 // Nothing, we're good
337 // Allow only ranges and normalize
338 GnmValue
*v
= gnm_expr_top_get_range (texpr
);
339 gnm_expr_top_unref (texpr
);
340 texpr
= v
? gnm_expr_top_new_constant (v
) : NULL
;
344 dependent_managed_set_expr (&hlcwb
->dep
, texpr
);
346 gnm_expr_top_unref (texpr
);
350 gnm_hlink_cur_wb_get_target (GnmHLink
const *lnk
)
352 GnmHLinkCurWB
*hlcwb
= (GnmHLinkCurWB
*)lnk
;
353 GnmExprTop
const *texpr
= hlcwb
->dep
.texpr
;
355 Sheet
*sheet
= lnk
->sheet
;
357 if (texpr
&& sheet
) {
358 GnmConventions
const *convs
= sheet_get_conventions (sheet
);
360 parse_pos_init_sheet (&pp
, sheet
);
361 tgt
= gnm_expr_top_as_string (texpr
, &pp
, convs
);
364 // Use parent class for storage. Ick!
365 ((GnmHLinkClass
*)gnm_hlink_cur_wb_parent_class
)
366 ->set_target ((GnmHLink
*)lnk
, tgt
);
369 return ((GnmHLinkClass
*)gnm_hlink_cur_wb_parent_class
)
374 gnm_hlink_cur_wb_init (GObject
*obj
)
376 GnmHLinkCurWB
*hlcwb
= (GnmHLinkCurWB
*)obj
;
377 dependent_managed_init (&hlcwb
->dep
, NULL
);
381 gnm_hlink_cur_wb_finalize (GObject
*obj
)
383 GnmHLinkCurWB
*hlcwb
= (GnmHLinkCurWB
*)obj
;
385 dependent_managed_set_expr (&hlcwb
->dep
, NULL
);
387 gnm_hlink_cur_wb_parent_class
->finalize (obj
);
391 gnm_hlink_cur_wb_class_init (GObjectClass
*object_class
)
393 GnmHLinkClass
*hlink_class
= (GnmHLinkClass
*) object_class
;
395 gnm_hlink_cur_wb_parent_class
= g_type_class_peek_parent (object_class
);
397 object_class
->finalize
= gnm_hlink_cur_wb_finalize
;
398 hlink_class
->Activate
= gnm_hlink_cur_wb_activate
;
399 hlink_class
->set_sheet
= gnm_hlink_cur_wb_set_sheet
;
400 hlink_class
->set_target
= gnm_hlink_cur_wb_set_target
;
401 hlink_class
->get_target
= gnm_hlink_cur_wb_get_target
;
404 GSF_CLASS (GnmHLinkCurWB
, gnm_hlink_cur_wb
,
405 gnm_hlink_cur_wb_class_init
, gnm_hlink_cur_wb_init
,
413 * gnm_hlink_get_range_target:
414 * @lnk: the hyperlink to query
415 * @sr: (out): location to store link target range
417 * This function determines the location that a link points to. It will
420 * Returns: %TRUE, if the link refers to a range.
423 gnm_hlink_get_range_target (GnmHLink
const *lnk
, GnmSheetRange
*sr
)
425 GnmHLinkCurWB
*hlcwb
;
426 GnmExprTop
const *texpr
;
428 GnmRangeRef
const *r
;
430 Sheet
*start_sheet
, *end_sheet
;
432 memset (sr
, 0, sizeof (*sr
));
434 g_return_val_if_fail (GNM_IS_HLINK (lnk
), FALSE
);
436 if (!GNM_IS_HLINK_CUR_WB (lnk
))
439 hlcwb
= (GnmHLinkCurWB
*)lnk
;
440 texpr
= hlcwb
->dep
.texpr
;
443 vr
= gnm_expr_top_get_range (texpr
);
446 r
= value_get_rangeref (vr
);
448 parse_pos_init_sheet (&pp
, lnk
->sheet
);
449 gnm_rangeref_normalize_pp (r
, &pp
, &start_sheet
, &end_sheet
,
451 sr
->sheet
= start_sheet
;
459 * gnm_hlink_get_target_expr:
460 * @lnk: the hyperlink to query
462 * This function determines the location that a link points to.
464 * Returns: (transfer none) (nullable): A #GnmExprTop describing the target.
467 gnm_hlink_get_target_expr (GnmHLink
const *lnk
)
470 GnmHLinkCurWB
*hlcwb
;
472 g_return_val_if_fail (GNM_IS_HLINK (lnk
), NULL
);
474 if (!GNM_IS_HLINK_CUR_WB (lnk
))
477 hlcwb
= (GnmHLinkCurWB
*)lnk
;
478 return hlcwb
->dep
.texpr
;
483 /***************************************************************************/
484 /* Link to arbitrary urls */
485 typedef struct { GnmHLinkClass hlink
; } GnmHLinkURLClass
;
491 gnm_hlink_url_activate (GnmHLink
*lnk
, WBCGtk
*wbcg
)
496 if (lnk
->target
== NULL
)
499 screen
= gtk_window_get_screen (wbcg_toplevel (wbcg
));
500 err
= go_gtk_url_show (lnk
->target
, screen
);
503 char *msg
= g_strdup_printf (_("Unable to activate the url '%s'"), lnk
->target
);
504 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg
),
514 gnm_hlink_url_class_init (GObjectClass
*object_class
)
516 GnmHLinkClass
*hlink_class
= (GnmHLinkClass
*) object_class
;
518 hlink_class
->Activate
= gnm_hlink_url_activate
;
521 GSF_CLASS (GnmHLinkURL
, gnm_hlink_url
,
522 gnm_hlink_url_class_init
, NULL
,
525 /***************************************************************************/
526 /* email is just a url, but it is cleaner to stick it in a distinct type */
527 typedef struct { GnmHLinkURLClass hlink
; } GnmHLinkEMailClass
;
532 GSF_CLASS (GnmHLinkEMail
, gnm_hlink_email
,
534 gnm_hlink_url_get_type ())
536 /***************************************************************************/
537 /* Link to arbitrary urls */
538 typedef struct { GnmHLinkClass hlink
; } GnmHLinkExternalClass
;
544 gnm_hlink_external_activate (GnmHLink
*lnk
, WBCGtk
*wbcg
)
547 gboolean res
= FALSE
;
551 if (lnk
->target
== NULL
)
554 cmd
= go_shell_arg_to_uri (lnk
->target
);
555 screen
= gtk_window_get_screen (wbcg_toplevel (wbcg
));
556 err
= go_gtk_url_show (cmd
, screen
);
560 char *msg
= g_strdup_printf(_("Unable to open '%s'"), lnk
->target
);
561 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg
),
571 gnm_hlink_external_class_init (GObjectClass
*object_class
)
573 GnmHLinkClass
*hlink_class
= (GnmHLinkClass
*) object_class
;
575 hlink_class
->Activate
= gnm_hlink_external_activate
;
578 GSF_CLASS (GnmHLinkExternal
, gnm_hlink_external
,
579 gnm_hlink_external_class_init
, NULL
,
583 * _gnm_hlink_init: (skip)
586 _gnm_hlink_init (void)
588 /* make sure that all hlink types are registered */
589 gnm_hlink_cur_wb_get_type ();
590 gnm_hlink_url_get_type ();
591 gnm_hlink_email_get_type ();
592 gnm_hlink_external_get_type ();