BBEdit Plug-In Programming
Volume Number: 14
Issue Number: 6
Column Tag: Tools Of The Trade
BBEdit Plug-In Programming
by Steve Sheets
Creating Extensions for Bare Bones Software's BBEdit Text
Editor
The Right Tool for the Right Job
As a well known starship engineer would say, "always use the right tool for the right
job". It's true for dilithium chambers and it is true for Macintosh development. When
working with resources, ResEdit and Resorcerer are the best tools available.
Hypercard and Macromedia Director have been the favorites of two generations of
multimedia authors. Part of what makes these tools so powerful is the potential to
customize them through ResEdit templates, Resorcerer "apprentice" plug-ins,
Hypercard XCMDs, and Director Xtras. This plug-in extendibility let's you customize
and focus their features to turn a great tool into the perfect tool for your job. The text
editor BBEdit is one of the best at what it does and its basic functions can also be
extended with third party BBEdit extensions. This article will describe how to create
your own BBEdit extensions so you can apply the power of BBEdit to the unique
challenges in your projects.
The Right Tool for the Right Editing
Bare Bones Software's BBEdit is a powerful text editor. A text editor is not a word
processor (where the output is intended to be a printed page). BBEdit is just for text
files -- the source file format for almost all compilers and interpreters. Many
developers prefer using BBEdit or another third party editor instead of the text tool
built into standard development environments (like Apple's MPW, Metrowerk's
CodeWarrior, Symantec C++ or Symantec's Visual Cafe). This preference has become
wide spread enough that most IDEs now have explicit support for external editors.
BBEdit is also a popular tool for web page design (text being the source format of all
HTML web pages) or any other job that requires text manipulation.
While the built in features of BBEdit are impressive to begin with, BBEdit has an
additional method to add functions -- the extensions API. Each plug-in is a code
resource, of type 'BBXT', that is loaded in, executed and unloaded when the user selects
the corresponding item in the extension menu. Bare Bones Software uses the API
themselves and BBEdit ships with a couple dozen plug-ins.
When executed, the plug-ins can do any number of text manipulation tasks. The
plug-in can even have limited user interaction, as in the case of modal dialogs or
Standard File dialogs. However, the plug-in does not have access to any global memory
nor to any settings of the BBEdit application itself.
A plug-in also has access to a large list of callback services. These services are
functions that Bare Bones Software provides to do common tasks such as accessing or
changing the currently selected text, creating or opening new windows, and creating
message, progress or error windows. Additional functions make it easy to handle undo,
AppleEvents and grep type commands.
Beyond these user interface and single use limitations, BBEdit plug-ins can do almost
anything with or to a text file. A plug-in can be a simple tool to automate some changes
in the selected text, or it can be a sophisticated compiler (C++, Java, or Resource)
that processes a text file into another format. The only limit is the developers
imagination.
This article will explain the format of a BBEdit plug-in (resource and file). The
extension interface will be described, as will the plug-in control flags resource.
Several, but not all, of the callback services will be described. Two examples of BBEdit
plug-ins are included for you to learn from. Bare Bones Software provides a complete
SDK with all commercial versions of their software and you can download this kit from
the BareBones web site at http://www.barebones.com/. The SDK includes a manual
explaining all the calls, as well as the required interface files.
Extension Resource and File
A BBEdit plug-in is an old fashion code resource, like an desk accessory, FKEY or menu
proc. The code resource can be a traditional 68K resource or an accelerated PowerPC
code resource. The code resource can even be a 'fat' code resource that contains both.
For most developers, a 68K version of their plug-in is best since it can run on both
platforms. You only need to create a PowerPC version to take advantage of the increased
speed of native code.
Any number of plug-ins can exist inside a single BBEdit plug-in file. This file must
reside inside the "BBEdit Plug-Ins" folder, which is located beside the BBEdit
Application. The file can also reside in a sub folder in the BBEdit Plug-ins folder,
which would cause the plug-in to appear inside a submenu in the application. This is a
good way to group plug-ins. This nesting only goes down one level. The BBEdit file must
be of file type 'BBXT'. It's creator type can be anything (allowing custom icon bundles)
but if the type is 'R*ch', then the file will have the generic BBEdit plug-in icon.
BBEdit will work correctly if aliases to the actual plug-in file are placed in the
plug-ins folder.
A BBEdit plug-in is a code resource of type 'BBXT'. The ID can be any number, while
the name should describe the plug-in. BBEdit will use this name when placing the
plug-in in the extension menu. BBEdit opens the plug-in file as a resource file when
the call is executing, so the plug-in can access other resources in the file.
Along with the code resource, the plug-in file can contain an optional "plug-in control
flags" resource. This resource is of type 'BBXF' and has the same ID as the plug-in it is
associated with. The resource contains 4 bytes of data, each bit signifying a specific
flag. The easiest way to create and modify this resource is with the "BBXF" resource
template provided by Bare Bones Software in their SDK. A Rez resource header would
have been nice, but is not currently available. The flags resource provides the BBEdit
application with information about what the plug-in does, and when it can be invoked.
These flags will be explained in more detail below. Technically, this flag resource is
not required for an old style plug-in, but is necessary if you wish to signify that your
plug-in is a new style one.
Calling Conventions
The original or old calling convention had a pascal style function as the main entry
point. The function was passed a callback pointer, and the window pointer of the top
most window. BBEdit version 3.5 and later support a newer calling convention. While
the older convention remains, it has all but been replaced by the new format. The new
format provides more information, as well as supporting Apple Events. The new style
does require the BBEdit application to run under System 7.0, but this is no longer
very limiting. The new style calling convention has a main entry point defined as:
pascal OSErr main(ExternalCallbackBlock *callbacks,
WindowPtr w, long flags, AppleEvent *event, AppleEvent *reply);
The routine returns an OSErr result indicating the errors, if any. If the plug-in runs
successfully, this should be set to noErr (0). The callback parameter points to a
structure used by the BBEdit application and the BBEdit callback routines. While the
plug-in has access to the structure directly, the structure may change in future
revisions of the application. Therefore, it is strongly recommend you only access the
callback using the callback services routines or macros.
The w parameter is the WindowPtr of the top most window, while the flags parameter
provides information about the applications current state. The flag parameter is a
combination of logically ORed flags, each flag having an associated mask defined for it.
The xfWindowOpen flag will be set if the front window is a edit or text window. If the
xfWindowChangable flag is set, the text in the front window can be modified. If the
xfHasSelection window is set, then the front window has some text selected. This text
can be accessed directly from the callback services routines. The xfUseDefault flag
allows the plug-in to invoke a non-user version of the plug-in. It will only be set if
the correct plug-in control flag is set, and the user holds down the option key when
invoking the plug-in. In this case, the plug-in should not place up a dialog or provide
any other user interface. If settings are required, use the default setting of the plug-in
(what ever you want to make them). The xfIsBBEditLite and the xfIsBBEditDemo flags
indicate if the BBEdit application is a Lite version (non-commercial) or a demo
version. The last two flags, xfWindowHasMailer and xfWindowHasActiveMailer relate
to the unsuccessful Apple PowerTalk Mail system. If the xfWindowHasMailer flag is
set, then the front window contains an PowerTalk mailer. In that case, if the
xfWindowHasActiveMailer flag is also set, then the mailer is currently active.
If the plug-in is invoked from an AppleEvent (or OSA Script), then the event and reply
parameters are the standard AE Event Handler parameters. If the AE parameters are
not NIL (for example, if an Apple Event was used to invoke the plug-in), then the
plug-in does not require user interaction (no modal dialog). The script may be invoked
by someone across the network. To allow the plug-in to be scriptable, a standard 'aete'
resource with the same ID as the plug-in is needed in the plug-in file. This 'aete'
resource can only support one AE suite, but can have any number of events as you
want. Bare Bones Software suggests using their BBEdit extension suite.
Extension Control Flags
The plug-in control flags provides information to the BBEdit application about your
plug-in. This information allows BBEdit to determine whether to enable or disable
your extensions menu item depending on the state of the application. For example, if
the plug-in requires PowerPC flag is set, then the plug-in will be disabled when it is
run on a 68K Macintosh.
The first three flags deal with handling an undoable plug-in. Bare Bones Software
provides several callback services that make undoing most text editing plug-ins
trivial. To signify that the extension know about undoing it's actions, the undo-savvy
flag needs to be set. If the Can Be Undone flag is also set, then BBEdit expects your
plug-in to use the callback routines to handle this. Otherwise, if this flag is not set,
when the user selects the menu item, an "This action can not be undone -- do you wish
to continue?" alert will appear when the menu item is selected. The plug-in is only
called if the user then clicks "Yes". To disable this alert, set the "Can't Undo" alert
flag.
The next five flags control whether or not the plug-in is active in the menu. The
Requires Non-Empty Window flag indicates the plug-in is disabled if there is no top
most window, or if the top most window has no text in it. The Requires Changeable
Window indicates the plug-in is disabled if there is no top most window, or if the
window is read only. The Requires Edit Window requires exactly that before the
plug-in is enabled. The Requires Selection flag indicates the top most window is there,
and some text is selected. The Requires PowerPC flag was already explained.
The last four flags are miscellaneous ones. The Support New Interface flag tells the
BBEdit application if the plug-in support the new or old conventions. For this article,
I strongly recommend setting this flag, and only using the new style. The Use Option
Key for Default flag means the user can hold the option key down to invoke the plug-in
with the default setting. This flag is only supported on the new style interface. The
Place on "Internet" Menu flag indicates where the plug-in wishes to be placed in the
menubar. Assuming the user has an Internet menu and this flag is set, the plug-in is
place on the Internet menu. The Is a Tool flag indicates the BBEdit plug-in is a BBEdit
Tool
BBEdit Tools are beyond the scope of this article. Tools are special plug-ins that
function very similarly to old fashion desk accessories. They provide dragable,
resizeable, floating windows that are drawn by the plug-in, and can handle mouse down
events in the window. BBEdit Tools also support Drag and Drop. Finally, they have
access to global memory and are persistent. The Bare Bones Software's SDK provides
more information, including examples, of BBEdit Tools.
Callback Services
BBEdit callback services are an essential part of the API. If the callback services
routines did not exist, then the BBEdit plug-ins would not be more than application
specific FKEYs. Using callbacks gives a plug-in complete control of the text in the
application. All callback routines begin with 'bbxt', making them easy to identify in
your code. These routines use the Pascal calling conventions and pass the main
function's callback parameter as their first parameter. For example, you pass the
bbxtGetCallbackVersion routine the single callback parameter, and it returns the
version number of the BBEdit API. Using this, you can insure which callback routine
is available for a given version of the application (check the header and
documentation).
Several routines use either windowptrs as parameters or pass them as results. These
are standard Macintosh windowptrs. The plug-in itself receives the windowptr of the
top most active window. If the window's Kind field is of type userKind, then the window
is a standard BBEdit text window. Be aware that there are a number of other window
kinds. Use the callback routines, or the control flags, to make sure your plug-in is
only invoked when the correct window is on top.
The first set of callback routines duplicate the standard feature of the Edit window.
Since an plug-in can both pass data into and take data out of the clipboard, this is a good
method to modify the text in a window. These routines include bbxtCopy, bbxtPaste,
and bbxtDelete. Other text editing routines include bbxtGetSelection and
bbxtSetSelection which can be used to get and set the selection of the text in a window.
The bbxtGetWindowContent call can used to directly access the text in a window, while
bbxtSetWindowContent can be used to change it. If you use the bbxtGetWindowContent
call to examine the text handle, and then change the content of that text handle, then
you should call the bbxtContentsChanged routine to inform the application of the
changes. The bbxtInsert call can be used to place text into the current selection space
of a window. Between these routines, you can control of changes to the text in a window.
The routines bbxtGetLastLine, bbxtGetLineNumber, bbxtGetLinePos, bbxtGetLineStart,
and bbxtGetLineEnd can be used to find the number of lines in a window, the line
number of a specific character, the offset of a specific line and the offset of a given
character from the beginning or end of it's line. These functions can be useful in
formatting lines of text.
Bare Bones Software also provides several file io routines. Given the file name,
volume id and directory id, bbxtGetFileText will load that file into a given handle. The
bbxtOpenFile call loads a text file into a standard edit window, and returns the
windowptr of that window. The bbxtGetFolder call displays a Standard File dialog for
choosing a folder (I wish the Mac OS would provide that call). The bbxtOpenSeveral
call provides a Standard File dialog for multiple selections of files (another one Apple
could do to duplicate). The bbxtNewDocument, bbxtOpenDocument and bbxtSave calls
invoke the same actions as if the user had selected the items from the File menu. Many
of these calls return the WindowPtr of the created window, which can be then used to
view or modify the text. You may also want information about a given window/file. The
bbxtGetDocInfo call can return the name, volume id and directory id of a window/file,
while bbxtGetModDate returns the last modification date of the file/window. For those
who wish to use CodeWarrior or Think project files, bbxtGetProjectList will parse
these files for you, while bbxtProjectTextList will generate a textual listing of the
project document's content. Finally, bbxtOpenFileByName will open the specified file,
using the same search logic as the "Open Selection Command".