Wednesday, January 30, 2013

Catching Line Changes (Combining Lines)

There are two key commands that can cause a line to be deleted (but there is currently no key command to delete a line).  One is deleting when the cursor is at the end of the line, where the current line will be combined with the next, the next line will be deleted.  The other is backspacing when the cursor is at the beginning of the line, where the current line will be deleted, but the contents of the line will be combined with the previous line.

For the delete check, where the cursor is at the end of the line, it can't be at the end of the file (no line will be deleted) and there can't be any text selected (selections will be handled separately).  For the backspace check, where the cursor is at the beginning of the line, it can't be at the beginning of the file (nothing to backspace to) and there can't be any text selected.

Both of these will emit the lines deleted signal for one line, delete will send the current line number plus one and backspace will send the current line number.  Both of these signals are sent before the actual deletion takes place in the edit box.  When the document changed signal is received, the current modified line number will be set (when the cursor leave the line, it will be reported as changed).

[commit 8cf236c8d3]

Tuesday, January 29, 2013

Separate Program Change Signals

One of the next items to handle are selections of text in the edit box where multiple lines selected may be cut of deleted, or multiple lines may be pasted.  A single line change slot, even with a type, is not sufficient to handle a line changed, lines inserted and lines deleted without some involved code.

Therefore, there will be three signals, the current line changed signal (with a number and text of the line), a new lines inserted inserted (with a number and of list of strings of the lines), and a new lines deleted signal (with a number and count of the number of lines).

For now, there is only the line changed signal and lines inserted signal (for one line) and the equivalent slots in MainWindow.  The lines deleted signal will be implemented next when lines are combined due to deleting at the end of the line or backspacing at the beginning of the line.

[commit 1226e81ca4]

Monday, January 28, 2013

Catching Line Changes (New Lines)

So far the edit box is catching lines that are modified including newly inserted lines.  One of the ways that new lines are inserted is the Return key (at end of the line) or Control+Return key (splitting a line).  The edit box needs to indicate that line has been inserted and not modified, so the line changed signal was modified to report the type of change, modified, inserted or deleted.  A new function was implemented for inserting a new line into the program called for both Return keys.

When the cursor is at the end of the line, a new blank line will be inserted on the next line.  If the current line is not modified, this line does not need to be emitted since it hasn't changed.  However, if the line has been modified, it will be emitted as a modified line.  For the new blank line, all that needs to be done is the line number recorded and the new modified line type set to inserted.  The line will be emitted as a new line when the cursor leaves the line.

When the cursor is in the middle of the line (splitting the line), the line will be emitted since it is being truncated.  For the new line, the end part of the line that is now on the new line, the line number is recorded and the new modified line type set to inserted.  The line will be emitted as a new line when the cursor leaves the line.

[commit b1db6d29b0]

Sunday, January 27, 2013

Catching Line Changes (Undo/Redo)

Detecting modified lines properly with undo and redo required some effort.  The undo and redo functions were reimplemented in the EditBox class so that these operations could be controlled.  The key press event handler was modified to catch the undo and redo key sequences and to call these reimplemented functions.  A new slot was added to catch when a new undo command is added to the undo queue. This was necessary to catch when a line is modified due to an undo command.

To properly detect line changes, there are several things that undo and redo need to keep track of.  No changes are detected until the cursor has been moved from the line, so if all the changes are undone, then no line change gets detected.  Therefore, the number of changes made to a line are counted, where this count is decremented for an undo and incremented for a redo.  Only upon leaving the line does change get detected if this count is not zero.

Once a modified line is detect, if a further undo returns the cursor to the line, each undo is counted, so that when the cursor leaves the line, the modified line (from the undo) gets detected.  Any redo operation while the cursor is on the line adjust the line modification count accordingly.

The source code can be studied for exactly the steps required to accomplish this, which consists of various checks throughout.  A new line changed signal was added to EditBox, so modified lines are now emitted with the line number.  A slot was added to the MainWindow class that is connected to this signal.  For now, this slot function simply outputs the modified line number and text to the console.

There is still a problem when undoing entire lines where lines are actually deleted by the undo.  These changes will need to be emitted as line deletions.  The same signal can be used, but somehow needs to indicate the line is being delete.  There are similar issues when two lines are combined (the second line needs to be deleted), or when cutting, pasting or deleting multiple line selections.  There also needs to be a way to indicate when new lines are added.

[commit 6112a7ffd8] [commit f941edae59]

