1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * dialog-hyperlink.c: Add or edit a hyperlink
6 * Copyright (C) 2002 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
24 #include <gnumeric-config.h>
30 #include <widgets/gnumeric-expr-entry.h>
31 #include "expr-name.h"
36 #include <style-color.h>
37 #include <sheet-control.h>
38 #include <sheet-view.h>
39 #include <sheet-style.h>
42 #include <goffice/goffice.h>
45 #include <glib/gi18n-lib.h>
60 GtkLabel
*type_descriptor
;
61 GnmExprEntry
*internal_link_ee
;
65 GtkWidget
*use_def_widget
;
69 dhl_free (HyperlinkState
*state
)
71 if (state
->gui
!= NULL
) {
72 g_object_unref (state
->gui
);
75 if (state
->link
!= NULL
) {
76 g_object_unref (state
->link
);
84 dhl_get_default_tip (char const * const target
) {
85 char *default_text
= _("Left click once to follow this link.\n"
86 "Middle click once to select this cell");
88 return g_strdup (default_text
);
90 return g_strjoin ("\n", target
, default_text
, NULL
);
94 dhl_set_tip (HyperlinkState
* state
)
96 char const *tip
= gnm_hlink_get_tip (state
->link
);
101 w
= go_gtk_builder_get_widget (state
->gui
, "use-default-tip");
102 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), TRUE
);
107 char const * const target
= gnm_hlink_get_target (state
->link
);
108 char *default_tip
= dhl_get_default_tip (target
);
109 gboolean is_default
= (strcmp (tip
, default_tip
) == 0);
110 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state
->use_def_widget
),
112 g_free (default_tip
);
116 w
= go_gtk_builder_get_widget (state
->gui
, "use-this-tip");
117 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), TRUE
);
119 tb
= gtk_text_view_get_buffer
120 (GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "tip-entry")));
122 gtk_text_buffer_set_text (tb
, (tip
== NULL
) ? "" : tip
, -1);
126 dhl_get_tip (HyperlinkState
*state
, char const *target
)
128 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state
->use_def_widget
)))
132 GtkTextBuffer
*tb
= gtk_text_view_get_buffer
133 (GTK_TEXT_VIEW (go_gtk_builder_get_widget (state
->gui
, "tip-entry")));
134 GtkTextIter start_iter
, end_iter
;
136 gtk_text_buffer_get_start_iter (tb
, &start_iter
);
137 gtk_text_buffer_get_end_iter (tb
, &end_iter
);
139 tip
= gtk_text_buffer_get_text (tb
, &start_iter
, &end_iter
, FALSE
);
141 if (tip
!= NULL
&& strlen (tip
) == 0) {
151 dhl_set_target_cur_wb (HyperlinkState
*state
, const char* const target
)
153 gnm_expr_entry_load_from_text (state
->internal_link_ee
, target
);
157 dhl_get_target_cur_wb (HyperlinkState
*state
, gboolean
*success
)
160 GnmExprEntry
*gee
= state
->internal_link_ee
;
161 char const *target
= gnm_expr_entry_get_text (gee
);
162 Sheet
*sheet
= sc_sheet (state
->sc
);
166 if (strlen (target
) == 0) {
169 val
= gnm_expr_entry_parse_as_value (gee
, sheet
);
171 /* not an address, is it a name ? */
175 parse_pos_init_sheet (&pp
, sheet
);
176 nexpr
= expr_name_lookup (&pp
, target
);
178 val
= gnm_expr_top_get_range (nexpr
->texpr
);
182 ret
= g_strdup (target
);
185 go_gtk_notice_dialog (GTK_WINDOW (state
->dialog
),
187 _("Not a range or name"));
188 gnm_expr_entry_grab_focus (gee
, TRUE
);
195 dhl_set_target_external (HyperlinkState
*state
, const char* const target
)
197 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "external-link");
199 gtk_entry_set_text (GTK_ENTRY (w
), target
);
203 dhl_get_target_external (HyperlinkState
*state
, gboolean
*success
)
205 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "external-link");
206 const char *target
= gtk_entry_get_text (GTK_ENTRY (w
));
209 return strlen (target
) > 0 ? g_strdup (target
) : NULL
;
213 dhl_set_target_email (HyperlinkState
*state
, const char* const target
)
215 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "email-address");
216 GtkWidget
*w2
= go_gtk_builder_get_widget (state
->gui
, "email-subject");
221 if (!target
|| *target
== '\0')
224 if( strncmp (target
, "mailto:", strlen ("mailto:")) != 0)
227 cursor
= g_strdup (target
+ strlen ("mailto:"));
229 subject
= strstr (cursor
, "?subject=");
231 guitext
= g_uri_unescape_string (subject
+ strlen ("?subject="),
233 gtk_entry_set_text (GTK_ENTRY (w2
), guitext
);
238 guitext
= g_uri_unescape_string (cursor
, NULL
);
240 gtk_entry_set_text (GTK_ENTRY (w
), guitext
);
247 dhl_get_target_email (HyperlinkState
*state
, gboolean
*success
)
249 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "email-address");
250 GtkWidget
*w2
= go_gtk_builder_get_widget (state
->gui
, "email-subject");
251 const char *address
= gtk_entry_get_text (GTK_ENTRY (w
));
252 const char *subject
= gtk_entry_get_text (GTK_ENTRY (w2
));
253 gchar
* enc_subj
, *enc_addr
;
257 if (!address
|| *address
== '\0') {
261 enc_addr
= go_url_encode (address
, 0);
262 if (!subject
|| *subject
== '\0') {
263 result
= g_strconcat ("mailto:", enc_addr
, NULL
);
265 enc_subj
= go_url_encode (subject
, 0);
267 result
= g_strconcat ("mailto:", enc_addr
,
268 "?subject=", enc_subj
, NULL
);
278 dhl_set_target_url (HyperlinkState
*state
, const char* const target
)
280 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "url");
282 gtk_entry_set_text (GTK_ENTRY (w
), target
);
286 dhl_get_target_url (HyperlinkState
*state
, gboolean
*success
)
288 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, "url");
289 const char *target
= gtk_entry_get_text (GTK_ENTRY (w
));
292 return strlen (target
) > 0 ? g_strdup (target
) : NULL
;
297 char const *icon_name
;
299 char const *widget_name
;
300 char const *descriptor
;
301 void (*set_target
) (HyperlinkState
*state
, const char* const target
);
302 char * (*get_target
) (HyperlinkState
*state
, gboolean
*success
);
304 { N_("Internal Link"), "gnumeric-link-internal",
305 "GnmHLinkCurWB", "internal-link-grid",
306 N_("Jump to specific cells or named range in the current workbook"),
307 dhl_set_target_cur_wb
,
308 dhl_get_target_cur_wb
},
310 { N_("External Link"), "gnumeric-link-external",
311 "GnmHLinkExternal", "external-link-grid" ,
312 N_("Open an external file with the specified name"),
313 dhl_set_target_external
,
314 dhl_get_target_external
},
315 { N_("Email Link"), "gnumeric-link-email",
316 "GnmHLinkEMail", "email-grid" ,
317 N_("Prepare an email"),
318 dhl_set_target_email
,
319 dhl_get_target_email
},
320 { N_("Web Link"), "gnumeric-link-url",
321 "GnmHLinkURL", "url-grid" ,
322 N_("Browse to the specified URL"),
328 dhl_set_target (HyperlinkState
* state
)
331 char const * const target
= gnm_hlink_get_target (state
->link
);
332 char const * type_name
;
335 type_name
= G_OBJECT_TYPE_NAME (state
->link
);
336 for (i
= 0 ; i
< G_N_ELEMENTS (type
); i
++) {
337 if (strcmp (type_name
, type
[i
].name
) == 0) {
338 if (type
[i
].set_target
)
339 (type
[i
].set_target
) (state
, target
);
347 dhl_get_target (HyperlinkState
*state
, gboolean
*success
)
350 char const *type_name
= G_OBJECT_TYPE_NAME (state
->link
);
353 for (i
= 0 ; i
< G_N_ELEMENTS (type
); i
++) {
354 if (strcmp (type_name
, type
[i
].name
) == 0) {
355 if (type
[i
].get_target
)
356 return (type
[i
].get_target
) (state
, success
);
366 dhl_cb_cancel (G_GNUC_UNUSED GtkWidget
*button
, HyperlinkState
*state
)
368 gtk_widget_destroy (state
->dialog
);
372 dhl_cb_ok (G_GNUC_UNUSED GtkWidget
*button
, HyperlinkState
*state
)
380 target
= dhl_get_target (state
, &success
);
382 return; /* Let user continue editing */
384 wb_control_sheet_focus (GNM_WBC (state
->wbcg
), state
->sheet
);
387 gnm_hlink_set_sheet (state
->link
, state
->sheet
);
388 gnm_hlink_set_target (state
->link
, target
);
389 tip
= dhl_get_tip (state
, target
);
390 gnm_hlink_set_tip (state
->link
, tip
);
392 style
= gnm_style_new ();
393 gnm_style_set_hlink (style
, g_object_ref (state
->link
));
394 gnm_style_set_font_uline (style
, UNDERLINE_SINGLE
);
395 gnm_style_set_font_color (style
, gnm_color_new_go (GO_COLOR_BLUE
));
398 cmdname
= _("Add Hyperlink");
399 cmd_selection_hyperlink (GNM_WBC (state
->wbcg
),
403 cmdname
= _("Edit Hyperlink");
404 cmd_selection_hyperlink (GNM_WBC (state
->wbcg
),
409 } else if (!state
->is_new
) {
410 style
= gnm_style_new ();
411 gnm_style_set_hlink (style
, NULL
);
412 cmdname
= _("Remove Hyperlink");
413 cmd_selection_hyperlink (GNM_WBC (state
->wbcg
), style
,
416 gtk_widget_destroy (state
->dialog
);
420 dhl_setup_type (HyperlinkState
*state
)
423 char const *name
= G_OBJECT_TYPE_NAME (state
->link
);
426 for (i
= 0 ; i
< G_N_ELEMENTS (type
); i
++) {
427 w
= go_gtk_builder_get_widget (state
->gui
, type
[i
].widget_name
);
429 if (!strcmp (name
, type
[i
].name
)) {
430 gtk_widget_show_all (w
);
431 gtk_image_set_from_icon_name
432 (state
->type_image
, type
[i
].icon_name
,
433 GTK_ICON_SIZE_DIALOG
);
434 gtk_label_set_text (state
->type_descriptor
,
435 _(type
[i
].descriptor
));
442 dhl_set_type (HyperlinkState
*state
, GType type
)
444 GnmHLink
*old
= state
->link
;
446 state
->link
= gnm_hlink_new (type
, state
->sheet
);
448 gnm_hlink_set_target (state
->link
, gnm_hlink_get_target (old
));
449 gnm_hlink_set_tip (state
->link
, gnm_hlink_get_tip (old
));
450 g_object_unref (old
);
452 dhl_setup_type (state
);
456 dhl_cb_menu_changed (GtkComboBox
*box
, HyperlinkState
*state
)
458 int i
= gtk_combo_box_get_active (box
);
459 dhl_set_type (state
, g_type_from_name (
464 dhl_init (HyperlinkState
*state
)
466 static char const * const label
[] = {
467 "internal-link-label",
468 "external-link-label",
469 "email-address-label",
470 "email-subject-label",
475 GtkSizeGroup
*size_group
;
476 GnmExprEntry
*expr_entry
;
477 unsigned i
, select
= 0;
480 GtkCellRenderer
*renderer
;
483 gtk_widget_hide (go_gtk_builder_get_widget (state
->gui
, "email-grid"));
485 size_group
= gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL
);
486 for (i
= 0 ; i
< G_N_ELEMENTS (label
); i
++)
487 gtk_size_group_add_widget (size_group
,
488 go_gtk_builder_get_widget (state
->gui
, label
[i
]));
489 g_object_unref (size_group
);
491 w
= go_gtk_builder_get_widget (state
->gui
, "link-type-image");
492 state
->type_image
= GTK_IMAGE (w
);
493 w
= go_gtk_builder_get_widget (state
->gui
, "link-type-descriptor");
494 state
->type_descriptor
= GTK_LABEL (w
);
496 w
= go_gtk_builder_get_widget (state
->gui
, "internal-link-grid");
497 expr_entry
= gnm_expr_entry_new (state
->wbcg
, TRUE
);
498 gtk_widget_set_hexpand (GTK_WIDGET (expr_entry
), TRUE
);
499 gtk_container_add (GTK_CONTAINER (w
), GTK_WIDGET (expr_entry
));
500 gtk_entry_set_activates_default
501 (gnm_expr_entry_get_entry (expr_entry
), TRUE
);
502 state
->internal_link_ee
= expr_entry
;
504 w
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
505 g_signal_connect (G_OBJECT (w
),
507 G_CALLBACK (dhl_cb_cancel
), state
);
509 w
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
510 g_signal_connect (G_OBJECT (w
),
512 G_CALLBACK (dhl_cb_ok
), state
);
513 gtk_window_set_default (GTK_WINDOW (state
->dialog
), w
);
515 gnm_init_help_button (
516 go_gtk_builder_get_widget (state
->gui
, "help_button"),
517 GNUMERIC_HELP_LINK_HYPERLINK
);
519 store
= gtk_list_store_new (2, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
520 w
= go_gtk_builder_get_widget (state
->gui
, "link-type-menu");
521 gtk_combo_box_set_model (GTK_COMBO_BOX (w
), GTK_TREE_MODEL (store
));
522 g_object_unref (store
);
524 for (i
= 0 ; i
< G_N_ELEMENTS (type
); i
++) {
525 GdkPixbuf
*pixbuf
= go_gtk_widget_render_icon_pixbuf
526 (GTK_WIDGET (wbcg_toplevel (state
->wbcg
)),
527 type
[i
].icon_name
, GTK_ICON_SIZE_MENU
);
528 gtk_list_store_append (store
, &iter
);
529 gtk_list_store_set (store
, &iter
,
533 g_object_unref (pixbuf
);
535 if (strcmp (G_OBJECT_TYPE_NAME (state
->link
),
540 renderer
= gtk_cell_renderer_pixbuf_new ();
541 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w
),
544 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w
), renderer
,
548 renderer
= gtk_cell_renderer_text_new ();
549 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w
),
552 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w
), renderer
,
555 gtk_combo_box_set_active (GTK_COMBO_BOX (w
), select
);
557 g_signal_connect (G_OBJECT (w
), "changed",
558 G_CALLBACK (dhl_cb_menu_changed
),
561 gnm_link_button_and_entry (go_gtk_builder_get_widget (state
->gui
, "use-this-tip"),
562 go_gtk_builder_get_widget (state
->gui
, "tip-entry"));
564 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
566 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
571 #define DIALOG_KEY "hyperlink-dialog"
573 dialog_hyperlink (WBCGtk
*wbcg
, SheetControl
*sc
)
576 HyperlinkState
* state
;
577 GnmHLink
*link
= NULL
;
580 g_return_if_fail (wbcg
!= NULL
);
582 if (gnm_dialog_raise_if_exists (wbcg
, DIALOG_KEY
))
585 gui
= gnm_gtk_builder_load ("res:ui/hyperlink.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
589 state
= g_new (HyperlinkState
, 1);
591 state
->wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
594 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "hyperlink-dialog");
596 state
->use_def_widget
= go_gtk_builder_get_widget (state
->gui
, "use-default-tip");
598 state
->sheet
= sc_sheet (sc
);
599 for (ptr
= sc_view (sc
)->selections
; ptr
!= NULL
; ptr
= ptr
->next
) {
600 GnmRange
const *r
= ptr
->data
;
601 link
= sheet_style_region_contains_link (state
->sheet
, r
);
606 /* We are creating a new link since the existing link */
607 /* may be used in many places. */
608 /* We are duplicating it here rather than in an ok handler in case */
609 /* The link is changed for a differnet cell in a different view. */
611 state
->link
= gnm_hlink_new (gnm_hlink_url_get_type (), state
->sheet
);
612 state
->is_new
= TRUE
;
614 state
->link
= gnm_hlink_new (G_OBJECT_TYPE (link
), state
->sheet
);
615 state
->is_new
= FALSE
;
616 gnm_hlink_set_target (state
->link
, gnm_hlink_get_target (link
));
617 gnm_hlink_set_tip (state
->link
, gnm_hlink_get_tip (link
));
620 if (dhl_init (state
)) {
621 go_gtk_notice_dialog (wbcg_toplevel (wbcg
), GTK_MESSAGE_ERROR
,
622 _("Could not create the hyperlink dialog."));
627 dhl_setup_type (state
);
629 dhl_set_target (state
);
632 /* a candidate for merging into attach guru */
633 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
635 go_gtk_nonmodal_dialog (wbcg_toplevel (state
->wbcg
),
636 GTK_WINDOW (state
->dialog
));
638 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
639 g_object_set_data_full (G_OBJECT (state
->dialog
),
640 "state", state
, (GDestroyNotify
) dhl_free
);
641 gtk_widget_show (state
->dialog
);