# Programming with jedit

## Introduction

This document describes aspects of `jedit` relevant to Tk/Tcl programmers and advanced `jedit` users, including how to write new editing modes (or modify existing ones) and how to incorporate `jedit` windows in your own applications.

## Writing Editing Modes

(This section isn't quite finished, but it should have enough to get you started, if you also look at the code for existing editing modes, especially `hook-mode`, which is a demonstration of the various facilities available.)

You create a new editing mode by creating a file called mode-mode.tcl, where mode is the name of the new mode. This file can live in any of the directories ~/.tk/jeditmodes, `$tk_library`/jedit/jeditmodes, or `$tk_library`/jeditmodes. (The directories are searched in that order.)

### Initialisation and Preferences

The bare minimum a new mode file should have in it is a procedure `mode:` mode`:init` (where mode is the name of the mode) which contains code like the following, to set modespecific preferences`:`

`proc mode:vmail:init { t } {`

global MODEPREFS

j:read_prefs -array MODEPREFS -prefix vmail \

-directory ~/.tk/jeditmodes -file vmail-defaults {

{textfont default}

{textwidth 80}

{textheight 24}

{textwrap char}

{sabbrev 0}

{dabbrev 0}

{autobreak 1}

{autoindent 0}

{savestate 0}

{buttonbar 1}

`{buttons {`

jedit:cmd:done

jedit:cmd:save

}}

{menu,editor 1}

{menu,file 1}

{menu,edit 1}

{menu,prefs 0}

{menu,abbrev 1}

{menu,filter 1}

{menu,format 0}

{menu,display 0}

{menu,mode1 1}

{menu,mode2 1}

{menu,user 1}

}

}

