Update Spanish translation
[gnumeric.git] / src / hlink.c
blob582fa4641a83c5a9429dcbab6de8c05794beaf7f
2 /*
3 * hlink.c: hyperlink support
5 * Copyright (C) 2000-2005 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <hlink.h>
26 #include <hlink-impl.h>
27 #include <command-context.h>
28 #include <workbook-control.h>
29 #include <workbook-view.h>
30 #include <selection.h>
31 #include <sheet.h>
32 #include <sheet-view.h>
33 #include <sheet-style.h>
34 #include <ranges.h>
35 #include <position.h>
36 #include <expr-name.h>
37 #include <expr.h>
38 #include <value.h>
39 #include <mstyle.h>
41 #include <goffice/goffice.h>
42 #include <gsf/gsf-impl-utils.h>
44 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GNM_HLINK_TYPE, GnmHLinkClass)
46 static GObjectClass *gnm_hlink_parent_class;
49 * WARNING WARNING WARNING
51 * The type names are used in the xml persistence DO NOT CHANGE THEM
53 /**
54 * gnm_hlink_activate:
55 * @lnk: #GnmHLink
56 * @wbcg: the wbcg that activated the link
58 * Returns: %TRUE if the link successfully activated.
59 **/
60 gboolean
61 gnm_hlink_activate (GnmHLink *lnk, WBCGtk *wbcg)
63 g_return_val_if_fail (GNM_IS_HLINK (lnk), FALSE);
65 return GET_CLASS (lnk)->Activate (lnk, wbcg);
68 /**
69 * gnm_sheet_hlink_find:
70 * @sheet: #Sheet
71 * @pos: #GcmCellPos
73 * Returns: (transfer none) (nullable): the found #GnmHLink.
74 **/
75 GnmHLink *
76 gnm_sheet_hlink_find (Sheet const *sheet, GnmCellPos const *pos)
78 GnmStyle const *style = sheet_style_get (sheet, pos->col, pos->row);
79 return gnm_style_get_hlink (style);
82 static void
83 gnm_hlink_finalize (GObject *obj)
85 GnmHLink *lnk = (GnmHLink *)obj;
87 g_free (lnk->target);
88 lnk->target = NULL;
90 g_free (lnk->tip);
91 lnk->tip = NULL;
93 gnm_hlink_parent_class->finalize (obj);
96 static void
97 gnm_hlink_base_set_sheet (GnmHLink *lnk, Sheet *sheet)
99 lnk->sheet = sheet;
102 static void
103 gnm_hlink_base_set_target (GnmHLink *lnk, gchar const *target)
105 gchar *tmp = g_strdup (target);
106 g_free (lnk->target);
107 lnk->target = tmp;
110 static const char *
111 gnm_hlink_base_get_target (GnmHLink const *lnk)
113 return lnk->target;
116 static void
117 gnm_hlink_class_init (GObjectClass *object_class)
119 GnmHLinkClass *hlink_class = (GnmHLinkClass *)object_class;
121 gnm_hlink_parent_class = g_type_class_peek_parent (object_class);
123 object_class->finalize = gnm_hlink_finalize;
124 hlink_class->set_sheet = gnm_hlink_base_set_sheet;
125 hlink_class->set_target = gnm_hlink_base_set_target;
126 hlink_class->get_target = gnm_hlink_base_get_target;
129 static void
130 gnm_hlink_init (GObject *obj)
132 GnmHLink *lnk = (GnmHLink * )obj;
133 lnk->target = NULL;
134 lnk->tip = NULL;
137 GSF_CLASS_ABSTRACT (GnmHLink, gnm_hlink,
138 gnm_hlink_class_init, gnm_hlink_init, G_TYPE_OBJECT)
141 * gnm_hlink_get_target:
142 * @lnk: #GnmHLink
144 * Returns: (transfer none): @lnk's target.
146 const char *
147 gnm_hlink_get_target (GnmHLink const *lnk)
149 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
151 return GET_CLASS (lnk)->get_target (lnk);
154 void
155 gnm_hlink_set_target (GnmHLink *lnk, gchar const *target)
157 g_return_if_fail (GNM_IS_HLINK (lnk));
159 GET_CLASS (lnk)->set_target (lnk, target);
163 * gnm_hlink_get_tip:
164 * @lnk: #GnmHLink
166 * Returns: (transfer none): @lnk's tooltip.
168 const char *
169 gnm_hlink_get_tip (GnmHLink const *lnk)
171 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
172 return lnk->tip;
175 void
176 gnm_hlink_set_tip (GnmHLink *lnk, gchar const *tip)
178 gchar *tmp;
180 g_return_if_fail (GNM_IS_HLINK (lnk));
182 tmp = g_strdup (tip);
183 g_free (lnk->tip);
184 lnk->tip = tmp;
188 * gnm_hlink_get_sheet:
189 * @lnk: #GnmHLink
191 * Returns: (transfer none): the sheet
193 Sheet *
194 gnm_hlink_get_sheet (GnmHLink *lnk)
196 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
197 return lnk->sheet;
200 void
201 gnm_hlink_set_sheet (GnmHLink *lnk, Sheet *sheet)
203 g_return_if_fail (GNM_IS_HLINK (lnk));
204 GET_CLASS (lnk)->set_sheet (lnk, sheet);
207 GnmHLink *
208 gnm_hlink_new (GType typ, Sheet *sheet)
210 GnmHLink *lnk;
212 g_return_val_if_fail (typ != 0, NULL);
213 g_return_val_if_fail (g_type_is_a (typ, GNM_HLINK_TYPE), NULL);
214 g_return_val_if_fail (!G_TYPE_IS_ABSTRACT (typ), NULL);
215 g_return_val_if_fail (IS_SHEET (sheet), NULL);
217 lnk = g_object_new (typ, NULL);
218 gnm_hlink_set_sheet (lnk, sheet);
219 return lnk;
223 * gnm_hlink_dup:
224 * @lnk: Existing link
226 * Returns: (transfer full): A duplicate link.
228 GnmHLink *
229 gnm_hlink_dup (GnmHLink *lnk)
231 GnmHLink *new_lnk = g_object_new (G_OBJECT_TYPE (lnk), NULL);
233 gnm_hlink_set_sheet (new_lnk, lnk->sheet);
234 gnm_hlink_set_target (new_lnk, gnm_hlink_get_target (lnk));
235 gnm_hlink_set_tip (new_lnk, lnk->tip);
237 return new_lnk;
241 * gnm_hlink_equal:
242 * @a: a #GnmHLink
243 * @b: a #GnmHLink
244 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
246 * Returns: %TRUE, if links are equal
248 gboolean
249 gnm_hlink_equal (GnmHLink const *a, GnmHLink const *b, gboolean relax_sheet)
251 g_return_val_if_fail (GNM_IS_HLINK (a), FALSE);
252 g_return_val_if_fail (GNM_IS_HLINK (b), FALSE);
254 if (a == b)
255 return TRUE;
257 if (!relax_sheet && a->sheet != b->sheet)
258 return FALSE;
260 return (g_strcmp0 (a->target, b->target) == 0 &&
261 g_strcmp0 (a->tip, b->tip) == 0);
264 /***************************************************************************/
265 /* Link to named regions within the current workbook */
266 typedef struct { GnmHLinkClass hlink; } GnmHLinkCurWBClass;
267 typedef struct {
268 GnmHLink hlink;
270 GnmDependent dep;
271 } GnmHLinkCurWB;
272 #define GNM_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), gnm_hlink_cur_wb_get_type (), GnmHLinkCurWB))
274 #define GNM_IS_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), gnm_hlink_cur_wb_get_type ()))
277 static GObjectClass *gnm_hlink_cur_wb_parent_class;
279 static gboolean
280 gnm_hlink_cur_wb_activate (GnmHLink *lnk, WBCGtk *wbcg)
282 WorkbookControl *wbc = GNM_WBC (wbcg);
283 SheetView *sv;
284 GnmSheetRange sr;
286 if (!gnm_hlink_get_range_target (lnk, &sr)) {
287 go_cmd_context_error_invalid
288 (GO_CMD_CONTEXT (wbcg),
289 _("Link target"),
290 lnk->target ? lnk->target : "-");
291 return FALSE;
294 sv = sheet_get_view (sr.sheet, wb_control_view (wbc));
295 sv_selection_set (sv, &sr.range.start,
296 sr.range.start.col, sr.range.start.row,
297 sr.range.end.col, sr.range.end.row);
298 gnm_sheet_view_make_cell_visible (sv, sr.range.start.col, sr.range.start.row, FALSE);
299 if (wbcg_cur_sheet (wbcg) != sr.sheet)
300 wb_view_sheet_focus (wb_control_view (wbc), sr.sheet);
302 return TRUE;
305 static void
306 gnm_hlink_cur_wb_set_sheet (GnmHLink *lnk, Sheet *sheet)
308 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
309 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
310 ->set_sheet (lnk, sheet);
311 dependent_managed_set_sheet (&hlcwb->dep, sheet);
314 static void
315 gnm_hlink_cur_wb_set_target (GnmHLink *lnk, const char *target)
317 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
318 GnmExprTop const *texpr = NULL;
320 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
321 ->set_target (lnk, NULL);
323 if (target && lnk->sheet) {
324 GnmParsePos pp;
325 GnmExprParseFlags flags = GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_INVALID;
326 GnmConventions const *convs = lnk->sheet->convs;
328 parse_pos_init_sheet (&pp, lnk->sheet);
329 texpr = gnm_expr_parse_str (target, &pp, flags, convs, NULL);
331 if (texpr == NULL || gnm_expr_top_is_err (texpr, GNM_ERROR_REF)) {
332 // Nothing, error
333 } else if (gnm_expr_get_name (texpr->expr)) {
334 // Nothing, we're good
335 } else {
336 // Allow only ranges and normalize
337 GnmValue *v = gnm_expr_top_get_range (texpr);
338 gnm_expr_top_unref (texpr);
339 texpr = v ? gnm_expr_top_new_constant (v) : NULL;
343 dependent_managed_set_expr (&hlcwb->dep, texpr);
344 if (texpr)
345 gnm_expr_top_unref (texpr);
348 static const char *
349 gnm_hlink_cur_wb_get_target (GnmHLink const *lnk)
351 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
352 GnmExprTop const *texpr = hlcwb->dep.texpr;
353 char *tgt = NULL;
354 Sheet *sheet = lnk->sheet;
356 if (texpr && sheet) {
357 GnmConventions const *convs = sheet_get_conventions (sheet);
358 GnmParsePos pp;
359 parse_pos_init_sheet (&pp, sheet);
360 tgt = gnm_expr_top_as_string (texpr, &pp, convs);
363 // Use parent class for storage. Ick!
364 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
365 ->set_target ((GnmHLink *)lnk, tgt);
366 g_free (tgt);
368 return ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
369 ->get_target (lnk);
372 static void
373 gnm_hlink_cur_wb_init (GObject *obj)
375 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)obj;
376 dependent_managed_init (&hlcwb->dep, NULL);
379 static void
380 gnm_hlink_cur_wb_finalize (GObject *obj)
382 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)obj;
384 dependent_managed_set_expr (&hlcwb->dep, NULL);
386 gnm_hlink_cur_wb_parent_class->finalize (obj);
389 static void
390 gnm_hlink_cur_wb_class_init (GObjectClass *object_class)
392 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
394 gnm_hlink_cur_wb_parent_class = g_type_class_peek_parent (object_class);
396 object_class->finalize = gnm_hlink_cur_wb_finalize;
397 hlink_class->Activate = gnm_hlink_cur_wb_activate;
398 hlink_class->set_sheet = gnm_hlink_cur_wb_set_sheet;
399 hlink_class->set_target = gnm_hlink_cur_wb_set_target;
400 hlink_class->get_target = gnm_hlink_cur_wb_get_target;
403 GSF_CLASS (GnmHLinkCurWB, gnm_hlink_cur_wb,
404 gnm_hlink_cur_wb_class_init, gnm_hlink_cur_wb_init,
405 GNM_HLINK_TYPE)
406 #if 0
408 #endif
412 * gnm_hlink_get_range_target:
413 * @lnk: the hyperlink to query
414 * @sr: (out): location to store link target range
416 * This function determines the location that a link points to. It will
417 * resolve names.
419 * Returns: %TRUE, if the link refers to a range.
421 gboolean
422 gnm_hlink_get_range_target (GnmHLink const *lnk, GnmSheetRange *sr)
424 GnmHLinkCurWB *hlcwb;
425 GnmExprTop const *texpr;
426 GnmValue *vr;
427 GnmRangeRef const *r;
428 GnmParsePos pp;
429 Sheet *start_sheet, *end_sheet;
431 memset (sr, 0, sizeof (*sr));
433 g_return_val_if_fail (GNM_IS_HLINK (lnk), FALSE);
435 if (!GNM_IS_HLINK_CUR_WB (lnk))
436 return FALSE;
438 hlcwb = (GnmHLinkCurWB *)lnk;
439 texpr = hlcwb->dep.texpr;
440 if (!texpr)
441 return FALSE;
442 vr = gnm_expr_top_get_range (texpr);
443 if (!vr)
444 return FALSE;
445 r = value_get_rangeref (vr);
447 parse_pos_init_sheet (&pp, lnk->sheet);
448 gnm_rangeref_normalize_pp (r, &pp, &start_sheet, &end_sheet,
449 &sr->range);
450 sr->sheet = start_sheet;
451 value_release (vr);
453 return TRUE;
458 * gnm_hlink_get_target_expr:
459 * @lnk: the hyperlink to query
461 * This function determines the location that a link points to.
463 * Returns: (transfer none) (nullable): A #GnmExprTop describing the target.
465 GnmExprTop const *
466 gnm_hlink_get_target_expr (GnmHLink const *lnk)
469 GnmHLinkCurWB *hlcwb;
471 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
473 if (!GNM_IS_HLINK_CUR_WB (lnk))
474 return NULL;
476 hlcwb = (GnmHLinkCurWB *)lnk;
477 return hlcwb->dep.texpr;
482 /***************************************************************************/
483 /* Link to arbitrary urls */
484 typedef struct { GnmHLinkClass hlink; } GnmHLinkURLClass;
485 typedef struct {
486 GnmHLink hlink;
487 } GnmHLinkURL;
489 static gboolean
490 gnm_hlink_url_activate (GnmHLink *lnk, WBCGtk *wbcg)
492 GError *err = NULL;
493 GdkScreen *screen;
495 if (lnk->target == NULL)
496 return FALSE;
498 screen = gtk_window_get_screen (wbcg_toplevel (wbcg));
499 err = go_gtk_url_show (lnk->target, screen);
501 if (err != NULL) {
502 char *msg = g_strdup_printf (_("Unable to activate the url '%s'"), lnk->target);
503 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
504 msg, err->message);
505 g_free (msg);
506 g_error_free (err);
509 return err == NULL;
512 static void
513 gnm_hlink_url_class_init (GObjectClass *object_class)
515 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
517 hlink_class->Activate = gnm_hlink_url_activate;
520 GSF_CLASS (GnmHLinkURL, gnm_hlink_url,
521 gnm_hlink_url_class_init, NULL,
522 GNM_HLINK_TYPE)
524 /***************************************************************************/
525 /* email is just a url, but it is cleaner to stick it in a distinct type */
526 typedef struct { GnmHLinkURLClass hlink; } GnmHLinkEMailClass;
527 typedef struct {
528 GnmHLinkURL hlink;
529 } GnmHLinkEMail;
531 GSF_CLASS (GnmHLinkEMail, gnm_hlink_email,
532 NULL, NULL,
533 gnm_hlink_url_get_type ())
535 /***************************************************************************/
536 /* Link to arbitrary urls */
537 typedef struct { GnmHLinkClass hlink; } GnmHLinkExternalClass;
538 typedef struct {
539 GnmHLink hlink;
540 } GnmHLinkExternal;
542 static gboolean
543 gnm_hlink_external_activate (GnmHLink *lnk, WBCGtk *wbcg)
545 GError *err = NULL;
546 gboolean res = FALSE;
547 char *cmd;
548 GdkScreen *screen;
550 if (lnk->target == NULL)
551 return FALSE;
553 cmd = go_shell_arg_to_uri (lnk->target);
554 screen = gtk_window_get_screen (wbcg_toplevel (wbcg));
555 err = go_gtk_url_show (cmd, screen);
556 g_free (cmd);
558 if (err != NULL) {
559 char *msg = g_strdup_printf(_("Unable to open '%s'"), lnk->target);
560 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
561 msg, err->message);
562 g_free (msg);
563 g_error_free (err);
566 return res;
569 static void
570 gnm_hlink_external_class_init (GObjectClass *object_class)
572 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
574 hlink_class->Activate = gnm_hlink_external_activate;
577 GSF_CLASS (GnmHLinkExternal, gnm_hlink_external,
578 gnm_hlink_external_class_init, NULL,
579 GNM_HLINK_TYPE)
582 * gnm_hlink_init_: (skip)
584 void
585 gnm_hlink_init_ (void)
587 /* make sure that all hlink types are registered */
588 gnm_hlink_cur_wb_get_type ();
589 gnm_hlink_url_get_type ();
590 gnm_hlink_email_get_type ();
591 gnm_hlink_external_get_type ();