Saturday, January 26, 2013

Edit Box – Custom Context Menu

The Undo and Redo commands require extra work to track line changes they cause.  These commands need to be intercepted before the operation is performed, because the wrong lines are detected as being changed.  There are three ways that these commands are activated, by their actions (Edit menu and tool bar), keys (Control+Z and Control+Y) and from the default context menu of the edit box.

The actions can be caught by reimplementing the undo() and redo() functions in the EditBox class.  The keys can be caught by adding checks for the key sequences in the key press event handler.  But the context menu can not be caught (at least none that I could find).

Instead of using the default context menu, a custom context menu will be used for EditBox.   The necessary actions are already present.  A context menu can easily be added either by adding each action individually or creating a list of actions and then adding the list.  The later method was used and after adding the actions, plus the desired menu separators, the setContextMenuPolicy() function is used to tell EditBox to use these actions instead of the default context menu.  A side benefit of the custom context menu is that the items have the icons assigned to the actions (the default context menu does not display any icons).

[commit f3fe01028e]

Friday, January 25, 2013

Catching Line Changes (Part 2)

There are a number of different keys that could change the position of the cursor that needs to check if the line before the movement was modified including Page Up, Page Down, Return (non-insert move to next line), Return (at end of line), Control+Return (insert new line), Left (if at beginning of the line), Right (if at end of the line), Control+Left (if at beginning of the line), Control+Right (if at end of the line), Control+Home (if not at the first line), Control+End (if not at the last line), and the list probably goes on (like simply clicking the mouse on another line).  In any case, a lot of keys to check and special conditions for many (line if at beginning of the line).

I realized there was a much easier way to catch these cursor movements by using the cursor position changed signal emitted by QTextEdit.  Therefore, this signal was connected to a new cursor moved slot in EditBox.  This slot checks if the current line has been modified (value not ‑1) and if this line number is different than the current line number of where the cursor was moved to, then the capture modified line function is called.

However, this conflicted with the current line reporting mechanism, which was previously called before the cursor was moved, but now was being called after the cursor had moved.  So instead of getting the current line (block) number from the text cursor, the current line (block) was found by calling the document's findBlockByNumber() function using the line modified variable value.

These changes work for most of the cursor movement commands, but there still some commands that are not handled properly like Undo, Redo, Control+A (Select All) after a modification, delete operations that combine lines (where the line below being joined needs to be treated as a delete line), and operations involving multiple line changes (like cutting a multiple line selection or pasting a block of lines).

[commit 2f0042faff]

Catching Line Changes (Part 1)

If this was a regular compiler, a plain text editor would be sufficient.  The program would be entered, the user would click run, the text would be compiled and then run.  However, this is an incremental compiler where each line as it is entered or modified needs to be compiled immediately.  Therefore, the edit needs to catch each line as it is entered or modified.

The document (QTextDocument) of the QTextEdit class provides two signals when it has changed, one when any change is made and one with the specific change made by position and number of characters removed or added.  Neither of these are sufficient as one has no detail and the other too much detail.  What is needed is when a line has been modified and when the cursor has moved away from this line.  It is not necessary to compile the line as each character is entered or deleted.

To accomplish this, a new member variable was added to EditBox that will hold the line number of the last line that was modified and will be initialized to ‑1 to indicate no line has been modified.  The document's contents changed signal is connected to a new document changed slot in EditBox.  Using this variable and slot, the steps for catching line changes are:
  1. In the new document changed slot, the current line number that the cursor is at is recorded in the new line modified member variable.
  2. If there is a request to move the cursor from the current line, the contents of the current line is obtained (which will eventually be compiled).
  3. The line modified variable will be reset to ‑1.
  4. The cursor is then moved.
To start only the up and down arrow keys are detected.  A new function for capturing the modified line was added to EditBox since there will be multiple situations that will move the cursor.  For testing, the number and the contents of the line are output to the console to verify that it is was working.  Eventually a signal will be emitted containing the line number and contents.

There was a minor problem that needed to be corrected.  The line modified variable was initialized to ‑1 in the constructor.  When a program was loaded, this triggered the contents of the document to changed and this signal was emitted causing it to record line 0 with a change.  To correct this, when the document modified flag is reset, the line modified variable was also reset, so a new EditBox function was added to do both of these operations.

Eventually upon loading a program, the entire program will need to be compiled line-by-line.  Now that the basic line detection mechanism is working, the detection of additional cursor movements can be implemented.

