Compilation: NULL/FALSE mixup.
[gnumeric.git] / src / hlink.c
blob01fd96d1281644895e6b39271db6ff5348550665
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
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
21 * USA
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include "gnumeric.h"
26 #include "hlink.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"
32 #include "sheet.h"
33 #include "sheet-view.h"
34 #include "sheet-style.h"
35 #include "ranges.h"
36 #include "position.h"
37 #include "expr-name.h"
38 #include "expr.h"
39 #include "expr-impl.h"
40 #include "value.h"
41 #include "mstyle.h"
43 #include <goffice/goffice.h>
44 #include <gsf/gsf-impl-utils.h>
46 #define GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS (instance, GNM_HLINK_TYPE, GnmHLinkClass)
48 static GObjectClass *gnm_hlink_parent_class;
51 * WARNING WARNING WARNING
53 * The type names are used in the xml persistence DO NOT CHANGE THEM
55 /**
56 * gnm_hlink_activate:
57 * @lnk:
58 * @wbcg: the wbcg that activated the link
60 * Returns: TRUE if the link successfully activated.
61 **/
62 gboolean
63 gnm_hlink_activate (GnmHLink *lnk, WBCGtk *wbcg)
65 g_return_val_if_fail (GNM_IS_HLINK (lnk), FALSE);
67 return GET_CLASS (lnk)->Activate (lnk, wbcg);
70 /**
71 * gnm_sheet_hlink_find:
72 * @sheet: #Sheet
73 * @pos: #GcmCellPos
75 * Returns: (transfer none): the found #GnmHLink.
76 **/
77 GnmHLink *
78 gnm_sheet_hlink_find (Sheet const *sheet, GnmCellPos const *pos)
80 GnmStyle const *style = sheet_style_get (sheet, pos->col, pos->row);
81 return gnm_style_get_hlink (style);
84 static void
85 gnm_hlink_finalize (GObject *obj)
87 GnmHLink *lnk = (GnmHLink *)obj;
89 g_free (lnk->target);
90 lnk->target = NULL;
92 g_free (lnk->tip);
93 lnk->tip = NULL;
95 gnm_hlink_parent_class->finalize (obj);
98 static void
99 gnm_hlink_base_set_sheet (GnmHLink *lnk, Sheet *sheet)
101 lnk->sheet = sheet;
104 static void
105 gnm_hlink_base_set_target (GnmHLink *lnk, gchar const *target)
107 gchar *tmp = g_strdup (target);
108 g_free (lnk->target);
109 lnk->target = tmp;
112 static const char *
113 gnm_hlink_base_get_target (GnmHLink const *lnk)
115 return lnk->target;
118 static void
119 gnm_hlink_class_init (GObjectClass *object_class)
121 GnmHLinkClass *hlink_class = (GnmHLinkClass *)object_class;
123 gnm_hlink_parent_class = g_type_class_peek_parent (object_class);
125 object_class->finalize = gnm_hlink_finalize;
126 hlink_class->set_sheet = gnm_hlink_base_set_sheet;
127 hlink_class->set_target = gnm_hlink_base_set_target;
128 hlink_class->get_target = gnm_hlink_base_get_target;
131 static void
132 gnm_hlink_init (GObject *obj)
134 GnmHLink *lnk = (GnmHLink * )obj;
135 lnk->target = NULL;
136 lnk->tip = NULL;
139 GSF_CLASS_ABSTRACT (GnmHLink, gnm_hlink,
140 gnm_hlink_class_init, gnm_hlink_init, G_TYPE_OBJECT)
142 const char *
143 gnm_hlink_get_target (GnmHLink const *lnk)
145 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
147 return GET_CLASS (lnk)->get_target (lnk);
150 void
151 gnm_hlink_set_target (GnmHLink *lnk, gchar const *target)
153 g_return_if_fail (GNM_IS_HLINK (lnk));
155 GET_CLASS (lnk)->set_target (lnk, target);
158 const char *
159 gnm_hlink_get_tip (GnmHLink const *lnk)
161 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
162 return lnk->tip;
165 void
166 gnm_hlink_set_tip (GnmHLink *lnk, gchar const *tip)
168 gchar *tmp;
170 g_return_if_fail (GNM_IS_HLINK (lnk));
172 tmp = g_strdup (tip);
173 g_free (lnk->tip);
174 lnk->tip = tmp;
178 * gnm_hlink_get_sheet:
179 * @lnk: link
181 * Returns: (transfer none): the sheet
183 Sheet *
184 gnm_hlink_get_sheet (GnmHLink *lnk)
186 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
187 return lnk->sheet;
190 void
191 gnm_hlink_set_sheet (GnmHLink *lnk, Sheet *sheet)
193 g_return_if_fail (GNM_IS_HLINK (lnk));
194 GET_CLASS (lnk)->set_sheet (lnk, sheet);
197 GnmHLink *
198 gnm_hlink_new (GType typ, Sheet *sheet)
200 GnmHLink *lnk;
202 g_return_val_if_fail (typ != 0, NULL);
203 g_return_val_if_fail (g_type_is_a (typ, GNM_HLINK_TYPE), NULL);
204 g_return_val_if_fail (!G_TYPE_IS_ABSTRACT (typ), NULL);
205 g_return_val_if_fail (IS_SHEET (sheet), NULL);
207 lnk = g_object_new (typ, NULL);
208 gnm_hlink_set_sheet (lnk, sheet);
209 return lnk;
213 * gnm_hlink_dup:
214 * @lnk: Existing link
216 * Returns: (transfer full): A duplicate link.
218 GnmHLink *
219 gnm_hlink_dup (GnmHLink *lnk)
221 GnmHLink *new_lnk = g_object_new (G_OBJECT_TYPE (lnk), NULL);
223 gnm_hlink_set_sheet (new_lnk, lnk->sheet);
224 gnm_hlink_set_target (new_lnk, gnm_hlink_get_target (lnk));
225 gnm_hlink_set_tip (new_lnk, lnk->tip);
227 return new_lnk;
231 * gnm_hlink_equal:
232 * @a: a #GnmHLink
233 * @b: a #GnmHLink
234 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
236 * Returns: %TRUE, if links are equal
238 gboolean
239 gnm_hlink_equal (GnmHLink const *a, GnmHLink const *b, gboolean relax_sheet)
241 g_return_val_if_fail (GNM_IS_HLINK (a), FALSE);
242 g_return_val_if_fail (GNM_IS_HLINK (b), FALSE);
244 if (a == b)
245 return TRUE;
247 if (!relax_sheet && a->sheet != b->sheet)
248 return FALSE;
250 return (g_strcmp0 (a->target, b->target) == 0 &&
251 g_strcmp0 (a->tip, b->tip) == 0);
254 /***************************************************************************/
255 /* Link to named regions within the current workbook */
256 typedef struct { GnmHLinkClass hlink; } GnmHLinkCurWBClass;
257 typedef struct {
258 GnmHLink hlink;
260 GnmDependent dep;
261 } GnmHLinkCurWB;
262 #define GNM_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), gnm_hlink_cur_wb_get_type (), GnmHLinkCurWB))
264 #define GNM_IS_HLINK_CUR_WB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), gnm_hlink_cur_wb_get_type ()))
267 static GObjectClass *gnm_hlink_cur_wb_parent_class;
269 static gboolean
270 gnm_hlink_cur_wb_activate (GnmHLink *lnk, WBCGtk *wbcg)
272 WorkbookControl *wbc = GNM_WBC (wbcg);
273 SheetView *sv;
274 GnmSheetRange sr;
276 if (!gnm_hlink_get_range_target (lnk, &sr)) {
277 go_cmd_context_error_invalid
278 (GO_CMD_CONTEXT (wbcg),
279 _("Link target"),
280 lnk->target ? lnk->target : "-");
281 return FALSE;
284 sv = sheet_get_view (sr.sheet, wb_control_view (wbc));
285 sv_selection_set (sv, &sr.range.start,
286 sr.range.start.col, sr.range.start.row,
287 sr.range.end.col, sr.range.end.row);
288 sv_make_cell_visible (sv, sr.range.start.col, sr.range.start.row, FALSE);
289 if (wbcg_cur_sheet (wbcg) != sr.sheet)
290 wb_view_sheet_focus (wb_control_view (wbc), sr.sheet);
292 return TRUE;
295 static void
296 gnm_hlink_cur_wb_set_sheet (GnmHLink *lnk, Sheet *sheet)
298 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
299 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
300 ->set_sheet (lnk, sheet);
301 dependent_managed_set_sheet (&hlcwb->dep, sheet);
304 static void
305 gnm_hlink_cur_wb_set_target (GnmHLink *lnk, const char *target)
307 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
308 GnmExprTop const *texpr = NULL;
310 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
311 ->set_target (lnk, NULL);
313 if (target && lnk->sheet) {
314 GnmParsePos pp;
315 GnmExprParseFlags flags = GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_INVALID;
316 GnmConventions const *convs = lnk->sheet->convs;
318 parse_pos_init_sheet (&pp, lnk->sheet);
319 texpr = gnm_expr_parse_str (target, &pp, flags, convs, NULL);
321 if (texpr == NULL || gnm_expr_top_is_err (texpr, GNM_ERROR_REF)) {
322 // Nothing, error
323 } else if (GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_NAME) {
324 // Nothing, we're good
325 } else {
326 // Allow only ranges and normalize
327 GnmValue *v = gnm_expr_top_get_range (texpr);
328 gnm_expr_top_unref (texpr);
329 texpr = v ? gnm_expr_top_new_constant (v) : NULL;
333 dependent_managed_set_expr (&hlcwb->dep, texpr);
334 if (texpr)
335 gnm_expr_top_unref (texpr);
338 static const char *
339 gnm_hlink_cur_wb_get_target (GnmHLink const *lnk)
341 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
342 GnmExprTop const *texpr = hlcwb->dep.texpr;
343 char *tgt = NULL;
344 Sheet *sheet = lnk->sheet;
346 if (texpr && sheet) {
347 GnmConventions const *convs = sheet_get_conventions (sheet);
348 GnmParsePos pp;
349 parse_pos_init_sheet (&pp, sheet);
350 tgt = gnm_expr_top_as_string (texpr, &pp, convs);
353 // Use parent class for storage. Ick!
354 ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
355 ->set_target ((GnmHLink *)lnk, tgt);
356 g_free (tgt);
358 return ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
359 ->get_target (lnk);
362 static void
363 gnm_hlink_cur_wb_init (GObject *obj)
365 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)obj;
366 dependent_managed_init (&hlcwb->dep, NULL);
369 static void
370 gnm_hlink_cur_wb_finalize (GObject *obj)
372 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)obj;
374 dependent_managed_set_expr (&hlcwb->dep, NULL);
376 gnm_hlink_cur_wb_parent_class->finalize (obj);
379 static void
380 gnm_hlink_cur_wb_class_init (GObjectClass *object_class)
382 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
384 gnm_hlink_cur_wb_parent_class = g_type_class_peek_parent (object_class);
386 object_class->finalize = gnm_hlink_cur_wb_finalize;
387 hlink_class->Activate = gnm_hlink_cur_wb_activate;
388 hlink_class->set_sheet = gnm_hlink_cur_wb_set_sheet;
389 hlink_class->set_target = gnm_hlink_cur_wb_set_target;
390 hlink_class->get_target = gnm_hlink_cur_wb_get_target;
393 GSF_CLASS (GnmHLinkCurWB, gnm_hlink_cur_wb,
394 gnm_hlink_cur_wb_class_init, gnm_hlink_cur_wb_init,
395 GNM_HLINK_TYPE)
396 #if 0
398 #endif
402 * gnm_hlink_get_range_target:
403 * @lnk: the hyperlink to query
404 * @sr: location to start link target range
406 * This function determines the location that a link points to. It will
407 * resolve names.
409 * Returns: %TRUE, if the link refers to a range.
411 gboolean
412 gnm_hlink_get_range_target (GnmHLink const *lnk, GnmSheetRange *sr)
414 GnmHLinkCurWB *hlcwb;
415 GnmExprTop const *texpr;
416 GnmValue *vr;
417 GnmRangeRef const *r;
418 GnmParsePos pp;
419 Sheet *start_sheet, *end_sheet;
421 g_return_val_if_fail (GNM_IS_HLINK (lnk), FALSE);
423 if (!GNM_IS_HLINK_CUR_WB (lnk))
424 return FALSE;
426 hlcwb = (GnmHLinkCurWB *)lnk;
427 texpr = hlcwb->dep.texpr;
428 if (!texpr)
429 return FALSE;
430 vr = gnm_expr_top_get_range (texpr);
431 if (!vr)
432 return FALSE;
433 r = value_get_rangeref (vr);
435 parse_pos_init_sheet (&pp, lnk->sheet);
436 gnm_rangeref_normalize_pp (r, &pp, &start_sheet, &end_sheet,
437 &sr->range);
438 sr->sheet = start_sheet;
439 value_release (vr);
441 return TRUE;
445 GnmExprTop const *
446 gnm_hlink_get_target_expr (GnmHLink const *lnk)
449 GnmHLinkCurWB *hlcwb;
451 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
453 if (!GNM_IS_HLINK_CUR_WB (lnk))
454 return NULL;
456 hlcwb = (GnmHLinkCurWB *)lnk;
457 return hlcwb->dep.texpr;
462 /***************************************************************************/
463 /* Link to arbitrary urls */
464 typedef struct { GnmHLinkClass hlink; } GnmHLinkURLClass;
465 typedef struct {
466 GnmHLink hlink;
467 } GnmHLinkURL;
469 static gboolean
470 gnm_hlink_url_activate (GnmHLink *lnk, WBCGtk *wbcg)
472 GError *err = NULL;
473 GdkScreen *screen;
475 if (lnk->target == NULL)
476 return FALSE;
478 screen = gtk_window_get_screen (wbcg_toplevel (wbcg));
479 err = go_gtk_url_show (lnk->target, screen);
481 if (err != NULL) {
482 char *msg = g_strdup_printf (_("Unable to activate the url '%s'"), lnk->target);
483 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
484 msg, err->message);
485 g_free (msg);
486 g_error_free (err);
489 return err == NULL;
492 static void
493 gnm_hlink_url_class_init (GObjectClass *object_class)
495 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
497 hlink_class->Activate = gnm_hlink_url_activate;
500 GSF_CLASS (GnmHLinkURL, gnm_hlink_url,
501 gnm_hlink_url_class_init, NULL,
502 GNM_HLINK_TYPE)
504 /***************************************************************************/
505 /* email is just a url, but it is cleaner to stick it in a distinct type */
506 typedef struct { GnmHLinkURLClass hlink; } GnmHLinkEMailClass;
507 typedef struct {
508 GnmHLinkURL hlink;
509 } GnmHLinkEMail;
511 GSF_CLASS (GnmHLinkEMail, gnm_hlink_email,
512 NULL, NULL,
513 gnm_hlink_url_get_type ())
515 /***************************************************************************/
516 /* Link to arbitrary urls */
517 typedef struct { GnmHLinkClass hlink; } GnmHLinkExternalClass;
518 typedef struct {
519 GnmHLink hlink;
520 } GnmHLinkExternal;
522 static gboolean
523 gnm_hlink_external_activate (GnmHLink *lnk, WBCGtk *wbcg)
525 GError *err = NULL;
526 gboolean res = FALSE;
527 char *cmd;
528 GdkScreen *screen;
530 if (lnk->target == NULL)
531 return FALSE;
533 cmd = go_shell_arg_to_uri (lnk->target);
534 screen = gtk_window_get_screen (wbcg_toplevel (wbcg));
535 err = go_gtk_url_show (cmd, screen);
536 g_free (cmd);
538 if (err != NULL) {
539 char *msg = g_strdup_printf(_("Unable to open '%s'"), lnk->target);
540 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
541 msg, err->message);
542 g_free (msg);
543 g_error_free (err);
546 return res;
549 static void
550 gnm_hlink_external_class_init (GObjectClass *object_class)
552 GnmHLinkClass *hlink_class = (GnmHLinkClass *) object_class;
554 hlink_class->Activate = gnm_hlink_external_activate;
557 GSF_CLASS (GnmHLinkExternal, gnm_hlink_external,
558 gnm_hlink_external_class_init, NULL,
559 GNM_HLINK_TYPE)
561 void
562 _gnm_hlink_init (void)
564 /* make sure that all hlink types are registered */
565 gnm_hlink_cur_wb_get_type ();
566 gnm_hlink_url_get_type ();
567 gnm_hlink_email_get_type ();
568 gnm_hlink_external_get_type ();