IGUANA
Interactive Graphics for User ANAlysis - (Version 6.13.0.g4.81)
Guide > Developer > How to > Create a Model
 

Valid XHTML 1.0 Transitional
Valid CSS!
Browser compatibility
tested on:
Opera/9.10
Firefox/1.5.0.7
Safari/2.0.4
IE/6.0.2900



 Overview

In IGUANA browsers are viewers of a model, an abstract representation of the data at hand. This guide explains how to design a simple browser model; you will probably want to read it together with the guide to create a browser as the two are interconnected.To create a model follow these steps:
This receipe assumes that you are using the IGUANA C++ templates (automatically available in our emacs customisation).

 Design the model and rep classes

A model has one or more representations, or reps.Using the MVC (Model-View-Controller) design, the browser is the viewer and the controller, while the model together with the reps is the model part.However do note that the model and the reps are view-specific from the application point of view, and therefore can and should include visual representation details that would not be appropriate for the application objects. The IGUANA architecture provides a number of mechanisms for the application objects, their reps and the models to interact, but nothing for the interaction of browsers and models.
For the purposes of this discussion we assume that the browser will display the contents of the model in a GUI window.We assume that the model will be simple: it will include the rep of at most one object.(A more complex model would represent several objects simultaneously, reflecting the relations of the original objects as relations among the reps.)Furthermore we assume that the application object cannot be changed via this model -- and therefore browsers attached to this model must be viewers.We also assume that the browser and therefore its model are to be generic: it should and can work with pretty much any object, it is not targeted to specific functionality in specific objects. We also restrict ourselves such that the browser has minimal presentation capability -- the basic rep we will provide will have all the functionality the clients will ever need.(For a more complex scenario where the browser will have a fair amount of flexibility, please refer to the developer's tutorial.)Finally, we assume the browser will be a passive viewer: it will not provide selection or sub-object detail picking that ought to be communicated to other browsers.
Given these assumptions, our design choices are as follows:
  • We will not use the application objects directly as reps. This is generally the case, but especially so since we will want to be able to represent any application object without imposing a specific interface on them, and since there will be visual knowledge involved in the reps.
  • As we will be showing only one object at the time, the size or complexity of the model and the reps are not a major issue -- we will use the simplest convenient design.
  • Since editing is not possible, the model does not need to provide the browser an interface to change the reps. It does however need to provide an interface for the browser to listen for changes made elsewhere in the application.The burden to track actual rep updates will remain with the representation methods -- little will be done about this in the reps or the model itself.
  • We assume that browser is simple and therefore does not need to interact with the reps to manage selection or visual appearance.This restricts the clients to use the reps we design here.

 Define the model and rep classes

Let us call the model IgTextModel and the rep class IgTextRep.The class for notifying browsers about model changes we will call IgTextModelEvent.Create the corresponding header files, interface/IgTextRep.h, interface/IgTextModel.h and interface/IgTextModelEvent.h.Opening new files with those names in emacs will automatically insert the header file and basic class structure if you are using our C++ customisation layer.
The files should look something like this:
#ifndef IG_TEXT_MODEL_IG_TEXT_REP_H
# define IG_TEXT_MODEL_IG_TEXT_REP_H
                                                                    
//<<<<<< INCLUDES                                   >>>>>>

# include "Ig_Modules/IgTextModel/interface/config.h"
# include "Ig_Framework/IgObjectBrowser/interface/IgRep.h"

//<<<<<< PUBLIC DEFINES                             >>>>>>
//<<<<<< PUBLIC CONSTANTS                           >>>>>>
//<<<<<< PUBLIC TYPES                               >>>>>>

class IgTextModel;

//<<<<<< PUBLIC VARIABLES                           >>>>>>
//<<<<<< PUBLIC FUNCTIONS                           >>>>>>
//<<<<<< CLASS DECLARATIONS                         >>>>>>

class IG_TEXT_MODEL_API IgTextRep : public IgRep
{
public:
    IgTextRep (IgTextModel *model, std::string &stuff);
    // implicit copy constructor
    // implicit assignment operator
    // implicit destructor

    virtual IgRepContext *	context (void) const;
    virtual IgModel *		model (void) const;
    virtual const std::string &	text (void) const;

protected:
    virtual void		context (IgRepContext *context);

private:
    IgTextModel			*m_model;
    IgRepContext		*m_context;
    std::string			m_text;
};

//<<<<<< INLINE PUBLIC FUNCTIONS                    >>>>>>
//<<<<<< INLINE MEMBER FUNCTIONS                    >>>>>>

#endif // IG_TEXT_MODEL_IG_TEXT_REP_H
				
#ifndef IG_TEXT_MODEL_IG_TEXT_MODEL_H
# define IG_TEXT_MODEL_IG_TEXT_MODEL_H
                                                                    
//<<<<<< INCLUDES                                   >>>>>>

# include "Ig_Modules/IgTextModel/interface/config.h"
# include "Ig_Framework/IgObjectBrowser/interface/IgModel.h"
# include "Ig_Framework/IgObjectBrowser/interface/IgObserver.h"
# include <classlib/callback.h>
# include <vector>

//<<<<<< PUBLIC DEFINES                             >>>>>>
//<<<<<< PUBLIC CONSTANTS                           >>>>>>
//<<<<<< PUBLIC TYPES                               >>>>>>

class IgTextRep;
class IgTextModelEvent;

//<<<<<< PUBLIC VARIABLES                           >>>>>>
//<<<<<< PUBLIC FUNCTIONS                           >>>>>>
//<<<<<< CLASS DECLARATIONS                         >>>>>>

class IG_TEXT_MODEL_API IgTextModel : public IgModel
{
public:
    typedef IgObserver<IgTextModelEvent>::Observer Observer;
    enum EventType { TextChanged };

    IgTextModel (void);
    ~IgTextModel (void);

    virtual void	listen (EventType event,
				const Observer &listener);
    virtual void	unlisten (EventType event,
				  const Observer &listener);
    virtual void	changed (void);

    virtual IgTextRep *	text (void) const;
    virtual void	set (IgTextRep *rep);
    virtual void	change (IgTextRep *rep);
    virtual void	remove (IgTextRep *rep);

private:
    typedef IgObserver<IgTextModelEvent> Listeners;

    IgTextRep		*m_current;
    Listeners		m_listeners;

    // undefined semantics
    IgTextModel (const IgTextModel &);
    IgTextModel &operator= (const IgTextModel &);
};

//<<<<<< INLINE PUBLIC FUNCTIONS                    >>>>>>
//<<<<<< INLINE MEMBER FUNCTIONS                    >>>>>>

#endif // IG_TEXT_MODEL_IG_TEXT_MODEL_H
				
#ifndef IG_TEXT_MODEL_IG_TEXT_MODEL_EVENT_H
# define IG_TEXT_MODEL_IG_TEXT_MODEL_EVENT_H
                                                                    
//<<<<<< INCLUDES                                   >>>>>>

# include "Ig_Modules/IgTextModel/interface/config.h"

//<<<<<< PUBLIC DEFINES                             >>>>>>
//<<<<<< PUBLIC CONSTANTS                           >>>>>>
//<<<<<< PUBLIC TYPES                               >>>>>>

class IgTextModel;

//<<<<<< PUBLIC VARIABLES                           >>>>>>
//<<<<<< PUBLIC FUNCTIONS                           >>>>>>
//<<<<<< CLASS DECLARATIONS                         >>>>>>

class IG_TEXT_MODEL_API IgTextModelEvent
{
public:
    IgTextModelEvent (IgTextModel *source);
    // implicit copy constructor
    // implicit assignment operator
    // implicit destructor

    IgTextModel *	source (void) const;

private:
    IgTextModel		*m_source;
};

//<<<<<< INLINE PUBLIC FUNCTIONS                    >>>>>>
//<<<<<< INLINE MEMBER FUNCTIONS                    >>>>>>

inline
IgTextModelEvent::IgTextModelEvent (IgTextModel *source)
    : m_source (source)
{}

inline IgTextModel *
IgTextModelEvent::source (void) const
{
    return m_source;
}

#endif // IG_TEXT_MODEL_IG_TEXT_MODEL_EVENT_H
				

 Implement the model and rep classes

Now implement the classes in src/IgTextRep.cc and src/IgTextModel.cc as follows.
//<<<<<< INCLUDES                                   >>>>>>
                                                                    
#include "Ig_Modules/IgTextModel/interface/IgTextRep.h"
#include "Ig_Modules/IgTextModel/interface/IgTextModel.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgRepContext.h"
#include <classlib/debug.h>

//<<<<<< PRIVATE DEFINES                            >>>>>>
//<<<<<< PRIVATE CONSTANTS                          >>>>>>
//<<<<<< PRIVATE TYPES                              >>>>>>
//<<<<<< PRIVATE VARIABLE DEFINITIONS               >>>>>>
//<<<<<< PUBLIC VARIABLE DEFINITIONS                >>>>>>
//<<<<<< CLASS STRUCTURE INITIALIZATION             >>>>>>
//<<<<<< PRIVATE FUNCTION DEFINITIONS               >>>>>>
//<<<<<< PUBLIC FUNCTION DEFINITIONS                >>>>>>
//<<<<<< MEMBER FUNCTION DEFINITIONS                >>>>>>

IgTextRep::IgTextRep (IgTextModel *model,
		      const std::string &text)
    : m_model (model),
      m_context (0),
      m_text (text)
{}

IgTextRep::~IgTextRep (void)
{
    // The destruction may come upon me in two ways: the
    // object is going away and the context is deleting me,
    // or something else (= the model) is killing me.  I
    // can tell the difference from asking the context for
    // its rep -- if it is null, then it is the context that
    // is destroying me, and I must take not to delete the
    // context myself.

    // Make sure model knows I am gone.
    m_model->remove (this);

    // Make the context go away if we are not being deleted
    // by it.
    ASSERT (m_context);
    if (m_context->rep ())
    {
        ASSERT (m_context->rep () == this);
        m_context->erase (false);
        delete m_context;
    }
}

void
IgTextRep::context (IgRepContext *context)
{
    ASSERT (! m_context);
    ASSERT (context);
    m_context = context;
}

//////////////////////////////////////////////////////////
IgRepContext *
IgTextRep::context (void) const
{ return m_context; }

IgModel *
IgTextRep::model (void) const
{ return m_model; }

//////////////////////////////////////////////////////////
const std::string &
IgTextRep::text (void) const
{ return m_text; }
				
//<<<<<< INCLUDES                                   >>>>>>
                                                                    
#include "Ig_Modules/IgTextModel/interface/IgTextModel.h"
#include "Ig_Modules/IgTextModel/interface/IgTextModelEvent.h"
#include "Ig_Modules/IgTextModel/interface/IgTextRep.h"
#include <classlib/debug.h>
#include <algorithm>

//<<<<<< PRIVATE DEFINES                            >>>>>>
//<<<<<< PRIVATE CONSTANTS                          >>>>>>
//<<<<<< PRIVATE TYPES                              >>>>>>
//<<<<<< PRIVATE VARIABLE DEFINITIONS               >>>>>>
//<<<<<< PUBLIC VARIABLE DEFINITIONS                >>>>>>
//<<<<<< CLASS STRUCTURE INITIALIZATION             >>>>>>
//<<<<<< PRIVATE FUNCTION DEFINITIONS               >>>>>>
//<<<<<< PUBLIC FUNCTION DEFINITIONS                >>>>>>
//<<<<<< MEMBER FUNCTION DEFINITIONS                >>>>>>

IgTextModel::IgTextModel (void)
    : m_current (0)
{}

IgTextModel::~IgTextModel (void)
{ delete m_current; }

//////////////////////////////////////////////////////////
void
IgTextModel::listen (EventType /* event */,
	             const Observer &listener)
{ m_listeners.listen (listener); }

void
IgTextModel::unlisten (EventType /* event */,
		       const Listener &listener)
{ m_listeners.unlisten (listener); }

void
IgTextModel::changed (void)
{ m_listeners.broadcast (IgTextModelEvent (this)); }

//////////////////////////////////////////////////////////
IgTextRep *
IgTextModel::text (IgTextRep *rep)
{ return m_current; }

//////////////////////////////////////////////////////////
void
IgTextModel::set (IgTextRep *rep)
{
    if (m_current != rep)
        delete m_current;
    m_current = rep;
    changed ();
}

void
IgTextModel::change (IgTextRep *rep)
{
    ASSERT (rep);
    if (m_current == rep)
        changed ();
}

void
IgTextModel::remove (IgTextRep *rep)
{
    ASSERT (rep);
    if (m_current != rep)
    {
        m_current = 0;
        changed ();
    }
}
				

 Add interface/xtypeinfo.h

Follow the corresponding section in the plug-in creation how-to to provide the extended type information for IgTextRep and IgTextModel.

 Add src/plugin.cc

Follow the corresponding section in the plug-in creation how-to to define the extended type information for IgTextRep and IgTextModel. You do not need to define any plug-in properties for this package, nor make it a plug-in -- just define the extended type information.

 Modify BuildFile

Nothing extraordinary here. You'll need dependencies on IgObjectBrowser and anything else you used. In this example we only depend on the C++ standard library so nothing special needs to be mentioned. The BuildFile in the package root directory should now look like this:
<Use name=Ig_Framework/IgObjectBrowser>
<Export>
  <Lib name=IgTextModel>
</Export>
				

 Build the package

You are now ready to build your plug-in with scram build. You should see your library linked against other libraries. Note that in IGUANA your package must be mentioned in the build order to build it from the top level. See the new package how-to for the instructions.