GtkOL Reference Manual

2.3. GtkOL serialization handling

    GtkOL implements the libgenerics xml serialization abtraction. It does not support the libgenerics archive abstraction. Any of the GtkOL components are serializable in an xml way unless they are declared as capsule or generic classes. The purpose of such an implementation is the ability for an application to dump its whole state into a file to be launched again with the same detailed state it was when it ended.


2.3.1. GtkOL serialization considerations

    First of all, let's have a look on how the xml file is structured about an instance.

seralization
fig. GtkOL serialization overview
   
    Xml Dump


    To dump a component state, you first have to declare an xml document giving it an xml file name, the write mode and the absolute xml element name.

CXMLDocument XMLDocWrite (CString("/home/user/gtkol-dump.xml"), XML_WRITE, CString("gtkol"));

    Once you've got it, you can redirect any GtkOL component into and the serialization process of the component and its descent hierarchy will be automatically handled.

CForm *aForm = new CForm ();
XMLDocWrite << aForm;


    Xml Load

    To reload a GtkOL components hierarchy from a given xml file, you first have to declare an xml document giving it the desired xml file name, the read mode and the expected absolute xml element name. You have to retreive the root xml element.

CXMLDocument XMLDocRead (CString("/home/user/gtkol-dump.xml"), XML_READ, CString("gtkol"));
CXMLElementNode *inXMLRoot = XMLDocRead.GetRootElement ();

   Then, you can request a GtkOL components hierarchy instanciation mapping the given xml scheme from the xml root element and free the xml pointer.

CSerialized *inSerialized = CSerialized::Instanciate (inXMLRoot);
delete inXMLRoot;

    The returned instance might be any of the GtkOL ones, depending on what has been dumped into the xml file when created. Let's consider the whole application has been dumped i.e. the GtkOL application instance itself has been dumped, so the complete GtkOL hierarchy is reloaded from scratch, you just have to check the returned pointer type and request the application launch...

if (inSerialized -> ClassIs (__metaclass(CApplication))) static_cast <CApplication *> (inSerialized) -> Run ();


    Simple application xml dump sample :

<?xml version="1.0" encoding="UTF-8" ?>
<gtkol>
 <cserialized name="CApplication" tag="appl">
  <ccomponent>
   <capplication>
    <argv>./gtkol-dump</argv>
   </capplication>
   <children>
    <cserialized name="CForm" tag="form">
     <cserialized name="CMainFormListener" tag="_mfm"></cserialized>
     <ccomponent>
      <ccontrol draggable="false" dropsite="true" x="252" y="30" w="520" h="670">
       <cwidget shown="true" enabled="true">
        <ccontainer padding="0">
         <cform caption="Gtkol Dump" maximize="false" minimize="false" fullscreen="false" sticky="false" above="false" below="false" resizeable="true" decorated="true" taskbar-hint="true" pager-hint="true"></cform>
        </ccontainer>
       </cwidget>
      </ccontrol>
      <children>
       <cserialized name="CVBoxLayout" tag="vbly">
        <ccomponent>
         <ccontrol draggable="false" dropsite="true">
          <cwidget shown="true" enabled="true">
           <ccontainer padding="0">
            <cboxlayout spacing="1" homogeneous="false">
             <cboxlayout-child box="start" expand="false" fill="false" padding="0"></cboxlayout-child>
            </cboxlayout>
           </ccontainer>
          </cwidget>
         </ccontrol>
         <children>
          <cserialized name="CMenuBar" tag="mnbr">
           <ccomponent>
            <ccontrol draggable="false" dropsite="false">
             <cwidget shown="true" enabled="true">
              <ccontainer padding="0"></ccontainer>
             </cwidget>
            </ccontrol>
            <children>
             <cserialized name="CMenuItem" tag="mntm">
              <ccomponent>
               <ccontrol draggable="false" dropsite="false">
                <cwidget shown="true" enabled="true">
                 <ccontainer padding="0">
                  <cmenuitem caption="_File"></cmenuitem>
                 </ccontainer>
                </cwidget>
               </ccontrol>
               <children>
                <cserialized name="CMenuItem" tag="mntm">
                 <cserialized name="CFileExitMenuItemListener" tag="_cmn"></cserialized>
                 <ccomponent>
                  <ccontrol draggable="false" dropsite="false">
                   <cwidget shown="true" enabled="true">
                    <ccontainer padding="0">
                     <cmenuitem caption="_Quit" key="113" combinaison="ctrl"></cmenuitem>
                    </ccontainer>
                   </cwidget>
                  </ccontrol>
                 </ccomponent>
                </cserialized>
               </children>
              </ccomponent>
             </cserialized>
            </children>
           </ccomponent>
          </cserialized>
         </children>
        </ccomponent>
       </cserialized>
      </children>
     </ccomponent>
    </cserialized>
   </children>
  </ccomponent>
 </cserialized>