[commit 2e3e29c6fb]

Thursday, January 24, 2013

New Return Key Behavior

Now that the edit box has its own key press event handler, the desired behavior of the Return key can be completed.  The Return key will only insert a new line if the cursor is at the end of line, except if the line is blank, otherwise it will just move the cursor to the next line.  The Control+Return can be used to always insert a new line.

To determine if the cursor is at the end of a non-blank line, the text cursor of the edit box is used.  The text cursor holds the position of the cursor and is also used to make selections and to modify  the document of the edit box.  Since the text cursor will probably by used for many of the new key sequences that will be implemented, a copy of it is obtained at the beginning of the key press event handler.  Any changes made to this copy won't show up in the edit box unless the edit box's cursor is updated (the textCursor() access function only returns a copy of the cursor).

The text cursor contains the functions atBlockEnd() and atBlockBegin() for determining if the cursor is at the end or beginning of the line.  For a normal text document, blocks are the same as lines.  So, if the cursor is not at the end of a block or at the beginning of a block, the cursor is moved to the beginning of the next block using the moveCursor() function with the NextBlock argument.  The modified cursor is put back into the edit box and the handler returns.

This created a problem for the Control+Return key code that created a new Return key event.  When this event came back to the handler, it went through the at end of block check and was treated as a Return key and only moved to the next line.  To correct this, the new event creation code was replaced with a call to the insertText() function of the text cursor with a string containing a new line character.  The handler then returns.

[commit 29159082ec]

Key Press Event Handler

Upon reading up on events, event filters and event handlers, I realized installing an event filter to handle additional key press events was not the best solution.  There are actually several levels at which events can be monitored and intercepted and event filters are the third level up.

The lowest level are the specific event handler functions for a widget, each of which handle a specific type of event.  The next level is the event handler for a widget that receives all types of events.  The third level is any installed event filter for the widget in backwards order of when they were installed.  There are several more levels above this that won't be detailed here.  The real purpose of event filters is for another class, like a parent widget, to monitor and intercept the events of several widgets.

Therefore, the event filter function was replaced with a key press event handler function.  This function will receive only key press events.  Instead of an if statement for checking for the Return or Enter keys, a switch statement was used since other keys will need to be handled.  Again, if the key is a Control+Return a new unmodified Return key press event is created and posted.  Unlike the event filter function, an event handler function just returns.  Being at the lowest level, there are no additional routines that will be called for the event.  Similar to the event filter, all unprocessed key press events are passed on to the base QTextEdit class key press event handler.

[commit 241fa39814]

Wednesday, January 23, 2013

Keyboard Event Filter

One of the tasks for the edit box is to intercept keys before they are processed by the QTextEdit base class so that additional programming related keyboard commands can be implemented.  This is accomplished by installing a custom event filter function to the widget.  An installed event filter is called before the doing the regular event processing.  An event filter can be a member of any class and be installed to any widget.  Because I don't see a reason not to, the EditBox class was given an event filter and it is installed to itself in the constructor:
installEventFilter(this);
The argument of this function call is the class containing the event filter, which in this case is the instance itself.  The event filter contains an argument of the object receiving the event and the event itself.  In this case, since the event filter was only installed for this instance of the edit box, the object will be the instance itself and there is no need to check it.

The first key to modify is the Return key.  For a text editor, the Return key simply inserts a new line.  It is desired that a Return key enter the current line into the program (parse, translate, etc.) no matter where on the line the cursor is at.  In other words, not to break the line into two lines at the cursor.  However, if the cursor is at the end of the line, it will open a new line (insert a blank line) on the next line.  To allow a line to be broken into two, the Control+Return key will be used.  The Control+Return is ignored by the QTextEdit class.  So the first behavior implemented in the new event filter was to treat a Control+Return key as a Return key.

The event filter receives all event types, so the event type is first checked for key press event.  The event pointer is then cast to a QKeyEvent so that the specifics of the key press event can be checked starting with if it contains the Return key.  Since the Enter key on the numeric keypad is received as a different code, both the Return and Enter values are checked for.  Finally the keyboard modifiers (Control, Shift, Alt, etc.) of the key press event is checked for a control modifier. 

The Control+Return key needs to be changed to a regular Return to allow the new line insertion to take place, but the key event can't be modified and passed on.  Instead, a new key press event is created for a unmodified Return key and this new event is posted by calling the QApplication::postEvent() static function with the edit box instance pointer (this) for the new event.  A true is returned to indicate that the Control+Return key event has been processed.  If the event was not a key press Control+Return event, the event is passed through to the regular event filter by calling QTextEdit::eventFilter().

