Using FrameWork and TextEdit


In this document we use the FrameWork and TextEdit modules to create a simple text editor. The functionality of the editor is very basic: you can open multiple files, type text and use cut/copy/paste. The main intention is to explain the use of FrameWork, really.

FrameWork

The FrameWork module provides you with a skeleton application. It declares a number of classes suitable for subclassing in your application, thereby releaving you of the burden of doing all event handling, etc. yourself. For a real understanding you will have to browse the source. Here is a short overview of the classes and what functionality they provide.
Application
This is the toplevel class you will override. It maintains the menu bar and contains the main event handling code. Normal use is to override the __init__ routine to do your own initializations and override makeusermenus to create your menus (your menu callback routines may be here too, but this is by no means necessary). The event handling code can be overridden at various levels, from very low-level (the dispatch method) to intermedeate level (do_keyDown, for instance) to high-level (do_key). The application class knows about the Window objects you create, and will forward events to the appropriate window (So, normally you would have a do_key method in your window object, not your application object).
MenuBar, Menu and MenuItem
These classes (and a few friends like SubMenu) handle your menus. You would not normally override them but use them as-is. The idiom for creating menus is a bit strange, see the test code at the bottom of FrameWork for sample use. The apple menu is handled for you by MenuBar and Application.
Window
The basic window. Again, a class that you normally subclass in your application, possibly multiple times if you have different types of windows. The init call instantiates the data structure but actually opening the window is delayed until you call open. Your open method should call do_postopen to let the base class handle linking in to the application object. Similarly with close and do_postclose. The rest of the code is mainly event-oriented: you override do_postresize, do_contentclick, do_update, do_activate and do_key to "do your thing". When these methods are called the relevant environment has been setup (like BeginDrawing has been called for updates, etc).
windowbounds
Not a class but a function: you pass it a width and height and it will return you a rectangle you can use to create your window. It will take care of staggering windows and it will try to fit the window on the screen (but the resulting rect will always have the size you specify).
ControlsWindow
A subclass of Window which automatically handles drawing and clicking for controls. You override the same methods as for Window (if you need to: control-related things are done automatically) and do_controlhit.
ScrolledWindow
A subclass of ControlsWindow, a window with optional scrollbars. If you override do_activate or do_postresize you must call the ScrolledWindow methods at the end of your override. You call scrollbars to enable/disable scrollbars and updatescrollbars to update them. You provide getscrollbarvalues to return the current x/y values (a helper method scalebarvalues is available) and scrollbarcallback to update your display after the user has used the scrollbars.
DialogWindow
A modeless dialog window initialized from a DLOG resource. See the second Interslip example for its useage.

A sample text editor

Let us have a look at ped.py (in the Demo:textedit folder), the Pathetic EDitor. It has multiple windows, cut/copy/paste and keyboard input, but that is about all. It looks as if you can resize the window but it does not work. Still, it serves as an example. We will improve on ped later, in a waste-based example.

Ped creates two classes, TEWindow and Ped. Let us start with the latter one, which is a subclass of FrameWork.Application and our main application. The init function has little to do aside from the standard init: it remembers a window sequence number (for untitled windows), and sets things up for menu disable to work. Remember, the makeusermenus is called automatically.

Makeusermenus creates the File and Edit menus. It also initializes a couple of lists that are used later to correctly enable and disable menu items (and complete menus) depending on whether a window is open, text is selected, etc. The callback functions for the menu items are all methods of this class.

Updatemenubar handles greying out (and re-enabling) of menu items depending on whether there is a current window and its state.

The rest of the methods are all callbacks and simple to understand. They check whether there is an active window (and complain loudly if there is none: the corresponding menu entry should have been disabled in that case!) and call the appropriate window method. Only the _open method (the common code for Open and New) deserves some mention. It instantiates a TEWindow object and opens it with the title, filename and contents of the file to edit. Note that FrameWork takes care of remembering the window object. A minor note on opening the file in binary mode: this is because TextEdit expects MacOS style carriage-return terminated lines, not python/unix/C style newline-terminated lines.

Oh yes: the quit callback does a little magic too. It closes all windows, and only if this succeeds it actually quits. This gives the user a chance to cancel the operation if some files are unsaved. Quitting itself is also a bit strange: you raise self to break out of the main loop. This bit of idiom was invented by Guido, so blame him:-).

Lastly, there is the idle method, called by the Application base class when no event is available. It is forwarded to the active window, so it can blink the text caret.

The TEWindow object handles a single window. Due to this structuring it is absolutely no problem to have multiple windows open at the same time (although a real application should exercise care when two windows refer to the same document). TEWindow uses the standard init code inherited from ScrolledWindow, and sets itself up at the time of the open call. It obtains screen coordinates, opens the window, creates rectangles for TextEdit to work in (the magical number 15 here is the size of a normal scroll bar: unfortunately there is no symbolic constant for it), creates the TextEdit object and initializes it with our data. Finally, the scroll bars are created (the initial values will be obtained automatically through getscrollbarvalues) and we activate ourselves (this is unfortunately not done automatically by the MacOS event handling code).

Do_idle simply calls the TextEdit routine that blinks the cursor. Getscrollbarvalues returns the current X and Y scrollbar values, scaled to 0..32767. For X we return None, which means "no scrollbar, please", for Y we use the scaler provided by ScrolledWindow.

Scrollbar_callback is called when the user uses the scrollbar. It is passed a string 'x' or 'y', one of 'set', '-', '--', '+', '++' and (for set) an absolute value. Note that the sign of the value passed to TEPinScroll is counter-intuitive.

do_activate (de)activates the scrollbars and calls the relevant TextEdit routine. Moreover, it tells the application object if we are now the active window, and updates the menubar. The next few methods are update and menu callbacks, and pretty straightforward. Note that do_close can return without closing the window (if the document is changed and the users cancels out of the operation). Also note the "magic" in menu_save_as that set the correct window title.

Things get moderately interesting again at the cut/copy/paste handling, since the TextEdit scrap is separate from the desktop scrap. For that reason there are various calls to routines that move the scrap back and forth. Have_selection is called by the menubar update code to determine whether cut and copy should be enabled.

Understanding the main program is left as an exercise to the reader.


That's all for this example, you could now continue with the next example, where we use WASTE, a more-or-less TextEdit compatible library with more functionality, to rebuild our editor. Or you can return to the table of contents to pick another topic.