Whyteboard - Help for developers
Version 0.41.1 - document last updated: 1 May 2010
---------------------------------

This document serves to help a developer who wishes to contribute / understand
Whyteboard's code.
You'll probably need some knowledge of Python + the wxPython GUI toolkit, as
Whyteboard is developed with these, using some advanced-ish concepts.

----------------------------------


The main class is GUI in gui.py. It consists of a wx.Frame - a "window" to the
general end-user, containing the title and close/minimise button etc. It has
references to its contained panels - the control panel on the left, menu bar,
tool bar, the canvas itself and the panel on the right.

The gui contains the global event bindings, e.g. undo/redo, copy/paste. It
dispatches function calls to relevant classes, for example undo will tell the
current tab to undo its last operation, Or, saving a file will involve prompting
the user for the location to save the file to, and then call a function in the
utility class to actually save the data.

Since the program is tabbed, the gui keeps a reference to its active canvas.
The gui updates its canvas reference when opening/closing/changing tabs. Often,
we want to perform operations only on the current tab, e.g paste an image into a
tab, export the current tab as an image and so on. Thus knowing the current tab
is useful.

The Canvas contains the drawable canvas - a scrollable window. It has its own
event bindings and no title bar. Multiple Canvases will exists at a given time,
and the GUI presents these views to the user through its tab list, thumbnails
panel and the Notes tree.

The Canvas class contains a list of shapes that has been drawn upon it. Each
instance of this class will have its own unique shapes list. This is how each
Whyteboard tab can perform its own undo and redo without affecting any other
Whyteboard instances.

Tools.py defines the shapes - different drawable tools that the program uses.
At the bottom of the file is a list named "items" -- this is the list of tools
the user can draw with. From this, buttons, icons, translated names, hotkeys
are generated. Here are some interesting methods/properties each shape provides:

 - name - translated name
 - hotkey - keyboard shortcut key
 - left_up() - called when drawing - left button pressed
 - left_down() - left button up
 - double_click() - double clicked
 - right_up() - right button up
 - motion() - mouse motion while left mouse button is held down
 - hit_test() - a check if an x/y point is "inside" the shape
 - load() - called when loading the shape
 - save() - when saving
 - properties() - a text description of the shapes' properties, e.g. x/y coords
 - preview() - tool preview drawn in the left panel

These allow you to customise your tools easily. There is an object hierarchy, as
so:

                     Tool
                      |
      ---------------------------------------------
      |               |              |            |
      Eyedrop   OverlayShape      Select        Media
                      |
  -------------------------------------------------------------------------
  |         |          |                                |        |        |
 Image   Rectangle   Circle                          Polygon    Text    Line
            |                                           |        |        |
       --------------------------------------          Pen      Note    Arrow
       |               |                    |           |
    BitmapSelect     RoundedRectangle    Ellipse    ----------
                                                    |        |
                                                 Eraser   Highlighter


Many of the classes at the bottom of the list simply extend the base classes'
behaviour - nested deeply in the hierarchy, they have many inherited methods.
Most of these classes invoke their superclasses' methods. For example, draw() in
OverlayShape will draw the shape overlayed on the canvas, meaning it is not
permanently drawn on the canvas as the user moves their mouse around. Note needs
to extend this behaviour by also drawing a background to the shape -- so first
calls draw() on its superclass, Text, which will draw the text, and invokes
draw() on OverlayShape.

So, to create your own tool you'd need to extend from an existing class (most
likely OverlayShape) and override the appropriate methods, remembering to call
super() on its parent. Also, add your class to the items list at the bottom of
tools.py, making sure the filename defined in its "icon" attribute exists under
/images/tools/[icon].png


The file "meta" contains read-only metadata used by the program. This may seem
a nasty way of having global data, but in Python, but the values are read-only
and the module isn't tied to any classes. It contains common data such as the
version number, configuration scheme, list of translators, whether the host
system supports transparency etc.

----------------------------------

The source files are organised as follows:

lib/           - third party modules
fakewidgets/   - mock GUI classes for unit testing

canvas.py      - canvas class
dialogs.py     - dialogs and their events, e.g. Progresss bar / resize / history
event_ids.py   - custom wxPython IDs for events
functions.py   - misc. stand-alone functions that can be used anywhere
gui.py         - main program frame, wx.App
meta.py        - meta data, meant to be dependent on wxPython only
panels.py      - control panel, notes, thumnbails
preferences.py - preferences dialog
tools.py       - class definitions for the drawing tools
unitests.py    - unit tests
utility.py     - a class the GUI references that performs saving/loading files,
                 getting file paths and misc. things

------------------------------------------

CODING PRACTICES

* Try and follow Python's Pep 8 guide http://www.python.org/dev/peps/pep-0008
* 80 characters per line can be hard to use at times and can be broken in some
  cases.
* Name methods like_this, not AsSuch. As wxPython uses CamelCased method names,
  it easily allows you to differentiate between wx and Whyteboard methods. Very
  useful for dialogs and custom panels.

* Try and write a unit test for your functionality to assert it works correctly
* Learn to use PubSub. It will help to decouple objects and even modules'
  dependencies on each other which is always a very good thing, and allows for
  easier testing
* Be wary of performance issues when performing loops.
* Python has a high overhead for functions calls so please be wary of that, too.

* Translate every string like _("so"). Make sure to use variable placeholders
  instead of string concatination as this gives translators more context

  _("The value is %i and its name is %s") % (x, name)
  is better than
  _("The value is ") + x + _(" and its name is ") + name