MlView is a tree-oriented XML editor for GNOME. It is written in C++ and is heavily based on gtkmm and the GNOME libraries. Its aim is to ease XML editing, with or without validation.
Chapter 1. Why this document ?
The purpose of this document is to help people willing to understand the internals of MlView. We tried to write down the general design principles of the project because we think that grasping the big picture helps to understand the code. Another important thing to notice is that this document has been authored in docbook using MlView itself. It is therefore a nice playground to find and address bugs and usability issues of the editor.
Chapter 2. Overview
MlView is a generic XML editor that can edit all types of xml documents. MlView's design is organized around the Model/View/Controller paradigm. The model a.k.a "document model" is an in memory representation of the XML document. The views are the things (namely widgets) that allow the user to edit the document Model. In other words, views are what allows the user to perform editing actions on the document model. Controllers are somehow what does the glue between the document model and the editing views.
Each type of view "exports" the editing actions that it allows. Some view may allow the user to work on the structure of the document, other views may allow her to work in a more textual form, for example. These two hypothetical types of views would obviously "export" to different sets of edition actions.
The MlView editor can then be seen as a container of views and documents. Each view being "connected" to one document model. When a view issues an editing action on a document model, this model emits signals to reflect its new state. View that are connected to that document can then receive these signals and update themselves to reflect the new state of the document they are connected to.
Chapter 3. MlView's main classes
The code of MlVIew, though in C, is heavily object oriented. One of the key classes is the MlViewEditor class. It's basically the abstraction of the views and documents container. This class can instanciate and destroy of views (instances of MlViewIView) and documents (instances of MlViewXMLDocument).
MlViewEditor.�This is what abstracts the editor. it's basically a container of views and documents. It provides methods to manage documents and views, namely: open documents, save documents close views etc ...
MlViewIView.This is the abstract interface implemented by all the editing views. It defines methods to connect/disconnect the view to/from a document model, set the name of the view, get the document the view is related to, execute an editing action. One should keep in mind that an editing view is generally not a single widget. In our terminology, and editing view is a set of widgets that lets the user edit a document.
MlViewAppContext.I hate global variables. Especially variables which scope si wider than a class. In MlView, a class takes one source file. Again, I hate variables which scopes are wider than a file. That being said, all the classes of MlView need to share some data and behaviour. MlViewAppContext is there for that purpose: share data and behaviour across the different classes of MlView. This class contains methods to display error/warning messages (in an unified way), access application wide settings, set/query key/value pairs etc ... Generally, all the classes of MlView have methods to set/get an instance of MlViewAppContext so that they can have access to all the needed application wide data and behaviour. A single instance of MlViewAppContext is created at the MlView launch time and is "passed" to all the classes that are further created.
MlViewApp.This is an abstraction of the MlView application. Its duty is to build the main window, the main toolbar, the application wide instance of MlViewAppContext and the instance of MlViewEditor.
MlViewViewAdapter.This is a minimal implementation of MlViewIView. It aimes at providing a class that editing views can derive from. It implements all the interfaces of MlViewIView and does basically nothing. All the editing views can then inherit from MlViewViewAdapter
MlViewTreeView.This is the editing view available in MlView today. It's made of 3 main widgets: A tree editing widget:
A tree editing widget: MlViewTreeEditor
A node editing widget: MlViewNodeEditor
A node/attribute name completion widget: MlViewCompletionTable
Chapter 4. Keybindings management
A keybinding is the association between a sequence of keyboard events (what happens when the user hits a keyboard key) and an editing action. Basically, a defined keybinding triggers an editing actin when the user types a given predefined sequence of keys.
MlView tries to ease the definition of keybindings for the programmer. Therefore, a keybinding engine has been written. It's abstracted by the MlViewKBEng class.
The MlViewKBEng class provides apis to register, and lookup a binding.
Chapter 5.Undo/Redo management
introduction.MlView has an infinite undo/redo feature designed around the concept of "document mutation". A document mutation is basically the abstraction of a modification of the document model. From now on, we will refer to changes on the document model as "document mutations".
Design of the Undo infrastructure.The Undo feature lives at the MlViewXMLDocument level. This class is the document object model used by MlView. It has methods like MlViewXMLDocument::add_child_node() that actually perform mutations on the document. Each time a mutation is performed on an instance of MlViewXMLDocument, a mutation is "stored" in an undo stack. Each MlViewXMLDocument has a private instance of undo stack. The undo stack is an instance of MlViewDocMutationStack. It is a stack of instances of MlViewDocMutation. At instanciation time MlViewDocMutation object must be registered callbacks that will be called to do or undo the mutation. Later on, the client code can invoke MlViewDocMutation::do_mutation() or MlViewDocMutation::undo_mutation() to do/undo a given mutation. As I said earlier, each time a mutation occurs, an instance of MlViewDocMutation that matches that mutation is pushed on the undo stack. Later on, the MlViewXMLDocument::undo_mutation() method (that execute the undo feature) pops the topmost instance of MlViewDocMutation from the undo stack and invokes its MlViewDocMutation::undo_mutation() method. That MlViewDocMutation::undo_mutation() method calls the undo code registered by the client code to actually perform the undo. Simple isn't it ? ;)
Then comes the Redo ...To be continued ...
Chapter 6. Study of the "open document" workflow
In this chapter, we are going to see what happens when the user opens an xml document. This "open document" workflow can be triggered by doing file -> open or by hitting the "open" button in the application toolbar. The actual entry point of this workflow is the MlViewEditor::open_local_xml_document_interactive() method. This method basically pops up a file chooser to let the user choose the path of the document she wants to use. If the document is already loaded in the editor, the user is asked if she wants to reload it. Eventually, the MlViewEditor::load_xml_file() method is called to load the actual document in the editor.
The MlViewEditor::load_xml_file() method first parses the xml document into an in memory tree (using the excellent xml capabilities offered by the Libxml2 library). That in memory xml tree is an instance of the MlViewXMLDocument class. Afterwards, an instance of MlViewTreeView (the default editing view) is built and added to the current instance of MlViewEditor. The user can now edit the document using the editing capabilities offered by that editing view.
As you have noticed, the default view used when loading documents into the editor is the "tree-view" (MlViewTreeView). However, the user can at any time create a new view on the document being edited and then choose to instanciate another type view. Of course, the editor must have been compiled with the support of other views but the "tree-view".