Programming
the V3.0 Front End

by John M. Novak

The following material is excerpted from a draft of a chapter for the book Elements of Mathematica Programming, edited by Troels Petersen, forthcoming from TELOS/Springer-Verlag (used with permission). The full chapter expands on many of the concepts I described in my tutorial on Programming the Front End, given at the 1998 Worldwide Mathematica Conference. The excerpt that follows includes the text of the first three sections of the chapter, which provides the foundations of front end programming.

In Version 3.0, Mathematica gained a much more flexible and powerful front end than it had before. The new capabilities include typesetting, an expanded help system, and programmability, among other things. This chapter focuses on the programmability of the new front end, from the perspective of an advanced Mathematica user. In particular, the essentials of Mathematica programming, of representing an expression, and of standard entry of typeset expressions will not be addressed. The elementary output routines such as StylePrint and CellPrint will be passed over, as will many of the subtle points of typesetting. Instead, this will concentrate on the underlying expression structure of notebooks, option handling, the front end/kernel interactions, and creating buttons.

To work effectively with the front end, several features need to be understood and manipulated. The most important are:

The Notebook Expression
Notebooks are represented by a Mathematica expression. Understanding the components of that expression and being able to output them to the front end on demand is the key to front end operations.

Current Selection Manipulation
A variety of commands are available for manipulating notebook expressions that operate on the current selection, rather than treating the notebook as a fixed structure modified by part manipulation commands. These selection handling commands, also called stream manipulation commands, are the most common means of affecting the front end.

Front End Options
Portions of the notebook expression, including the individual cells, boxes within the cells, the notebook as a whole, and, for that matter, the entire front end, have various options that can be set. In many cases, it is faster to simply set an option than emit an entire notebook expression. Several commands can be used to manipulate options.

Front End/Kernel Interactions
The front end actually implements a limited evaluator that allows front end operations to be invoked directly in the front end, bypassing the kernel. Awareness of where an evaluation is occurring, and of the kernel/front end callback mechanism, is vital for advanced front end operations.

Front End Tokens
A low-level tool for working with the front end, front end tokens allow access to essentially all menu items and front end operations. However, most of these tokens are undocumented, and these function calls are more likely to be changed than others, so they should be used with care. In return, they may provide substantial performance benefits over other mechanisms.

Button Functions
Buttons are the primary graphic interface control tool in the Version 3.0 front end. However, their use for complex operations is not well documented. Knowledge of the capabilities and limitations of buttons will enable you to write powerful applications operated via a graphic interface.

Together, these tools will give you the capability to control the front end and create graphic interfaces to your applications. You should be aware that the front end is evolving; these tools are merely the first step in creating a fully general graphic interface. Many useful features, such as generalized dialog boxes and interactive graphics, remain to be implemented. In time, they will be added, making portions of this chapter obsolete; but the core concept of using Mathematica expressions for representing and manipulating the front end will remain intact.

The Notebook Expression

In Mathematica Version 2.x, a notebook was simply the main document type. In Version 3.0, the concept of the document was extended tremendously. Now, virtually any window that you can manipulate is actually a notebook; and a large number of the characteristics of a window that you can affect can be changed. For instance, the palettes of buttons are notebooks; so is the "About Mathematica…" box. The bottom portion of the Help Browser is a notebook, though because of the operations that place it in the special window, it can't be manipulated as flexibly. At this point in time, most dialog boxes are not notebooks; they are generated using operating system-specific tools, and are not open to the same degree of manipulation as a notebook.

Given this enhanced flexibility, the first question that arises is "how is a window represented"? The answer is "via a notebook expression". Since the most common use of a notebook is still the traditional hierarchical document, the notebook expression reflects this heritage. It takes the form

Notebook[{
	Cell[data, style, options],
	Cell[data, style, options],
	…
},
options
]

This is a Mathematica expression like any other. In the kernel, it is inert; that is, it doesn't evaluate to anything, so it is effectively a data type. When sent to the front end by the proper commands, it is turned into a window that you can view. Conversely, you can retrieve the notebook expression for a particular notebook window.

The separation of front end (graphic interface) and kernel (computational engine) is very important to working with notebooks. The protocol for communication between these two main parts of Mathematica is MathLink. Because MathLink does not currently use a shared memory model, it becomes necessary to have handles to notebooks that can be quickly passed over the link and used to refer to and operate on a notebook. That handle perforce becomes another data type, called the notebook object. Its syntax is:

NotebookObject[front end object, id]

The id is unique to a particular Mathematica (front end) session. The front end object is another handle, this time to the entire front end; it in turn contains a handle to the MathLink stream used to communicate with the front end. In general, you won’t need to access the internals of this object, but it is useful to be aware of it. Because the internals of this object are not important to most users, it is formatted as ⁃  NotebookObject  ⁃. The most important point is that this object is not the notebook expression, but instead a reference to a particular notebook window.

We can now tie these two data objects together by looking at a couple of the simplest commands for working with notebooks object and expressions: NotebookPut and NotebookGet. NotebookPut will transfer a notebook expression from the kernel to the front end, returning the referencing notebook object, while NotebookGet accepts a notebook object and returns the expression corresponding to that object. Here’s an example.