[commit cb32f9ab0e]

Tuesday, January 22, 2013

Cross-Platform Fixed Width Font

There are several solutions for setting the font in the edit box to a fixed width font, but a cross-platform solution was needed.  One solution would be to use a font that is available on both Windows and Linux, for example Courier, but this is not the best looking font, though it is one of the few available on Windows.  My current preferred fixed width font is Monospace, but this font is not available on Windows.  Attempting to set this font had no effect on Windows, and the default proportional font was used.

After some research, the correct sequence was found to select the Monospace font on Linux and to select an alternate fixed width font on Windows.  First the current font is obtained from the QTextEdit base class.  The font is set to fixed pitch to indicate the type of font desired if the selected font family is not found.  The font family is set to the "Monospace" string and the style hint is set to Monospace.  Finally the current font is set to this modified font.  This code was put into the EditBox constructor:
QFont font = currentFont();
font.setFixedPitch(true);
font.setFamily("Monospace");
font.setStyleHint(QFont::Monospace);
setCurrentFont(font);
On Windows (XP, 7 and 8), this appears to select the Courier New font at a fairly small 8 point font, which doesn't look so good on XP, but better on 7 and 8.  The size of the Monospace font on Linux also looks to be an 8 point font.  This is good enough for the now and eventually a font selection dialog will be added so that any font and size can be selected.

[commit 7155b30097]

Monday, January 21, 2013

Minor Fix

While testing the tagged download archive for release 0.3.1, a warning message from Qt occurred indicating that the window title did not contain a '[*]' placeholder.  This was caused by the application loading the last opened file, after which the window modified flag is cleared.  This triggers Qt to update the window title, which was not set yet, hence the warning message.

This warning did not occur if a file was specified on the command line.  If the application was started with no prior loaded file or recent list, this message also did not occur, but the window title was set to "MainWindow" instead of the correct "Untitled - IBCP" title.

The warning did not occur if the file was specified on the command line because the code called the set current program function with the file specified, which set the window title before the file was loaded.  The solution was to also call the set current program function if no file was specified on the command with the current program member variable value, which solved both the warning message problem and the "MainWindow" title problem.

[commit 6410a5318e]

Edit Box For Program Entry

Now that some additional GUI has been implemented, it is time to start to work on the edit box to make it more appropriate for editing a program and not just text.  The immediate goal is to accept BASIC code that is currently recognized by the parser and translator, parse and translate it and display the translation in a dock widget beside the edit box.  (A dock widget is a widget that can be moved or docked to any side of the application window or detached completely from the window.)

For an incremental compiler, the application will need to know when a line has been edited (when the user leaves a modified line) so that the line can be recompiled (for now translated), which could be several lines (by a deletion when multiple lines were selected, or a paste of several lines).  Also, the font of the edit box needs to be changed to a fixed width font and there needs to be a way to add color, for example, indicating errors and syntax highlighting of BASIC keywords and other program elements.

As seen so far, this will be accomplished with the text cursor that is part of the QTextEdit's document.  The document part of QTextEdit is one continuous string of characters, but there is a way to retrieve and replace individual lines within the document.  All this needs to be figured out, which will take place over the next several commits.

But first, with additional GUI implemented and working, this is a good place to tag for v0.3.1.  The various files (CMake, read me and release notes) were updated for this development release.

[commit a938626ffc]

GUI – Pasting Plain Text

By default, the QTextEdit class that EditBox is derived from accepts rich text (formatted text).  The solution for this matter was simply to call the setAcceptRichText() function with false as the argument to prevent pasting rich text.  This call was added to the constructor of EditBox.

While adding this to the constructor, it was noticed that the EditBox class was essentially empty, deriving from QTextEdit adding no new functionality.  The delete and select all action functions in MainWindow did more that just call a functions in edit box, so this code was moved to new functions in EditBox.  Since delete is a reserved name, the delete text function was named remove().

[commit 4050620cae]

Sunday, January 20, 2013

GUI – Edit Actions – Tool Bar

Adding the actions (icons) to the tool bar was simply a matter of dragging the actions from the Action Editor at the bottom of Designer to the tool bar.  Separators were added between the file actions and the cut, copy, paste and delete actions and the undo and redo actions.

