diff --git a/plugins/fn-info/functions.c b/plugins/fn-info/functions.c
index cf06ef1..bd4f4bb 100644
--- a/plugins/fn-info/functions.c
+++ b/plugins/fn-info/functions.c
@@ -1840,8 +1840,11 @@ gnumeric_get_link (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
 		gnm_cellpos_init_cellref (&pos, a, &(ei->pos->eval), sheet);
 		link = sheet_hlink_find (sheet, &pos);
 
-		if (link)
-			return value_new_string (gnm_hlink_get_target (link));
+		if (link) {
+			GnmValue *res = value_new_string (gnm_hlink_get_target (link));
+			g_object_unref (link);
+			return res;
+		}
 	}
 
 	return value_new_empty ();
diff --git a/plugins/fn-lookup/functions.c b/plugins/fn-lookup/functions.c
index f1eed50..3f1ca16 100644
--- a/plugins/fn-lookup/functions.c
+++ b/plugins/fn-lookup/functions.c
@@ -1686,10 +1686,15 @@ static GnmFuncHelp const help_hyperlink[] = {
 static GnmValue *
 gnumeric_hyperlink (GnmFuncEvalInfo *ei, GnmValue const * const *args)
 {
-	GnmValue const * v = args[1];
-	if (v == NULL)
-		v = args[0];
-	return value_dup (v);
+	GnmValue const *l = args[0];
+	GnmValue const *v = args[1] ? args[1] : l;
+
+	/*
+	 * If the special GNM_EXPR_EVAL_HYPERLINK flag is set, return the
+	 * link.  Otherwise return the value.
+	 */
+
+	return value_dup ((ei->flags & GNM_EXPR_EVAL_HYPERLINK) ? l : v);
 }
 
 /***************************************************************************/
diff --git a/src/expr.c b/src/expr.c
index c3a4baa..356d58b 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -670,7 +670,8 @@ gnm_expr_extract_ref (GnmRangeRef *res, GnmExpr const *expr,
 
 		ei.pos = pos;
 		ei.func_call = &expr->func;
-		v = function_call_with_exprs (&ei, flags);
+		ei.flags = flags;
+		v = function_call_with_exprs (&ei);
 
 		if (v != NULL) {
 			if (v->type == VALUE_CELLRANGE) {
@@ -1448,7 +1449,8 @@ gnm_expr_eval (GnmExpr const *expr, GnmEvalPos const *pos,
 		GnmFuncEvalInfo ei;
 		ei.pos = pos;
 		ei.func_call = &expr->func;
-		res = function_call_with_exprs (&ei, flags);
+		ei.flags = flags;
+		res = function_call_with_exprs (&ei);
 		if (res == NULL)
 			return (flags & GNM_EXPR_EVAL_PERMIT_EMPTY)
 			    ? NULL : value_new_int (0);
diff --git a/src/func.c b/src/func.c
index a518df2..5b489c9 100644
--- a/src/func.c
+++ b/src/func.c
@@ -1732,14 +1732,13 @@ free_values (GnmValue **values, int top)
 /**
  * function_call_with_exprs:
  * @ei: EvalInfo containing valid fn_def!
- * @flags:
  *
  * Do the guts of calling a function.
  *
  * Returns the result.
  **/
 GnmValue *
-function_call_with_exprs (GnmFuncEvalInfo *ei, GnmExprEvalFlags flags)
+function_call_with_exprs (GnmFuncEvalInfo *ei)
 {
 	GnmFunc const *fn_def;
 	int	  i, iter_count, iter_width = 0, iter_height = 0;
@@ -1748,10 +1747,13 @@ function_call_with_exprs (GnmFuncEvalInfo *ei, GnmExprEvalFlags flags)
 	int	 *iter_item = NULL;
 	int argc;
 	GnmExprConstPtr *argv;
+	GnmExprEvalFlags flags, pass_flags;
 
 	g_return_val_if_fail (ei != NULL, NULL);
 	g_return_val_if_fail (ei->func_call != NULL, NULL);
 
+	flags = ei->flags;
+
 	argc = ei->func_call->argc;
 	argv = ei->func_call->argv;
 	fn_def = ei->func_call->func;
@@ -1777,6 +1779,10 @@ function_call_with_exprs (GnmFuncEvalInfo *ei, GnmExprEvalFlags flags)
 	    iter_count == -1)
 		return gnumeric_if2 (ei, argc, argv, flags);
 
+	pass_flags = (flags &
+		      (GNM_EXPR_EVAL_ARRAY_CONTEXT |
+		       GNM_EXPR_EVAL_HYPERLINK));
+
 	for (i = 0; i < argc; i++) {
 		char arg_type = fn_def->fn.args.arg_types[i];
 		/* expr is always non-null, missing args are encoded as
@@ -1786,6 +1792,7 @@ function_call_with_exprs (GnmFuncEvalInfo *ei, GnmExprEvalFlags flags)
 		if (arg_type == 'A' || arg_type == 'r') {
 			tmp = args[i] = gnm_expr_eval
 				(expr, ei->pos,
+				 pass_flags |
 				 GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
 				 GNM_EXPR_EVAL_WANT_REF);
 			if (VALUE_IS_ERROR (tmp)) {
@@ -1809,10 +1816,13 @@ function_call_with_exprs (GnmFuncEvalInfo *ei, GnmExprEvalFlags flags)
 		}
 
 		/* force scalars whenever we are certain */
-		tmp = args[i] = gnm_expr_eval (expr, ei->pos,
-		       ((iter_count >= 0 || arg_type == '?')
-			       ? (GNM_EXPR_EVAL_PERMIT_EMPTY | GNM_EXPR_EVAL_PERMIT_NON_SCALAR)
-			       : (GNM_EXPR_EVAL_PERMIT_EMPTY)));
+		tmp = args[i] = gnm_expr_eval
+			(expr, ei->pos,
+			 pass_flags |
+			 GNM_EXPR_EVAL_PERMIT_EMPTY |
+			 (iter_count >= 0 || arg_type == '?'
+			  ? GNM_EXPR_EVAL_PERMIT_NON_SCALAR
+			  : (GNM_EXPR_EVAL_PERMIT_EMPTY)));
 
 		if (arg_type == '?')	/* '?' arguments are unrestriced */
 			continue;
diff --git a/src/func.h b/src/func.h
index eee4eae..3ec536e 100644
--- a/src/func.h
+++ b/src/func.h
@@ -218,6 +218,7 @@ struct _GnmFunc {
 struct _GnmFuncEvalInfo {
 	GnmEvalPos const *pos;
 	GnmExprFunction const *func_call;
+	GnmExprEvalFlags flags;
 };
 
 GType       gnm_func_get_type        (void);
@@ -266,8 +267,7 @@ char       *gnm_func_convert_markup_to_pango (char const *desc,
 
 /*************************************************************************/
 
-GnmValue *function_call_with_exprs	(GnmFuncEvalInfo *ei,
-					 GnmExprEvalFlags flags);
+GnmValue *function_call_with_exprs	(GnmFuncEvalInfo *ei);
 GnmValue *function_call_with_values     (GnmEvalPos const *ep, char const *name,
 					 int argc, GnmValue const * const *values);
 GnmValue *function_def_call_with_values (GnmEvalPos const *ep, GnmFunc const *fn,
diff --git a/src/gnumeric.h b/src/gnumeric.h
index e8bcbbb..0ed549a 100644
--- a/src/gnumeric.h
+++ b/src/gnumeric.h
@@ -85,7 +85,8 @@ typedef enum {
 	GNM_EXPR_EVAL_PERMIT_NON_SCALAR	= 0x1,
 	GNM_EXPR_EVAL_PERMIT_EMPTY	= 0x2,
 	GNM_EXPR_EVAL_WANT_REF		= 0x4,
-	GNM_EXPR_EVAL_ARRAY_CONTEXT     = 0x8
+	GNM_EXPR_EVAL_ARRAY_CONTEXT     = 0x8,
+	GNM_EXPR_EVAL_HYPERLINK		= 0x10
 } GnmExprEvalFlags;
 
 G_END_DECLS
diff --git a/src/hlink.c b/src/hlink.c
index 393f2da..00b5a20 100644
--- a/src/hlink.c
+++ b/src/hlink.c
@@ -38,6 +38,8 @@
 #include "expr.h"
 #include "value.h"
 #include "mstyle.h"
+#include "func.h"
+#include "cell.h"
 
 #include <goffice/goffice.h>
 #include <gsf/gsf-impl-utils.h>
@@ -64,18 +66,48 @@ gnm_hlink_activate (GnmHLink *lnk, WBCGtk *wbcg)
 	return GET_CLASS (lnk)->Activate (lnk, wbcg);
 }
 
+/*
+ * Approximation: we want to check for hyperlink calls anywhere.
+ */
+static gboolean
+is_hyperlink_call (const GnmExprTop *texpr)
+{
+	GnmExpr const *fcall = texpr
+		? gnm_expr_top_first_funcall (texpr)
+		: NULL;
+	GnmFunc *func;
+
+	if (!fcall)
+		return FALSE;
+
+	func = gnm_expr_get_func_def (fcall);
+	return strcmp (func->name, "hyperlink") == 0;
+}
+
 /**
  * sheet_hlink_find:
  * @sheet: #Sheet
  * @pos: #GcmCellPos
  *
- * Returns: (transfer none): the found #GnmHLink.
+ * Returns: (transfer full): the found #GnmHLink, if any.
  **/
 GnmHLink *
 sheet_hlink_find (Sheet const *sheet, GnmCellPos const *pos)
 {
-	GnmStyle const *style = sheet_style_get (sheet, pos->col, pos->row);
-	return gnm_style_get_hlink (style);
+	GnmStyle const *style;
+	GnmCell const *cell = sheet_cell_get (sheet, pos->col, pos->row);
+	GnmHLink *hlink;
+
+	if (cell && is_hyperlink_call (cell->base.texpr)) {
+		hlink = g_object_new (gnm_hlink_url_get_type (), NULL);
+		gnm_hlink_set_target (hlink, "http://faking.it");
+		gnm_hlink_set_tip (hlink, "Link to faking.it");
+		return hlink;
+	}
+
+	style = sheet_style_get (sheet, pos->col, pos->row);
+	hlink = gnm_style_get_hlink (style);
+	return hlink ? g_object_ref (hlink) : NULL;
 }
 
 static void
diff --git a/src/item-grid.c b/src/item-grid.c
index 5254c81..4373c97 100644
--- a/src/item-grid.c
+++ b/src/item-grid.c
@@ -72,7 +72,7 @@ struct _GnmItemGrid {
 	/* information for the cursor motion handler */
 	guint cursor_timer;
 	gint64 last_x, last_y;
-	GnmHLink *cur_link; /* do not dereference, just a pointer */
+	GnmHLink *cur_link;
 	GtkWidget *tip;
 	guint tip_timer;
 
@@ -156,9 +156,9 @@ item_grid_finalize (GObject *object)
 		ig->cursor_timer = 0;
 	}
 	ig_clear_hlink_tip (ig);
-	ig->cur_link = NULL;
+	g_clear_object (&ig->cur_link);
 
-	(*G_OBJECT_CLASS (parent_class)->finalize) (object);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static gint
@@ -191,6 +191,10 @@ cb_cursor_motion (GnmItemGrid *ig)
 		gtk_widget_destroy (gtk_widget_get_toplevel (ig->tip));
 		ig->tip = NULL;
 	}
+
+	if (old_link)
+		g_object_unref (old_link);
+
 	return FALSE;
 }
 
@@ -1050,36 +1054,22 @@ cb_extend_expr_range (GnmPane *pane, GnmPaneSlideInfo const *info)
 static gint
 cb_cursor_come_to_rest (GnmItemGrid *ig)
 {
-	Sheet const *sheet = scg_sheet (ig->scg);
 	GocCanvas *canvas = ig->canvas_item.canvas;
-	GnmPane *pane = GNM_PANE (canvas);
-	GnmHLink *link;
-	gint64 x, y;
-	GnmCellPos pos;
 	char const *tiptext;
 
-	/* Be anal and look it up in case something has destroyed the link
-	 * since the last motion */
-	x = ig->last_x;
-	y = ig->last_y;
-	pos.col = gnm_pane_find_col (pane, x, NULL);
-	pos.row = gnm_pane_find_row (pane, y, NULL);
-
-	link = sheet_hlink_find (sheet, &pos);
-	if (link != NULL && (tiptext = gnm_hlink_get_tip (link)) != NULL) {
-		g_return_val_if_fail (link == ig->cur_link, FALSE);
-
-		if (ig->tip == NULL && strlen (tiptext) > 0) {
-			GtkWidget *cw = GTK_WIDGET (canvas);
-			int wx, wy;
-
-			gnm_canvas_get_position (canvas, &wx, &wy,
-						 ig->last_x, ig->last_y);
-			ig->tip = gnumeric_create_tooltip (cw);
-			gtk_label_set_text (GTK_LABEL (ig->tip), tiptext);
-			gnumeric_position_tooltip (ig->tip, wx, wy, TRUE);
-			gtk_widget_show_all (gtk_widget_get_toplevel (ig->tip));
-		}
+	if (ig->tip == NULL &&
+	    ig->cur_link &&
+	    ((tiptext = gnm_hlink_get_tip (ig->cur_link))) &&
+	    *tiptext != 0) {
+		GtkWidget *cw = GTK_WIDGET (canvas);
+		int wx, wy;
+
+		gnm_canvas_get_position (canvas, &wx, &wy,
+					 ig->last_x, ig->last_y);
+		ig->tip = gnumeric_create_tooltip (cw);
+		gtk_label_set_text (GTK_LABEL (ig->tip), tiptext);
+		gnumeric_position_tooltip (ig->tip, wx, wy, TRUE);
+		gtk_widget_show_all (gtk_widget_get_toplevel (ig->tip));
 	}
 
 	return FALSE;
@@ -1170,8 +1160,10 @@ item_grid_button_released (GocItem *item, int button, G_GNUC_UNUSED double x_, G
 			GnmHLink *link;
 			/* check for hyper links */
 			link = sheet_hlink_find (sheet, pos);
-			if (link != NULL)
+			if (link != NULL) {
 				gnm_hlink_activate (link, scg_wbcg (scg));
+				g_object_unref (link);
+			}
 		}
 	}
 	return TRUE;
