GUI: reduce vertical size of the toolbar area
[gnumeric.git] / src / hlink.c
blobed7b3c91397186b47d6e273c16caa4d9b9c89d33
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 "value.h"
40 #include "mstyle.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
54 /**
55 * gnm_hlink_activate:
56 * @lnk: #GnmHLink
57 * @wbcg: the wbcg that activated the link
59 * Returns: %TRUE if the link successfully activated.
60 **/
61 gboolean
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);
69 /**
70 * gnm_sheet_hlink_find:
71 * @sheet: #Sheet
72 * @pos: #GcmCellPos
74 * Returns: (transfer none) (nullable): the found #GnmHLink.
75 **/
76 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);
83 static void
84 gnm_hlink_finalize (GObject *obj)
86 GnmHLink *lnk = (GnmHLink *)obj;
88 g_free (lnk->target);
89 lnk->target = NULL;
91 g_free (lnk->tip);
92 lnk->tip = NULL;
94 gnm_hlink_parent_class->finalize (obj);
97 static void
98 gnm_hlink_base_set_sheet (GnmHLink *lnk, Sheet *sheet)
100 lnk->sheet = sheet;
103 static void
104 gnm_hlink_base_set_target (GnmHLink *lnk, gchar const *target)
106 gchar *tmp = g_strdup (target);
107 g_free (lnk->target);
108 lnk->target = tmp;
111 static const char *
112 gnm_hlink_base_get_target (GnmHLink const *lnk)
114 return lnk->target;
117 static void
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;
130 static void
131 gnm_hlink_init (GObject *obj)
133 GnmHLink *lnk = (GnmHLink * )obj;
134 lnk->target = NULL;
135 lnk->tip = NULL;
138 GSF_CLASS_ABSTRACT (GnmHLink, gnm_hlink,
139 gnm_hlink_class_init, gnm_hlink_init, G_TYPE_OBJECT)
142 * gnm_hlink_get_target:
143 * @lnk: #GnmHLink
145 * Returns: (transfer none): @lnk's target.
147 const char *
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);
155 void
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);
164 * gnm_hlink_get_tip:
165 * @lnk: #GnmHLink
167 * Returns: (transfer none): @lnk's tooltip.
169 const char *
170 gnm_hlink_get_tip (GnmHLink const *lnk)
172 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
173 return lnk->tip;
176 void
177 gnm_hlink_set_tip (GnmHLink *lnk, gchar const *tip)
179 gchar *tmp;
181 g_return_if_fail (GNM_IS_HLINK (lnk));
183 tmp = g_strdup (tip);
184 g_free (lnk->tip);
185 lnk->tip = tmp;
189 * gnm_hlink_get_sheet:
190 * @lnk: #GnmHLink
192 * Returns: (transfer none): the sheet
194 Sheet *
195 gnm_hlink_get_sheet (GnmHLink *lnk)
197 g_return_val_if_fail (GNM_IS_HLINK (lnk), NULL);
198 return lnk->sheet;
201 void
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);
208 GnmHLink *
209 gnm_hlink_new (GType typ, Sheet *sheet)
211 GnmHLink *lnk;
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);
220 return lnk;
224 * gnm_hlink_dup:
225 * @lnk: Existing link
227 * Returns: (transfer full): A duplicate link.
229 GnmHLink *
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);
238 return new_lnk;
242 * gnm_hlink_equal:
243 * @a: a #GnmHLink
244 * @b: a #GnmHLink
245 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
247 * Returns: %TRUE, if links are equal
249 gboolean
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);
255 if (a == b)
256 return TRUE;
258 if (!relax_sheet && a->sheet != b->sheet)
259 return FALSE;
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;
268 typedef struct {
269 GnmHLink hlink;
271 GnmDependent dep;
272 } GnmHLinkCurWB;
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;
280 static gboolean
281 gnm_hlink_cur_wb_activate (GnmHLink *lnk, WBCGtk *wbcg)
283 WorkbookControl *wbc = GNM_WBC (wbcg);
284 SheetView *sv;
285 GnmSheetRange sr;
287 if (!gnm_hlink_get_range_target (lnk, &sr)) {
288 go_cmd_context_error_invalid
289 (GO_CMD_CONTEXT (wbcg),
290 _("Link target"),
291 lnk->target ? lnk->target : "-");
292 return FALSE;
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);
303 return TRUE;
306 static void
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);
315 static void
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) {
325 GnmParsePos pp;
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)) {
333 // Nothing, error
334 } else if (gnm_expr_get_name (texpr->expr)) {
335 // Nothing, we're good
336 } else {
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);
345 if (texpr)
346 gnm_expr_top_unref (texpr);
349 static const char *
350 gnm_hlink_cur_wb_get_target (GnmHLink const *lnk)
352 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)lnk;
353 GnmExprTop const *texpr = hlcwb->dep.texpr;
354 char *tgt = NULL;
355 Sheet *sheet = lnk->sheet;
357 if (texpr && sheet) {
358 GnmConventions const *convs = sheet_get_conventions (sheet);
359 GnmParsePos pp;
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);
367 g_free (tgt);
369 return ((GnmHLinkClass*)gnm_hlink_cur_wb_parent_class)
370 ->get_target (lnk);
373 static void
374 gnm_hlink_cur_wb_init (GObject *obj)
376 GnmHLinkCurWB *hlcwb = (GnmHLinkCurWB *)obj;
377 dependent_managed_init (&hlcwb->dep, NULL);
380 static void
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);
390 static void
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,
406 GNM_HLINK_TYPE)
407 #if 0
409 #endif
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
418 * resolve names.
420 * Returns: %TRUE, if the link refers to a range.
422 gboolean
423 gnm_hlink_get_range_target (GnmHLink const *lnk, GnmSheetRange *sr)
425 GnmHLinkCurWB *hlcwb;
426 GnmExprTop const *texpr;
427 GnmValue *vr;
428 GnmRangeRef const *r;
429 GnmParsePos pp;
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))
437 return FALSE;
439 hlcwb = (GnmHLinkCurWB *)lnk;
440 texpr = hlcwb->dep.texpr;
441 if (!texpr)
442 return FALSE;
443 vr = gnm_expr_top_get_range (texpr);
444 if (!vr)
445 return FALSE;
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,
450 &sr->range);
451 sr->sheet = start_sheet;
452 value_release (vr);
454 return TRUE;
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.
466 GnmExprTop const *
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))
475 return NULL;
477 hlcwb = (GnmHLinkCurWB *)lnk;
478 return hlcwb->dep.texpr;
483 /***************************************************************************/
484 /* Link to arbitrary urls */
485 typedef struct { GnmHLinkClass hlink; } GnmHLinkURLClass;
486 typedef struct {
487 GnmHLink hlink;
488 } GnmHLinkURL;
490 static gboolean
491 gnm_hlink_url_activate (GnmHLink *lnk, WBCGtk *wbcg)
493 GError *err = NULL;
494 GdkScreen *screen;
496 if (lnk->target == NULL)
497 return FALSE;
499 screen = gtk_window_get_screen (wbcg_toplevel (wbcg));
500 err = go_gtk_url_show (lnk->target, screen);
502 if (err != NULL) {
503 char *msg = g_strdup_printf (_("Unable to activate the url '%s'"), lnk->target);
504 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
505 msg, err->message);
506 g_free (msg);
507 g_error_free (err);
510 return err == NULL;
513 static void
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,
523 GNM_HLINK_TYPE)
525 /***************************************************************************/
526 /* email is just a url, but it is cleaner to stick it in a distinct type */
527 typedef struct { GnmHLinkURLClass hlink; } GnmHLinkEMailClass;
528 typedef struct {
529 GnmHLinkURL hlink;
530 } GnmHLinkEMail;
532 GSF_CLASS (GnmHLinkEMail, gnm_hlink_email,
533 NULL, NULL,
534 gnm_hlink_url_get_type ())
536 /***************************************************************************/
537 /* Link to arbitrary urls */
538 typedef struct { GnmHLinkClass hlink; } GnmHLinkExternalClass;
539 typedef struct {
540 GnmHLink hlink;
541 } GnmHLinkExternal;
543 static gboolean
544 gnm_hlink_external_activate (GnmHLink *lnk, WBCGtk *wbcg)
546 GError *err = NULL;
547 gboolean res = FALSE;
548 char *cmd;
549 GdkScreen *screen;
551 if (lnk->target == NULL)
552 return FALSE;
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);
557 g_free (cmd);
559 if (err != NULL) {
560 char *msg = g_strdup_printf(_("Unable to open '%s'"), lnk->target);
561 go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg),
562 msg, err->message);
563 g_free (msg);
564 g_error_free (err);
567 return res;
570 static void
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,
580 GNM_HLINK_TYPE)
583 * _gnm_hlink_init: (skip)
585 void
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 ();