While testing the edit actions, it was noticed that the edit box currently accepts formatted text with the paste action (for example, copying an italicized word from a word processor and pasting it into the program).  This will be corrected in the next commit.

[commit 24c21711b9]

GUI – Edit Actions - Enabling

For enabling the undo and redo actions, there are signals from the edit box (derived from the QTextEdit class) when the undo or redo availability changes.  Likewise, there is a signal for when the copy selection availability changes.  The undo, redo, cut, copy and delete actions were set to disabled in Designer.

The edit box's undo and redo signals are connected to the undo and redo set enabled slot respectively.  When an undo or redo is available, this signal is emitted to the action with a true value, which enables the action.  Likewise when an undo or redo is no longer available, this signal is emitted to the action with a false value, which disables the action.

The edit box's copy available signal is connected to the cut, copy and delete set enabled slot.  The copy available simply means that text has been selected, which can then be copied, but also cut or deleted.

[commit d9a3f3b0b7]

GUI – Edit Menu - Actions

The edit box already has a context (right-click) menu with Undo, Redo, Cut, Copy, Paste, Delete and Select All.  Applications customarily also have these same actions on the Edit menu and on the tool bar.  These actions were created in Designer after adding the Edit menu and adding each of these edit actions.  Once the actions were created, each was edited to add an icon, shortcut key and status tip.

The slot functions for each action was implemented in the MainWindow class with the names on_actionName_triggered so that these would automatically be connected to the actions.  The implementation of most of these functions was a single line that calls the appropriate member function of the EditBox instance (undo, redo, cut, copy, and paste).  The delete and select all actions required slightly different handling.

For delete, there is no function to delete the currently selected text in the QTextEdit class that EditBox is derived from.  The QTextCursor member of QTextEdit, which controls the cursor and selection of QTextEdit, must be used.  The textCursor() access function is used to obtain the text cursor of EditBox.  For deleting the current selected, the removeSelectText() function of QTextCursor is called:
m_editBox->textCursor().removeSelectedText();
The select all action required a little more handling.  The QTextCursor has a way of selecting text by using the select() function with an argument to specify what to select, with QTextCursor::Document being the option for selecting all of the text.  However, the select() function can't be called directly using the textCursor() access function like with delete because the selection doesn't get back to the document inside the edit box.  Instead, the text cursor needs to be obtained, the selection made and the text cursor put back with the setTextCursor() access function:
QTextCursor textCursor = m_editBox->textCursor();
textCursor.select(QTextCursor::Document);
m_editBox->setTextCursor(textCursor);
Additional functionality is required to enable and disable these actions when appropriate.  For example, cut and copy should only be enabled if text is currently selected.  The context menu already works like this.  This functionality will be the subject of the next commit.

[commit d8c2933481]

GUI – Recent Files List – Status Tips