(Actually, you can leave out the preferences you don't want to override in your mode in the list you give to `j:read_prefs`.) This code fragment sets the defaults for the various modespecific preferences for your new mode, although a user can still override them (for this particular mode) on the ModeSpecific Preferences panel.

Note that the `mode:` mode`:init` procedure gets an argument t. That's the name of the text widget whose mode is being set, so you can do things like configure tags or add bindings if you need to.

If you need to refer to the widget's toplevel (for instance, to add new userinterface components), you can do that with the command ``jedit:text_to_top $t`'. If you do anything in `mode:` mode`:init` that shouldn't persist if the user changes modes, you should also provide a procedure called `mode:` mode`:cleanup` (which will also be called with the text widget as its sole argument) to undo your changes. For instance, if you pack an entry for the user to type a cryptographic key into the toplevel window in `mode:crypt:init`, you should create a `mode:crypt:cleanup` procedure to remove it. (Both procedures would need to use `jedit:text_to_top` to get the name of the toplevel window.)

### NaturalLanguage Strings

If your mode defines any new strings to display to the user, you should define them at the top of the mode file with `j:ldb:set_defaults`. (At present there's no support for a modespecific naturallanguage database file separate from the `jedit` database, so your translations will need to be specified explicitly in the mode file, merged into the `jedit` database file, or stored in your users' personal database files. I hope to support modespecific databases in the future.)

### Buttonbars

You can specify the buttons that are to appear on the buttonbar for your mode by providing them as the ``buttons`' default when you read in your modespecific preferences with `j:read_prefs` in your initialisation procedure. Currently there's no user interface to allow your users to override this default; this, too, is on my agenda for a future version.

Alternatively, to create a custom buttonbar, you can define a procedure called `mode:` mode`:mkbuttons` with code similar to the following:

`proc mode:vmail:mkbuttons { w t } {`

j:buttonbar $w -pady 2 -buttons [format {

{send { } {jedit:cmd:done %s}}

{sign { } {mode:vmail:insert_sig %s}}

} $t $t]

$w.b configure -bitmap @/some/path/send.xbm

$w.b configure -bitmap @/some/path/sign.xbm

return $w

}

This procedure gets both the name of the toplevel widget and the name of the text widget. It should create the buttonbar as a child of the toplevel and return its path, but not pack it itself. This method lets you create buttons that don't correspond to registered commands, or buttons that don't look like normal jedit buttons.

### Procedure Names

I recommend that the names of any procedures you define in your mode file start with ``mode:` mode`:'`, where mode is the name of the mode. At present, nothing enforces this, but it will help ensure that your mode doesn't conflict with other modes. (Remember, the user can have more than one `jedit` window open at a time, and they can be in different modes). Future versions of `jedit` may require this naming convention.

### `Hook' Procedures

You can further customise the behaviour of the editor in your new mode by creating any of the following procedures:

`mode:` mode`:pre_read_hook` filename`` widget``

mode: mode`:read` filename`` widget``

mode: mode`:post_read_hook` filename`` widget``

mode: mode`:pre_write_hook` filename`` widget``

mode: mode`:write` filename`` widget``

mode: mode`:post_write_hook` filename`` widget``

mode: mode`:pre_quit_hook` widget``

mode: mode`:quit` widget``

mode: mode`:pre_close_hook` widget``

mode: mode`:close` widget``

mode: mode`:pre_spacebar_hook` widget``

mode: mode`:spacebar` widget``

mode: mode`:post_spacebar_hook` widget``

mode: mode`:pre_tabkey_hook` widget``

mode: mode`:tabkey` widget``

mode: mode`:post_tabkey_hook` widget``

mode: mode`:pre_returnkey_hook` widget``

mode: mode`:returnkey` widget``

mode: mode`:post_returnkey_hook` widget``

mode: mode`:pre_paste_hook` widget``

mode: mode`:post_paste_hook` widget``

mode: mode`:pre_xpaste_hook` widget``

mode: mode`:post_xpaste_hook` widget``

mode: mode`:autoindent` widget``

If a `mode:` mode`:pre_`...`_hook` or `mode:` mode`:post_`...`_hook` procedure is defined, it will be called before or after the relevant action. If one of the procedures without ``_hook`' is defined, it will be called instead of the default action.

Note that the behaviour of the `Close' command and it's corresponding modespecific procedures is linked to that of the `Quit' command and it's corresponding modespecific procedures. If there is only one window open, choosing `Close' is exactly like choosing `Quit', and will trigger `mode:` mode`:pre_quit_hook` and `mode:` mode`:quit` if they exist rather than the corresponding `Close' hooks. The `mode:` mode`:pre_close_hook` and `mode:` mode`:close` procedures are called only when there's more than one window open. This means that if you define them, you usually want to take similar actions in the corresponding `Quit' hooks.

### Registering New Commands

If you define new commands in your mode, you should register them using the `j:command:register` procedure. This allows you to use the commands in menus created with `j:menu:commands`.

### ModeSpecific Menus

If you want to create a special menu for your `jedit` mode, you can create a procedure called `mode:` mode`:mkmenu1`, where mode is the name of your mode. It will receive two arguments: the name of a menubutton to create, and the name of the `jedit` text widget this menu is attached to. As an example, here's the procedure that define's the `vmail` mode's `Mail' menu:

`##################################################`

# define the Vmail menu:

##################################################

proc mode:vmail:mkmenu1 { menu t } {

j:menubutton $menu $menu.m {Vmail}

j:menu:commands $menu $t {

mode:vmail:insert_sig

jedit:cmd:done

}

}

(In this example, `mode:vmail:insert_sig` is a procedure defined elsewhere in the mail-mode.tcl file.) It's easiest to create menus with the procedures in the jmenu.tcl library, but you can create them however you like.

Note that the `mode:` mode`:mkmenu1` procedure has to create the menubutton itself (but not pack it), and it has no control over the name of the menubutton widget.

If you want to create two modespecific menus, you can also define a `mode:` mode`:mkmenu2` procedure; the requirements are the same as for `mode:` mode`:mkmenu1`.

### Command Accelerators

I don't yet have a mechanism for automatically applying modespecific command accelerators using the information provided by `j:ldb:binding`. In fact, if you provide bindings in the string database for modespecific commands, they may be applied to other windows, in different modes. For now, I recommend using explicit Tk `bind`(n) commands in `mode:` mode`:init` to set up modespecific command accelerators. I also recommend using Meta-1 through Meta-9 for modespecific command accelerators if possible; they're reserved for that purpose, so they won't conflict with other bindings, and they're automatically cleared when the user switches modes.

## Embedding jedit in Other Applications

Almost all the functionality of `jedit` is in libraries, so if you write your own Tkbased applications, you can easily incorporate `jedit` into them. You need to include a little bit of code at the beginning of your application to allow it to use the jstools libraries; this is described in the Usage section of their documentation. Essentially, you need to make sure the `auto_path` Tcl variable is set properly to find the libraries. Then you can say

`jedit:jedit` [`-window` w]`` [`-mode` mode]`` [`-line` line]`` [`-file` filename]

to edit a file (or open a new empty document window, if you don't specify ``-file` filename').

If the window w doesn't already exist, it will be created as a new toplevel window. (If you don't specify it, an arbitrary window name will be chosen.) It's fine for w to exist already, though; that way you can include an entire `jedit` window inside another window.

If you specify mode, the window will be in that mode; otherwise `jedit` will guess the mode based on the filename.

If you specify mode and also define all the necessary procedures for that mode in your code before calling `jedit:jedit` (such as `mode:` mode`:init`), you can tightly constrain the behaviour of the resulting `jedit` window, essentially creating a custom mode for your application without having to provide a separate mode file to define it.

If you specify line (and it's a valid number), then the insert point will be positioned at the beginning of the lineth line of text after the file is loaded; this is used to implement the `vi`style `+`linenumber commandline syntax that lots of applications expect in an editor (e.g. to position the cursor at the beginning of the body, rather than the beginning of the header, of a mail or news message.)

``