Next: Fields, Previous: Lazy Properties, Up: Text Properties [Contents][Index]

#### 31.19.8 Defining Clickable Text

Clickable text is text that can be clicked, with either the mouse or via a keyboard command, to produce some result. Many major modes use clickable text to implement textual hyper-links, or links for short.

The easiest way to insert and manipulate links is to use the `button` package. See Buttons. In this section, we will explain how to manually set up clickable text in a buffer, using text properties. For simplicity, we will refer to the clickable text as a link.

Implementing a link involves three separate steps: (1) indicating clickability when the mouse moves over the link; (2) making `RET` or `mouse-2` on that link do something; and (3) setting up a `follow-link` condition so that the link obeys `mouse-1-click-follows-link`.

To indicate clickability, add the `mouse-face` text property to the text of the link; then Emacs will highlight the link when the mouse moves over it. In addition, you should define a tooltip or echo area message, using the `help-echo` text property. See Special Properties. For instance, here is how Dired indicates that file names are clickable:

     (if (dired-move-to-filename)
         (add-text-properties
           (point)
           (save-excursion
             (dired-move-to-end-of-filename)
             (point))
           '(mouse-face highlight
             help-echo "mouse-2: visit this file in other window")))

To make the link clickable, bind `RET` and `mouse-2` to commands that perform the desired action. Each command should check to see whether it was called on a link, and act accordingly. For instance, Dired’s major mode keymap binds `mouse-2` to the following command:

    (defun dired-mouse-find-file-other-window (event)
      "In Dired, visit the file or directory name you click on."
      (interactive "e")
      (let ((window (posn-window (event-end event)))
            (pos (posn-point (event-end event)))
            file)
        (if (not (windowp window))
            (error "No file chosen"))
        (with-current-buffer (window-buffer window)
          (goto-char pos)
          (setq file (dired-get-file-for-visit)))
        (if (file-directory-p file)
            (or (and (cdr dired-subdir-alist)
                     (dired-goto-subdir file))
                (progn
                  (select-window window)
                  (dired-other-window file)))
          (select-window window)
          (find-file-other-window (file-name-sans-versions file t)))))

This command uses the functions `posn-window` and `posn-point` to determine where the click occurred, and `dired-get-file-for-visit` to determine which file to visit.

Instead of binding the mouse command in a major mode keymap, you can bind it within the link text, using the `keymap` text property (see Special Properties). For instance:

    (let ((map (make-sparse-keymap)))
      (define-key map [mouse-2] 'operate-this-button)
      (put-text-property link-start link-end 'keymap map))

With this method, you can easily define different commands for different links. Furthermore, the global definition of `RET` and `mouse-2` remain available for the rest of the text in the buffer.

The basic Emacs command for clicking on links is `mouse-2`. However, for compatibility with other graphical applications, Emacs also recognizes `mouse-1` clicks on links, provided the user clicks on the link quickly without moving the mouse. This behavior is controlled by the user option `mouse-1-click-follows-link`. See Mouse References in The GNU Emacs Manual.

To set up the link so that it obeys `mouse-1-click-follows-link`, you must either (1) apply a `follow-link` text or overlay property to the link text, or (2) bind the `follow-link` event to a keymap (which can be a major mode keymap or a local keymap specified via the `keymap` text property). The value of the `follow-link` property, or the binding for the `follow-link` event, acts as a condition for the link action. This condition tells Emacs two things: the circumstances under which a `mouse-1` click should be regarded as occurring inside the link, and how to compute an action code that says what to translate the `mouse-1` click into. The link action condition can be one of the following:

`mouse-face`

  If the condition is the symbol `mouse-face`, a position is inside a link if there is a non-`nil` `mouse-face` property at that position. The action code is always `t`.

  For example, here is how Info mode handles `mouse-1`:

      (define-key Info-mode-map [follow-link] 'mouse-face)

a function

  If the condition is a function, func, then a position pos is inside a link if `(func pos)` evaluates to non-`nil`. The value returned by func serves as the action code.

  For example, here is how pcvs enables `mouse-1` to follow links on file names only:

      (define-key map [follow-link]
        (lambda (pos)
          (eq (get-char-property pos 'face) 'cvs-filename-face)))

anything else

  If the condition value is anything else, then the position is inside a link and the condition itself is the action code. Clearly, you should specify this kind of condition only when applying the condition via a text or property overlay on the link text (so that it does not apply to the entire buffer).

The action code tells `mouse-1` how to follow the link:

a string or vector

  If the action code is a string or vector, the `mouse-1` event is translated into the first element of the string or vector; i.e., the action of the `mouse-1` click is the local or global binding of that character or symbol. Thus, if the action code is `"foo"`, `mouse-1` translates into `f`. If it is `[foo]`, `mouse-1` translates into `foo`.

anything else

  For any other non-`nil` action code, the `mouse-1` event is translated into a `mouse-2` event at the same position.

To define `mouse-1` to activate a button defined with `define-button-type`, give the button a `follow-link` property. The property value should be a link action condition, as described above. See Buttons. For example, here is how Help mode handles `mouse-1`:

    (define-button-type 'help-xref
      'follow-link t
      'action #'help-button-action)

To define `mouse-1` on a widget defined with `define-widget`, give the widget a `:follow-link` property. The property value should be a link action condition, as described above. For example, here is how the `link` widget specifies that a `mouse-1` click shall be translated to `RET`:

    (define-widget 'link 'item
      "An embedded link."
      :button-prefix 'widget-link-prefix
      :button-suffix 'widget-link-suffix
      :follow-link "\C-m"
      :help-echo "Follow the link."
      :format "%[%t%]")

Function: mouse-on-link-p pos

  This function returns non-`nil` if position pos in the current buffer is on a link. pos can also be a mouse event location, as returned by `event-start` (see Accessing Mouse).

Next: Fields, Previous: Lazy Properties, Up: Text Properties [Contents][Index]