</gtkol>
serialization-capture.png


2.3.2. GtkOL runtime potential approach

    With such an implementation, it would be very easy to create a complete GtkOL application while just building it as an xml file and injecting it in a simple "gtkol runtime binary" that would dynamically link particular widgets signal handlers such as a pool of developper typicall listeners, open the given xml template and instanciate the complete GUI, giving the developper the possibility to write the minimum amount of code : the event handlers scope only.


GtkOL runtime
fig. GtkOL runtime logical implementation

The simple reusable GtkOL runtime could be something like :

// get the gtkol application definition and the generics metamodule importer API
#include "capplication.h"
#include "cmetamoduleimporter.h"

//---------------------------------------------------------------------------------------
// gtkol-runtime entry point
//---------------------------------------------------------------------------------------
int main (int argc, char **argv)
{
    // check command line arguments
    if (argc != 2)
    {
        ::printf ("%s file.xml\n", argv[0]);
        return 0;
    }

    // get the potential runtime modules from the specified directory
    CStrings inMetaModuleNames (CMetaModuleImporter::GetLibNames (CString("./modules")));

    // declare a metamodule importer list
    TBuffer <CMetaModuleImporter *> theMetaModuleImporters;

    try
    {
       // foreach potential module, try to link it with the metamodule importer API
       for (size_t i=inMetaModuleNames.GetLength(), j=0; i>0; i--, j++)
            theMetaModuleImporters += new CMetaModuleImporter (*inMetaModuleNames[i-1]);

       // get an xml document, check its root element name
       CXMLDocument XMLDoc (CString(argv[1]), XML_READ, CString("gtkol"));
       CXMLElementNode *inXMLNode = XMLDoc.GetRootElement();

       // instanciate the specified gtkol hierarchy
       CSerialized *inSerialized = CSerialized::Instanciate (inXMLNode);

       // check we got an expected CApplication instance and launch it if so !
       if (inSerialized -> ClassIs (__metaclass(CApplication))) static_cast <CApplication *> (inSerialized) -> Run ();

       // free the pointers
       delete inXMLNode;
       delete inSerialized;
    }
    // so sad...
    catch (CException *e)
    {
        printf ("An exception occured : %s\n", e->GetMessage().Get());
        return -1;
    }

    // unlink the modules and free any resource
    for (size_t i=theMetaModuleImporters.GetLength(); i>0; i--) delete *theMetaModuleImporters[i-1];

    // ok
    return 0;
}

And with the previous xml dump sample, the developper would have to write only two listeners that would be dynamically linked to the runtime : the referenced CMainFormListener and CFileExitMenuItemListener classes that could be written with something like :

#include "cform.h"

//------------------------------------------------------------------------------------
// CMainFormListener
//------------------------------------------------------------------------------------
class CMainFormListener : public CFormListener
{
        // called when the form is closed
        virtual void OnClose (CObject *)
        {
                CXMLDocument XMLDoc (CString("./gtkol-dump.xml"), XML_WRITE, CString("gtkol"));
                XMLDoc << CComponent::GetComponent (1L);
        }

        SECTION_DYNAMIC_METACLASS;
};
DECLARE_DYNAMIC_METACLASS ('_mfm', CMainFormListener, CFormListener);
RESOLVE_DYNAMIC_METACLASS (CMainFormListener);

DECLARE_METAMODULE_EXPORT (CMainFormListener);

#include "cmenu.h"

//--------------------------------------------------------------------------------------
// CFileExitMenuItemListener
//--------------------------------------------------------------------------------------
class CFileExitMenuItemListener : public CMenuItemListener
{
        // called when the menu is clicked
        virtual void OnClick (CObject *inSender)
        {
                static_cast <CForm *> (static_cast <CMenuItem *> (inSender) -> GetOwner (__metaclass(CForm))) -> Close ();
        }

        SECTION_DYNAMIC_METACLASS;
};
DECLARE_DYNAMIC_METACLASS ('_cmn', CFileExitMenuItemListener, CMenuItemListener);
RESOLVE_DYNAMIC_METACLASS (CFileExitMenuItemListener);

DECLARE_METAMODULE_EXPORT (CFileExitMenuItemListener);

Compilation directives :
c++ -Wno-multichar -O2 -rdynamic -o gtkol-runtime `pkg-config --cflags libgtkol-1.2` gtkol-runtime.c `pkg-config --libs libgtkol-1.2`
c++ -Wno-multichar -O2 -shared -o cfileexitmenuitemlistener.so `pkg-config --cflags libgtkol-1.2` cfileexitmenuitemlistener.c
c++ -Wno-multichar -O2 -shared -o cmainformlistener.so `pkg-config --cflags libgtkol-1.2` cmainformlistener.c