\(nb\  = 
      NotebookPut[
	
        Notebook[
		{Cell["A Head of a Notebook", \ 
              "Section"], 
			Cell[
              "Some descriptive text might go here.", "Text"]}
		]]
        \)

FullForm[nb]

NotebookGet[nb]

The very first thing to notice is that the front end may rearrange and add information to a notebook expression without additional input from the user. In many cases this is to optimize front end handling of a notebook; in others, it is due to specific front end features such as auto-grouping. Some options may also vary from machine to machine.

Given this foundation, we can start looking at some of the details of the notebook expression. We first look at the cell data types. As seen from the example we just executed (assuming you haven’t globally turned off automatic cell grouping), one of those data types is CellGroupData. A CellGroupData expression takes a list of subcells as its first argument, and the state of the group (Open or Closed) as the second. This data type represents the hierarchical structure of the classical notebook. Note that cells whose data types are CellGroupData expressions are the only cells that do not also have a style associated with them.

\(NotebookPut[
	
      Notebook[{
			Nest[
            Cell[CellGroupData[{Cell["Head cell", "Text"], #, #}, 
                  Open]] &, 
				Cell["A Cell", 
              "Text"], 
				5]}, 
		CellGrouping -> Manual]]
      \)

Here we begin to see the advantages of coupling the front end and kernel through expressions; it becomes very easy to write Mathematica programs that create notebooks. This simple case displays a notebook that violates the traditional nesting of a notebook’s cells by using the notebook option CellGrouping. If you experiment with variations on this little program, you will discover at least one more characteristic of the CellGroupData type; the first cell in a CellGroupData list of cells must be a ‘leaf’ cell, that is, one that does not itself contain a CellGroupData expression. This is because all cell groups must have a head cell. If you attempt to violate this convention, the front end will rearrange the grouping of your cells. The requirement for a head cell most likely derives from the need to retain a clear marker for a group when that group is closed; the head cell is displayed, often acting as a description of the closed group.

The most common cell data types are TextData and BoxData. (A simple string is also a TextData type, as we found in the first example when we wrote out a notebook and read it back in.)

A TextData wrapper is used for formatted text. This consists of paragraphs entered in much the same way as a word processor, with style modifications allowed on words. It does not directly include any two-dimensional forms (such as superscripts), however these can be handled via in-line cells. An in-line cell is a complete Cell expression embedded in a cell data type; currently, only TextData expressions support this feature. In Mathematica Version 3.1, BoxData wrappers will too. The in-line cell can contain any of the ‘leaf’ cell data types (all but CellGroupData), including the graphics data types described later.

To examine some examples of the TextData wrapper, we will use a series of unformatted cells. A significant feature of the front end that is very useful to anyone wishing to program it is the ability to examine and edit the raw cell expression for any given leaf cell. To toggle between the raw cell expression and the formatted cell, use the Format ⊳ Show Expression menu item. An unformatted cell is actually a cell whose contents are in the RawData cell data type; it takes a single argument, which is a string containing the cell expression to be formatted. In most cases, however, you won’t see this wrapper, but only the unformatted contents.

Here are some simple unformatted cells showing uses of the TextData cell type.

Cell[TextData["This is some text."], "Text"]

Cell[TextData[{"This is text ",
  StyleBox["with", FontSlant -> "Italic"], " ",
  StyleBox["style", FontWeight -> "Bold"], "."}], "Text"]

Note two things in particular about the next example: in an unformatted cell, any non-ASCII character is represented by its Mathematica “long name”, which is of the form ∖[name]. Also, note that if you edit an in-line cell in a formatted cell, the background color changes to let you know that you are working with an in-line cell. Because an in-line cell can contain any leaf data type, including TextData, an interesting use of such cells is to prevent line-breaking in a bit of text that breaks incorrectly. For example, text in “computer voice” (like this: a + b) should generally not line-break when embedded in a paragraph, yet if you simply change the style, it will be prone to breaking at the spaces. Putting it in an in-line cell will inhibit breaking. However, in-line cells are more commonly used to work with two-dimensional layout, such as the superscript in the following example.

Cell[TextData[{"Here comes an in\[Hyphen]line cell: ",
  Cell[BoxData[SuperscriptBox["a","b"]]]}], "Text"]

Here is the formatted version of the above cell:

Here comes an in-line cell: (a^b)

Any style of cell can have a TextData object. The cell style simply determines what kind of data object will be created in a new instance of that cell. This is quite useful when dealing with large cells containing Mathematica code in InputForm; because a large StandardForm cell can take a while to parse, it is sometimes useful to put code in an Input cell with text rather than boxes for faster parsing. (This can be done automatically for all new input cells by choosing the Cell ⊳ Default Input Format Type ⊳ InputForm menu item.)

Cell[TextData["Text for evaluation"], "Input"]

TextData expressions can also contain several other types of boxes: these include ButtonBox, CounterBox, and ValueBox. A ButtonBox in a text object is similar to that in a BoxData cell type, and will be described in detail later. The main difference between one in text and one in boxes is that the framed background often seen in buttons is not available. This is related to the fact that buttons in text are usually hyperlinks, and are set off from surrounding text via text formatting (e.g., colors and underlines), rather than being shown as a traditional GUI (graphic user interface) button. The box types CounterBox and ValueBox are specific to text cells, and cannot be used inside of a BoxData expression at this time. They won't be described in detail here; they are used for automatic numbering and for displaying information about a front end attribute. For example, the following cell shows the current front end version number, when formatted.

Cell[TextData[{"Here's the version: ",
    ValueBox["$FullVersion"]}], "Text"]

Here is the formatted version of the above cell:

Here's the version: $FullVersion

One way to create CounterBox and ValueBox boxes is via the Input ⊳ Create Automatic Numbering Object… and the Input ⊳ Create Value Display Object… menu items.

Each cell data type can be thought of as triggering a different kind of editor or viewer. For example, while TextData objects use a standard text editor, BoxData types specify the use of an editor for a two-dimensional layout of typeset expressions. The 2-D layout is expressed by a sequence of nested box primitives. Each box primitive has certain characteristic behaviors for layout; for instance, a SuperscriptBox takes two arguments, which are arranged with the second as a superscript to the first. In many cases, the boxes have options controlling their position or size.

While you must know the specifics of each box to produce particular effects, there are too many box types to go into full detail here. In general, however, the ways of working with all the different boxes are very similar, and don't change the way you write front end programs, so a few brief examples should give you a good starting point.

Here’s an example of a typeset cell using BoxData.

Cell[BoxData[
    RowBox[{
      SubsuperscriptBox["∫", "0", "∞"], 
      RowBox[{
        FractionBox["x", 
          RowBox[{
            SuperscriptBox["x", "3"], "+", "5"}]], 
        RowBox[{"\[DifferentialD]", "x"}]}]}]], "Input"]

Here is the formatted version of the above cell:

(∫_0^∞) (x/((x^3) + 5)) \[DifferentialD]x

You can see that RowBox primitives place things next to each other, *scriptBox object place things slightly above or below and to the side of some set of boxes, fraction boxes place one set of boxes over another with a horizontal separator, and so on. Some additional useful box primitives include FrameBox, which draws a border around its contents, AdjustmentBox, which can tweak the position of its boxes, GridBox, for arranging grids of boxes, and ButtonBox, which can be used to create an interactive interface. These are only a few of the boxes available to you, and most have various options controlling their behavior; you will need to experiment to understand the full range of layout tools open to you.

Here’s a pair of little utilities to help you better visualize boxes. When you are entering a typeset expression via palettes and control keys, you don’t have any direct exposure to the boxes involved. The first utility captures those boxes before they are turned into a Mathematica expression. This is done via a typesetting rule. The rule essentially says that when something that looks like LiteralBoxes[…] is encountered in parsing, do not parse the contents, but instead turn the expression into the actual box expression. As mentioned before, this chapter doesn’t go into the details of typesetting, so not all of the ramifications of this will be described; but note that LiteralBoxes is thus not a function, but instead a sort of typesetting wrapper, despite its appearance.

LiteralBoxes is not built into Mathematica, but is defined by the following rule.

\(MakeExpression[
        RowBox[{"LiteralBoxes", \ "[", \ boxes_, \ "]"}], \ form_]
        \  := 
	HoldComplete[boxes]\)

LiteralBoxes[a  +  (b/c)]

Next is a small utility that takes a box expression and adds frames around all the subboxes, then outputs the boxes so that they are typeset. This will help you visualize the effects of the various boxes (though somewhat distorted by the frames).

\(showBoxes[boxes_]\  := 
	
      DisplayForm[MapAll[If[Head[#] === List, \ #, \ FrameBox[#]] &, \ boxes]]
        \)

showBoxes[
      LiteralBoxes[
        (∫_a^b)  ((Sqrt[x  +  3])/(2  x))  \[DifferentialD]x]]

Together, these tools will allow you to experiment with various box layouts.

At the current time, BoxData cells cannot contain in-line cells, so certain kinds of formatting (such as aligning graphics with a GridBox) cannot yet be done. This will change in the future.

The final cell data type covered here is GraphicsData. Images, whether PostScript, bitmap, or machine-specific types (such as Macintosh PICT or Windows MetaFile) are stored in a GraphicsData object. The object takes two arguments; the first is a string denoting the type (e.g., "PostScript", "PICT"), and the second is the image data in the form of a string. Here’s a simple example of a small image. (The "Bitmap" graphics type is a Mathematica-specific format that can render on all platforms.)

Here is the formatted version of the above cell:

The data used in a GraphicsData object is not something that you will normally generate from scratch. Instead, you will most often use the function DisplayString, a Mathematica function new in V3.0 that displays to a string rather than a stream. For example, there is no option for plot commands to direct the output of a plot to a particular notebook. However, by combining DisplayString with the knowledge of the notebook format, we can write a display function that will do the job. Here’s a variation that only uses notebook commands discussed so far.

\(displaytonew[gr_]\  := 
	
      NotebookPut[
        Notebook[
          {Cell[GraphicsData["PostScript", DisplayString[gr]], 
              "Graphics"]}]]\)

Plot[Sin[x],  {x,  0,  2 π},  
      DisplayFunction  ->  displaytonew]

As an aside, note that the necessity of first rendering to PostScript will change in some future version of Mathematica, as the Graphics object will format directly as a graphic, rather than being rendered first into PostScript and then into a bitmap.

These are not all the cell data types, and more are likely to be added in the future. However, they cover most of the cases you will encounter. With these, you should be able to understand the structure of most notebooks you will work with. Many characteristics of these structures can be changed by the use of options; this will be discussed in more detail in the section on option handling.

Manipulating the Selection

Before discussing options in the parts of notebooks, it is useful for us to expand the repertoire of notebook manipulation commands. We’ve already seen NotebookPut and NotebookGet, which handle the entire notebook expression at one time; but what if we wish to deal with only a part of the notebook?

A Mathematica programmer looking at notebook expressions for the first time would naturally think of manipulating a notebook by replacing parts of the expression: replace the (5^th) cell by a new cell expression; transform the first cell group in a particular way; etc. However, recall that the notebook expression is normally in the front end, not the kernel. Further, note that it can often be difficult to determine exactly which part of the notebook one wishes to transform, due to closed cells or cell groups, for instance. Finally, manipulating the expression by parts does not have a simple parallel to the sorts of changes performed manually in a graphic interface.

Because of these limitations, the model currently implemented for notebook operations is akin to stream handling. That is, the notebook is viewed as essentially a linear sequence of objects, and manipulations occur by transforming a pointer to a location in the sequence. The location is referred to as the current selection, and corresponds to the highlighted region of a visible notebook. The entire notebook is referenced by a handle (the NotebookObject), rather than maintaining the full expression in kernel memory.

Since the current selection can correspond to a range, the parallel to streams is not exact; but it is close enough that many notebook commands mimic stream commands. For example, NotebookOpen[filename] opens the named notebook, returning a NotebookObject handle. NotebookClose[handle] closes the specified notebook, while NotebookFind[handle, data] sets the current selection to the next instance of data in the given notebook.

Given this model, you should thus first think about streams and graphic interface operations when manipulating notebooks, rather than structural transformations of the notebook expression. The exception to this is when you are dealing with notebooks small enough to be transferred to the kernel, where the entire expression is manipulated via pattern matching.

The first step in operating on a notebook is to get its handle (the notebook object). We’ve seen how this can be done with NotebookPut and NotebookOpen; but how do we find the handle for other notebooks?

The Notebooks[] command returns the handles for all open notebooks. (The following will vary depending on what you have done during a session and how many notebook windows are open.)

Notebooks[] // InputForm

Note that not all open notebooks must be visible. In fact, various notebook programming tasks depend on the current notebook being invisible (with the option Visible set to False). An invisible notebook will still appear in the Notebooks[] list.

Generally, the notebook that is frontmost (which may include a palette) is called the selected notebook. (This will actually vary a bit depending on your operating system; under NEXTSTEP, for instance, a palette is never frontmost.) The selected notebook often appears slightly different, having a colored title bar, for instance. You can get the handle to the selected notebook with the command SelectedNotebook[]. The frontmost non-palette notebook is called the input notebook, with the corresponding command InputNotebook[]. In this situation, a palette is considered to be any notebook with the WindowClickSelect option set to False. This option specifies that any click inside the window proper does not make it the frontmost window, which allows you to click on a button without obscuring the notebook that you are editing in. Most of your button functions will operate on the InputNotebook[].

Here’s a bit of demonstration code that creates a palette with a button that, when pressed, makes a new notebook showing what the handles were for the selected and input notebooks at the time of the button press. A variety of options are used here; most will be explained in detail in later sections. The key for now is to examine the notebook structures.

\(NotebookPut[
      Notebook[{
          Cell[BoxData[
              ButtonBox["Press Me", 
						
                ButtonEvaluator\  -> \ Automatic, \ 
                Active\  -> \ True, 
						
                ButtonFunction\  :> \ 
                  NotebookPut[
                    Notebook[{
										
                        Cell[ToString[FullForm[SelectedNotebook[]]], 
                          "Text"], 
										
                        Cell[ToString[FullForm[InputNotebook[]]], \ 
                          "Text"]}]]]], 
				NotebookDefault, \ 
            CellMargins\  -> \ {{0, 0}, {0, 0}}]}, 
		
        WindowClickSelect\  -> \ False, \ WindowSize\  -> \ Fit, 

        		WindowElements\  -> \ {}, \ 
        WindowFrameElements\  -> \ "CloseBox"]]\)

Two additional common handle-generating commands are EvaluationNotebook[] and ButtonNotebook[]. If an evaluation is occurring, EvaluationNotebook[] returns the triggering notebook. If a function that was started by a button is evaluating, ButtonNotebook[] will return a handle to the notebook containing the button. This is most useful for self-modifying palettes, or for modifying a cell near the button.

EvaluationNotebook[]

A handy way to experiment with notebook commands is to set up a target notebook with cells that you would like to operate on. Make that notebook frontmost, then evaluate something like nb  =  EvaluationNotebook[]. Make a selection in the target notebook, then switch to a scratch notebook and evaluate commands using nb as the handle, e.g., NotebookClose[nb]. This is particularly useful when experimenting with selection operations.

The three operations you will perform most often with a notebook are reading the current selection, writing to the current selection, and changing the current selection. The first two are handled by NotebookRead[handle] and NotebookWrite[handle, data], respectively, while changing the selection is performed most often by SelectionMove[handle, direction, scope].

NotebookRead will return different objects based on the current selection. If the selection is an insertion cursor of any type (that is, nothing is highlighted), an empty list is returned. If the selection is within a text cell, a string or a list of strings and boxes is returned. When the selection is within a box cell type, NotebookRead gives a BoxData object. If the selection is an entire cell, the whole cell expression is returned, while if a sequence of cells that is not a full cell group is selected, a list of cells is found. (An entire cell group is a cell with a CellGroupData cell data type.)

NotebookWrite will first delete the current selection if it is a range, then write out the data. If the data is a box expression, it should be wrapped with BoxData. If the insertion cursor is between cells, a new cell will be created. If it is within a cell, the data will be integrated with that cell as closely as possible (thus, if your data is a cell expression containing text, and you are writing to a text cell, the cell wrapper will be stripped and the text will be integrated with the cell). Here is a sequence of commands that creates a new notebook and writes cells to it:

\(\((nb\  = \ NotebookCreate[]; 
	
      NotebookWrite[nb, \ Cell["The First Cell", \ "Text"]]; 
	
      NotebookWrite[nb, \ "A new cell?"]; 
	
      NotebookWrite[nb, \ BoxData[SuperscriptBox["a", "b"]]])\)\)

Notice that NotebookWrite of a string acts much the same as if you had typed that string; that is, the new cell is created in the default new cell style (an input cell by default), and the insertion cursor remains in the cell. Thus, the BoxData that gets written in the last command ends up in the same cell, rather than starting yet another cell. If you want the writes to create new cells, you should start from a cell insertion cursor, then make certain all the data are wrapped in Cell expressions. So how can you make certain you are at a cell insertion cursor? Use SelectionMove!

SelectionMove has three required arguments; the notebook handle, the direction of the move, and the scope of the move. The direction can be Previous, Next, Before, After, or All; the scope can be Character, Word, Expression, CellContents, Cell, and Notebook, among others. Not all scopes are equally applicable; for instance, if the current selection is in a text cell, movement by Expression doesn’t make sense. The directions Previous and Next differ from Before and After in that the first select instances of the object named in the scope that are in front of or after the current selection, while the latter move the insertion cursor in front of or behind the nearest object named in the scope.

Here’s one way to experiment with SelectionMove. Take the following cell, and turn it into a palette via the File ⊳ Generate Palette from Selection menu item. Clicking on the buttons will then transform the current selection in the input notebook by a SelectionMove with the arguments specified by the grid position.

({{ , \(P
r
e
v
i
o
s\), \(N
e
x
t\), \(B
e
f
o
r
e\), \(A
f
t
e
r\), \(A

          l

          l\)}, {Character, 11, 21, 31, 41, 51}, {Word, 12, 22, 32, 42, 52}, {Expression, 13, 23, 33, 43, 53}, {CellContents, 14, 24, 34, 44, 54}, {Cell, 15, 25, 35, 45, 55}, {Notebook, 16, 26, 36, 46, 56}})

The SelectionMove commands emitted by the palette will work in much the same way when evaluated by your programs.

The combination of movement, reading, and writing operations within the notebook stream provide many alternatives for presenting information to and getting information from the user. For example, one sort of operation frequently desired is a sort of dialog-like data entry field. As previously mentioned, the current version of Mathematica does not generally support graphic-interface dialogs; however, the existing commands can perform manipulations that are adequate to most purposes. The following bit of code grabs information from the cell that follows it, then emits a result based on that.

\(\((SelectionMove[InputNotebook[], \ All, \ EvaluationCell]; 
      SelectionMove[InputNotebook[], Next, Cell]; 
	
      dat\  = \ NotebookRead[InputNotebook[]]; 
	
      SelectionMove[InputNotebook[], After, Cell]; 
	
      NotebookWrite[InputNotebook[], 
        dat /. {SuperscriptBox[a_, b_] :> \ SubscriptBox[a, b], 
            SubscriptBox[a_, b_] :> SuperscriptBox[a, b], 
            SubsuperscriptBox[a_, b_, c_] :> 
              SubsuperscriptBox[a, c, b]}])\)\)

(a^3) (Subscript[b, 7]) (Subscript[c, d]^e)

(Subscript[a, 3]) (b^7) (Subscript[c, e]^d)

As can be seen, the result of this program was to swap subscripts and superscripts in the cell following the code. Naturally, this sort of operation looks much nicer if the code can be hidden and control invested in a button. That procedure will be described in the section on buttons at the end of the chapter.

The code seen so far has a number of drawbacks. A key failing is that the “input field” is positioned in a specific location relative to the code or button. For some applications, variants of this are necessary; but you gain much more flexibility in layout if you don’t need to take such care in the positioning of the field. One way to do this is to tag the field somehow, and search for that tag. There are two nice ways of doing this. The first is to tag by the cell’s style, the second by a cell-level option called CellTag. Generally, the CellTag is preferred, since it is very easy to create unique tags for each cell, or use a common tag for a group of cells you wish to handle simultaneously. Tags can be assigned programmatically, using the kinds of notebook operations discussed so far, or by hand, via the Find ⊳ Add/Remove Cell Tags… menu item. Note that cell tags are also used for other purposes, such as destinations for hyperlinks, and selection of sections of a notebook for display in the Help Browser; if you perform programmatic operations for setting cell tags, be sure not to disturb tags assigned for other purposes.

Assuming that you have a cell that is tagged, how do you locate it? The relevant command is NotebookFind. NotebookFind[handle, data, dir, type] will locate the string data in the specified notebook. That data will be interpreted as being of type, which can be CellTags, CellStyle, CellContents, or CellLabel. The direction will be Next, Previous, or All. (You may notice that CellLabel is an option. What is the difference between a cell label and a cell tag? The cell label is only allowed to be a single string, while you can have a list of cell tags. Furthermore, the cell label is set by a Mathematica kernel evaluation; it is where the In[] prompt is held. This also means that for many notebooks, the cell label is automatically deleted when the notebook is closed.)

Another feature lacking in the previous example is that the operation was performed on the literal boxes. Suppose you wish to work with Mathematica’s interpretation of the boxes, rather than the formatting information itself; what do you do? Essentially, you must call the formatter yourself, via the MakeExpression command. To reverse the process, you call MakeBoxes (alternately, use ToExpression/ToBoxes if you don’t need tight control of the evaluation of the expression to be turned into an expression or boxes).

Finally, note that you can use various formatting options to make the input cell look more like an input field.

Putting these pieces together, we might have the following.

((((x^2) + 3))^4)

\(\((NotebookFind[InputNotebook[], "TheInputCell", All, 
        CellTags]; 
	
      dat\  = \ MakeExpression[First[NotebookRead[InputNotebook[]]]]; 
	
      SelectionMove[InputNotebook[], After, Cell]; 
	
      NotebookWrite[InputNotebook[], 
        Cell[BoxData[ToBoxes[Expand\  @@ \ dat]], "Output"]])\)\)

Note that the result does not overwrite the previous result. Because you aren’t going through the main evaluator for this example, the previous output cell is not automatically deleted. You should also notice that the result of MakeExpression is wrapped in HoldComplete, which must be removed to actually do a useful evaluation (handled in this case by applying Expand, rather than simply wrapping it around the result). Be careful if you have more than one of these in a notebook; this particular code will fail if there are multiple cells with a cell tag "MyInputCell".

This assortment of examples has given you the flavor of front end stream manipulation. The keys are the selection changing functions SelectionMove and NotebookFind. Combined with the basic ability to read and write to the selection given by NotebookRead and NotebookWrite, you can assemble a large number of notebook manipulations.

Front End Options

Many valuable operations can be handled by changing attributes of a selection, rather than reading in an entire cell or notebook and transforming its expression directly. There are a large number of attributes, such as font color and size; cell characteristics such as the bracket, frame, tags, and label; aspects of a notebook such as window position and size, as well as the style sheet; and globals for the entire front end such as message behavior and paths. Almost all options can be found somewhere in the option inspector, and modified by hand; but there is a standard assortment of functions for doing the same programmatically.

The main operations you may want to perform with options include determining and setting current option values. These are done with the functions Options and SetOptions. These functions also need a handle to the option being checked; in the case of front end globals, this is $FrontEnd. For example, here is the value of the StartupSound option, which dictates whether a sound is played when Mathematica is started.

Options[$FrontEnd, StartupSound]

Multiple options can be examined by providing their names in a list. Here is the ScreenRectangle option, which is the virtual screen size for the front end (generally inherited from the screen size set by the system), and the FrontEndStackSize, which gives the size of the internal stack.

Options[$FrontEnd,  {ScreenRectangle,  FrontEndStackSize}]

To change option values, use the SetOptions function. In the following evaluation, the StartupSoundOption is set to False. (Don’t forget to reset it to True after trying this!) SetOptions changes the option value, and returns a list of all options set to non-default values. (Shallow is used here to reduce the amount of visible output, though the value stored in the command history is complete.)

SetOptions[$FrontEnd, StartupSound -> False] // Shallow

Unlike SetOptions applied to a kernel function, the change made to the state of $FrontEnd persists between sessions. In the kernel, setting the default value for an option currently only pertains to the running session; you also cannot return to the original default value for an option unless you store it first. In the front end, a changed option will remain in the new state when you restart the program; furthermore, you can use the option inspector to return an option to its default value (clicking the small button that appears to the left of a set option in the inspector returns it to the default state).

Also contrary to kernel design is the front end’s behavior of returning only the non-default options from functions like Options. This design is questionable, so for now it should not be depended on to remain the same. However, you can use the current behavior as a visual diagnostic to determine what options don’t have default values. Notice that some options are always considered to have non-default settings.

Another option behavior that varies from the kernel relates to what could be called “superoptions”, which encapsulate other options. One example is the MessageOptions option, which controls behaviors relating to informing the user of an error. (StyleForm is also used in the following example in conjunction with the ShowStringCharacters option to control the behavior of quotes in the output; it demonstrates another way in which options may be set. StyleForm allows options that dictate formatting to be set for the output expression. The result of Options is also being stored in a variable mopt for later use.)

StyleForm[mopt  =  Options[$FrontEnd,  MessageOptions], 
      ShowStringCharacters  ->  Rule]

These superoptions, like MessageOptions, generally have a name ending in 'Options' or 'Settings'. This superoption syntax is occasionally used in kernel applications; the main difference here is that the front end suboptions are all named with strings instead of symbols, and that SetOptions will allow you to change one of these suboptions individually, rather than forcing the setting of the option to affect all the suboptions simultaneously.

For example, the following is very useful in dialog box style operations. If you have a standalone window with a button-triggered evaluation, an error may be emitted and be displayed in the notebook being used as a dialog, distorting the display. One solution is for your evaluation to temporarily reset the "KernelMessageAction" suboption to MessageOptions to redirect the error message to a separate messages window.

SetOptions[$FrontEnd, 
      MessageOptions 
        -> {"KernelMessageAction" -> {"PrintToConsole"}}];

Here is a an evaluation that emits a kernel error message. The message is not placed between input and output in the usual fashion, but instead shows up in the messages window.

1/0

Note that the other suboptions are unaffected.

Options[$FrontEnd,  MessageOptions]

Peculiarly, SetOptions does not accept a list of options, but it will accept a sequence of options. Here is one way to reset the options from values obtained by Options.

SetOptions[$FrontEnd, ##] &  @@  mopt;

Now the kernel messages behave as before.

1/0

Options can be set on several levels other than that of the entire front end. For example, a common assortment of options are set on the level of an entire notebook. In terms of the notebook expression, these options are saved at the end of the expression (much like the options in a kernel Graphics object). Thus, when using functions that send an entire notebook expression, you can include the options explicitly. In addition, NotebookCreate accepts options to be applied to the generated notebook.

nb = NotebookCreate[Background  ->  Hue[0]]

Options and SetOptions are used much as before.

Options[nb,  Background]

SetOptions[nb,  Background  ->  Hue[0.3]]

Notice the front end’s behavior of changing some objects to a canonical form; in this case, the color used has been converted to an RGBColor expression.

Some of the most useful of these options from a programming perspective are those described as notebook and window properties. This includes attributes such as the editability of the window, as well as its size, color, position, and selectability, among others. The bulk of the options found under the “Notebook Options” section of the Option Inspector are generally set only at the notebook level.

Start with a notebook’s size and position. This is determined by two options: WindowMargins and WindowSize. The WindowMargins are given as two pairs of values, corresponding to left-right margins and bottom-top margins. (This syntax is used for positioning other notebook elements, so you should be familiar with it.) This gives us: WindowMargins -> {{left, right}, {bottom, top}}. The unit of measure is points; this generally corresponds to pixels. The distance is measured from the specified side of the screen. In principle, we can specify both size and position with this syntax; however, it is frequently more convenient to specify a window size explicitly, and give position with respect to a particular corner of the screen. (In some versions of Mathematica, implementation issues require that we use such a syntax.) In that case, the value used for a margin to be determined by the system is Automatic. (If all four values are Automatic, the window is centered on the screen.) Window size is given by a {width, height} pair, also in points. Here are a few examples:

A window 10 points from the left side of the screen, and 10 points from the bottom:

NotebookCreate[
      WindowMargins  ->  {{10, Automatic}, {10, Automatic}}];

The next is partially off the screen; 20 points off the left side, 10 points from the top. (Note that under Windows, the top of the screen is given from the bottom of the menu bar, while the bottom is with respect to the taskbar.)

NotebookCreate[
      WindowMargins  ->  {{-20, Automatic}, {Automatic, 10}}];

A 200 by 200 point window centered on the screen:

\(\(NotebookCreate[WindowSize\  -> \ {200, 200}, 

      WindowMargins\  -> 
        \ {{Automatic, Automatic}, {Automatic, Automatic}}]; \)\)

Other variations include using the value Fit for a window size, which adjusts the windows dimensions with respect to its contents. This is common with palette notebooks, which display only a grid of buttons.

Options permeate all levels of a notebook, and can be set on individual cells or on parts of the contents of a cell. If you are generating the entire object in question, it is easy to see how to set an option; but suppose you wish to change a setting without rewriting the entire object? There is not an obvious handle to a part of a notebook, the way there is to the entire front end or an individual notebook. The answer is a wrapper called NotebookSelection. Wrapped around a notebook object, it represents the current selection of the notebook in question. The encapsulated object can then be used in functions like SetOptions.

Many of the options that can be set at the level of an entire cell can also be set on parts of the cell; these include things like font attributes such as family, style, size, and color. Cell-specific options can also be used to control the style (or visibility) of the cell bracket, as well as various labels such as the cell frame labels, cell dingbat, and cell label (which corresponds to the In[]/Out[] tags in input and output cells).

Here, for example, the current selection is moved to enclose the evaluating cell, and the font color is changed to red:

(SelectionMove[EvaluationNotebook[],  All,  EvaluationCell]; 
      SetOptions[NotebookSelection[EvaluationNotebook[]],  
        FontColor  ->  Hue[0]])

We can start putting some of these pieces together to create useful tools. For example, a simple “progress dialog” can be made using the commands shown so far combined with an array of options.

The basic idea of a graphic progress indicator is to have a bar that incrementally grows the width of the window as progress is made in some task. This naturally breaks up into three steps: opening the window, incrementing the progress dialog, and closing the window when done.

Here is the code for the first step:

\(CreateProgress[desctext_String: "Progress :"]\  := 
	
      NotebookPut[
		Notebook[
          {
				Cell[desctext, \ 
              "Text", 
				FontWeight\  -> \ "Bold", 
              FontSize\  -> \ 18], 
				Cell[
              "", 
					CellTags\  -> \ 
                "ProgressBar", 
					CellFrame\  -> \ 
                True, 
					Background\  -> \ 
                Hue[0], 
					CellFrameMargins\  -> \ 
                {{0, 0}, {Inherited, Inherited}}, 
					CellMargins\  
                -> \ 
                {{10, 190}, \ 
                  {Inherited, Inherited}}]}, 
			ShowCellBracket\  
            -> \ False, 
			WindowSize\  -> \ 
            {200, \ 100}, 
			WindowMargins\  -> \ 
            {{Automatic, Automatic}, 
              {Automatic, 10}}, \ 
			WindowTitle\  -> \ 
            "Progress", 
			WindowElements\  -> \ {}]]\)

Two approaches to creating the dialog might have been reasonable here: first, opening an empty notebook and writing the contents to it via NotebookWrite; or second, writing the entire starting notebook at once via NotebookPut. The advantages of this second approach are clear, as it allows us to clearly specify all the parts in a single operation. Here, all the various starting options are set explicitly in the data objects affected; there is no need to apply SetOptions separately.

The first cell in this window displays the progress bar text, which may be passed as the first argument to CreateProgress. Here, I explicitly specify some font options determining how the text is to be displayed; a better approach is to establish a custom style sheet, and refer to a particular style, but that adds complexity, though it greatly improves modularity and maintainability of the code.

The next cell displays the bar. This is handled here by having the bar rendered as the interior of an empty cell. The background of the cell falls inside what is called the cell frame; as the left and right margins of the cell are adjusted, the block of color inside the frame changes size. The margins are specified in much the same manner as WindowMargins.  The CellMargins specifies the distance from the sides of the page (by default, the sides of the window), and the distance from the adjacent cells on top and bottom (or more accurately, the distance to the boundary specified by the adjacent cells margins). The value Inherited in these options means ‘inherit the default value from the cell style’. The CellFrameMargins specifies the boundaries around the text inside the cell frame. The color of the background is given by the Background option. We tag the cell with the CellTags option, for use in relocating the cell, as in examples earlier in the chapter.

Note that cell options may be specified at the level of the entire notebook, in which case they apply to every cell in the notebook. If the option is specified again on a particular cell, the option at the cell level takes precedence over the one at the notebook level. Here, the ShowCellBracket option is set to False to hide the cell brackets.

The WindowSize and WindowMargins options behave much as before. In this case, the window is 200 points across by 100 points high, centered horizontally on the screen, but 10 points from the top of the screen. The WindowTitle option specifies what shows in the top of the window’s frame, if the windowing system allows it for the particular style of frame. If this option is Automatic, it shows "Untitled-n" or the file name; if None, it displays no title. Otherwise, it is usually set to a specific string, as in this case. Finally, setting the WindowElements option to {} gets rid of the various adornments around the window, such as scroll bars, status area, etc.

Next, we need a routine to adjust the size of the bar to some particular percentage specified as input. For simplicity, the following code has the window’s width and margins input as arguments (with hard-coded defaults), though it could be adjusted to read these values from the progress notebook itself. The algorithm is simple: NotebookFind is used to locate the progress bar in the specified notebook, and the cell margins are reset to a value determined from the percentage, width of the window, and margins of the bar at 100%. Again, for brevity, error checking was ignored in this case. (For example, the routine should properly verify that the window still exists before acting on it, perhaps by determining if the notebook object nb is a member of the list returned by Notebooks[].

\(AdjustProgress[nb_NotebookObject, perc_, \ wid_:  200, 
        lrmargins_:  10]\  := 
	
      \((NotebookFind[nb, "ProgressBar", \ All, \ 
          CellTags]; 
		SetOptions[
          NotebookSelection[nb], 
			CellMargins\  -> \ 
            {{lrmargins, \ 
                wid \((1 - perc)\)\  + \ lrmargins\ \((2\ perc\  - \ 1)\)}, 
              {Inherited, Inherited}}])\)\)

Finally, we close the progress dialog via a NotebookClose. The Interactive->False option is used so that the front end does not ask the user if she wishes to save the dialog notebook (an effect of the dialog being simply another notebook…)

CloseProgress[nb_NotebookObject]  := 
      NotebookClose[nb,  Interactive  ->  False]

Here we evaluate the routines:

progressdialog  =  CreateProgress[]

Adjust the width of the progress bar in 10% increments, pausing a second between each adjustment:

Do[AdjustProgress[progressdialog, n]; Pause[1], {n, 0, 1, 0.1}]

CloseProgress[progressdialog]

In practice, you might use this by scattering AdjustProgress evaluations throughout a long operation.

It is evident that a large number of enhancements could be made to this code, improving the style of the window and the behavior of the progress bar; but this prototype suffices to demonstrate a number of useful concepts in option handling.

Unfortunately, this chapter cannot contain a definitive listing of options; there are enough for an entire book! For example, there are many options associated with the various types of boxes in typeset cells, as well as StyleBox wrappers in text cells. The best way to find out more about these at the current time is to experiment with the option inspector, remembering to also examine the raw cell expressions.

The remaining sections of the actual text expand the above material significantly. The most important addition involves detailed handling of buttons and button functions, with several substantial examples. The book Elements of Mathematica Programming contains the full chapter, as well as a number of other chapters with valuable information for the Mathematica programmer.