I realized that no status tips were given to the open recent menu actions.  Therefore a status tip was added for the Clear Recent List action in Designer.  Sub-menu actions can not be given status tips (they can be assigned, but they don't show up when hovering over the sub-menu because the sub-menu is opened).

Status tips were also assigned to each of the open recent file actions added to the sub-menu with a string that simply says "Open" followed by the full path of the file name.

The code was also changed to use the QstringList::at() function to access the files in the file list member variable instead of using the [] operator.  The reason is that the at() function is more efficient because the [] operator returns a modifiable reference to the item (an lvalue), where as the at() function just returns a reference (an rvalue).

[commit 858bbe1e28]

GUI – Current Directory

While the application is running, it will remember the directory of the last file loaded so when the open file dialog is displayed again, that directory will be the starting directory.  This application's current working directory is updated by the open file dialog after a file is selected.  A "." for the current directory is used as the starting directory passed to the open file dialog box, so when the application is restarted, the current directory starts back to where the application was started from.  The application should remember the last directory when restarted.

To have the application remember the last directory, a new current directory member variable was added to the MainWindow class.  After a new file is loaded from the Open menu action, the directory of the file path is saved in this variable.  This variable is then saved as part of the application settings and restored when the application is started.

As was done for the restore and save settings for the RecentFiles source file, the names of the settings were put into constant character strings so that the strings are not specified in two different places (the restore and save functions) to prevent the possibility of a mistake in the name between the two functions, which would cause a setting to not be restored properly.

[commit 04c28a8252]

Saturday, January 19, 2013

GUI – Recent Files List

The next GUI feature implemented is a recent programs list, which consists of a new Open Recent sub-menu item on the File menu below the Open menu item.  This sub-menu contains a list of recent programs along with a separator and a Clear Recent List menu item.  The Open Recent menu item is disabled if there are not recent files.  Files are added to the recent list when opened or when the Save As... menu item is used.

New files are inserted at the top of the list, pushing older programs down the list.  Currently only 4 programs are listed in the list (this will be made configurable later), but up to a  maximum of 10 recent programs are kept in the list.  When a new program is added to the list, any existing entry of the program in the list is removed first and any files above the maximum count are removed.

A new RecentFiles class was implemented to maintain this list of recent files and the Open Recent sub-menu actions.  When instanced, the open recent file actions are created for the maximum number of files, sets each to invisible, sets its icon to the file open icon and connects their triggered signal to an open file slot (which does not occur automatically like when menu actions are added in Designer).  The open recent file actions are inserted before the first existing action in the menu, which is assumed to contain a separator and the Clear Recent List actions (added in Designer).

The RecentFiles class contains public functions for adding a file to the recent list, clearing the list, restoring the list (and count) from the saved application settings, and saving the list (and count) to the application settings.  When one of the recent file menu actions is triggered, an open file signal is emitted.  When MainWindow instances this class, it connects this signal to a new program open slot function.

The RecentFiles class contains two private support functions.  One to update the actions on the sub-menu list, which first removes any files in the list that don't exist, and then scans the list setting the open recent file menu action's text and data to the base name of the file path and its full path, and makes it visible.  Any actions above the current count or the number of files present are set invisible.  The second is a simple support function for returning the base file name of a path.

[commit bf8cda57eb]

Tuesday, January 1, 2013

GUI – Program File Argument

The next feature added is the ability to specify a program file to load on the command line.  This is specifically to support integrating the application with the OS where files with a certain extension can be associated with the application.

The CommandLine class was modified to look for a single argument that does not begin with a "-" option character if none of the other expected options were found.  This argument is stored into a new file name member variable, which has an access function to get its value.  The usage help message was updated for the new argument.

The MainWindow constructor was modified to check if the file name from the CommandLine instance is not empty.  If it is not empty, then the current program is set to this file name overriding any in the saved settings.  If this program doesn't exist or there is an error loading the program, then the application starts up blank but with the current program name set to the file specified instead of being Untitled.

[commit c8f48aa9d5]

GUI – Save Current Program

Now, that the basic GUI elements are in place, some additional features will be added before beginning work in changing the edit box from a simple text editor into a program editor.  The first feature to add is to have the application save the program file path name that was loaded the last time it was run.  This involves saving the current program path as part of the saved settings.

When the settings are restored, the set current program helper function is called with the restored program name.  The current program member variable can't be set directly because the window title does not get set and a warning message is issued by the Qt routines that there is no '[*]' placeholder in the window title when the program is loaded.  When the program is loaded into the edit box, a document modified signal is sent to the set window modified slot, when then attempts to set the modified flag, but placeholder has not yet been set in the window title.  The helper function takes care of this.

Once the edit box instance has been created, the constructor checks if the current program is not empty.  It then checks if the program file still exists, otherwise the file was either deleted or moved since the last run, so the current program path is cleared.  Also, if there is an error loading the program file, the current program path is cleared.

[commit dd489476cc]

Base GUI Complete

All the basic GUI elements have now been added, so this is a good place to tag the release, which was tagged with the name v0.3.0.  The various files (license, read me, release notes, etc.) were updated for this development release.

It was also noticed the about box (nor the test output) contained the full GPL statement, only the copyright and warranty statements.  The CommandLine class was updated to reflect this, but the test output was not changed so that the regression tests continue to pass.  The About box was updated to contain the full GPL statement along with the required Oxygen icons license statement and web links.

The original release numbering is again being used.  While Git did sort the tags as desired with the '-' developmental release before the '.' official release (so v0.2.0 will be listed after v0.2-6); GitHub (on the tags page) appears to only look at the numbers ignoring the separator characters, so v0.2.0 is sorted before v0.2-1 instead of after v0.2-6, which could be very confusing even though a fuzzy time of the release is listed, it's not obvious, and there is no way to sort by this time.

Since all 0.x releases are development releases, there is no reason to have development development releases.  For now on, only the last one of the development release series will be uploaded to Sourceforge, whatever is release number is at that point.  The source code of the others can be downloaded as archives from GitHub on the tags page.  The release number for any patches needed for an official release will simply increment the last (patch) number and re-uploaded.

[commit 4f0378e72c]