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

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

This guide continues the scenario described in the model creation how-to, by explaining how to build the viewer, a browser, to look at the model. Follow these steps:
This receipe assumes that you are using the IGUANA C++ templates (automatically available in our emacs customisation).

 Design the browser

The browser is the viewer or the controller part of the MVC (Model-View-Controller) design. While IGUANA architecture provides some basic infrastructure supportn for browsers, such as message passing among browsers and browser hosting in sites, there is little more for the actual implementation part. We recommend certain basic guidelines, but you will have to judge whether these are appropriate to the case at hand or not.
Typically browsers are observers of their model. Whenever the model changes, it notifies all observers, so the browsers will know to update their displays. Dependening on the complexity of the model, there can be just a single message telling the model has changed, or the messages can carry more detailed information about how and which parts of the model have changed. In general browsers should not assume to be the sole owners of the models -- several browsers might be sharing the same model. A browser might also be combining information from several models simultanously. This has a bearing on how the browser should assume to be communicating with the model and how the reps will be designed. [FIXME: this example treats the model as own!]
The browser may provide means to not just view the object, but to also interact with it. One of the simplest form of interaction is picking or selection: when an object is selected in one browser, other browsers should reflect the change of the selection. Thus selection among a group of browsers is essentially a piece of shared state; in IGUANA, browsers collaborate through a selection service to keep track of that state. [FIXME: Session groups.]
The browsers may also provide other means to interact with the application object or its representation. For example, a browser might control some aspect of the object such that other browsers will change their view of the object as a result (e.g. visibility, colour, ...). The browser might provide means to invoke actions on the object, for example through a pop-up menu on right mouse click.
For the purposes of this guide, we assume a simple browser that simply displays the contents of the model in a GUI window, but does not provide any additional means to interact with the object. As the example model basically represents an application object as text, this example browser will simply render that text in a Qt GUI window. However, since Qt has a text renderer that can format HTML, we will use this capability to provide a more interesting example.
Given these assumptions, our design choices are as follows:
  • The browser will show only one object at a time.
  • Editing will not be possible; the browser should just react to rendering requests.
  • As we have assumed that the browser will not interact with the reps for rendering, we simply use them to grab the text they hold.
The browser will have to fullfill the following three protocols:
  • It should co-operate with Qt-based site tree.
  • It should co-operate with the model and its events.
  • It should fulfill the base class interfaces defined by IgBrowser.

 Define the browser class

Create the header file interface/IgQtTextBrowser.h for the browser class IgQtTextBrowser. Opening a new file with that name 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_QT_TEXT_BROWSER_IG_QT_TEXT_BROWSER_H
# define IG_QT_TEXT_BROWSER_IG_QT_TEXT_BROWSER_H
                                                                      
//<<<<<< INCLUDES                                   >>>>>>

# include "Ig_Modules/IgQtTextBrowser/interface/config.h"
# include "Ig_Framework/IgObjectBrowser/interface/IgBrowser.h"
# include <qtextview.h>

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

class IgSession;
class IgBrowserSite;
class IgSelectionMessage;

class IgTextModel;
class IgTextModelEvent;
class IgTextRep;

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

class IG_TEXT_BROWSER_API IgQtTextBrowser
    : public QTextView, public IgBrowser
{
public:
    IgQtTextBrowser (IgSession *session, IgSite *site);
    ~IgQtTextBrowser (void);

    virtual void	browser (IgRepresentable *object);
    virtual IgTextModel *model (void) const;
    virtual IgSession *	session (void) const;

protected:
    virtual void	textChanged (IgTextModelEvent event);
    virtual void	selectMessage (IgSelectionMessage message);

private:
    IgSession		*m_session;
    IgSite		*m_site;
    IgTextModel		*m_model;

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

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

#endif // IG_QT_TEXT_BROWSER_IG_QT_TEXT_BROWSER_H
				

 Implement the browser class

Now implement src/IgQtTextBrowser.cc:
//<<<<<< INCLUDES                                   >>>>>>
                                                                     
#include "Ig_Modules/IgQtTextBrowser/interface/IgQtTextBrowser.h"
#include "Ig_Modules/IgTextModel/interface/IgTextModel.h"
#include "Ig_Modules/IgTextModel/interface/IgTextRep.h"
#include "Ig_Modules/IgQtBrowser/interface/IgQtSite.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgRepSet.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgRepContext.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgSelectionService.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgSelectionMessage.h"
#include "Ig_Framework/IgObjectBrowser/interface/IgSession.h"

#undef emit // zap qt's placeholder macro
#include <classlib/debug.h>
#include <classlib/callback.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                >>>>>>

IgQtTextBrowser::IgQtTextBrowser (IgSession *session, IgSite *site)
    : QTextView (IgQtSite::hostFrom (site)),
      m_session (session),
      m_model (new IgTextModel)
{
    ASSERT (m_sesssion);
    IgQtSite::host (site, this);
    IgSelectionService::instance (m_session)->listen
        (create_callback (this, &IgQtTextBrowser::selectMessage));
    m_model->listen (IgTextModel::TextChanged, create_callback
		     (this, &IgQtTextBrowser::textChanged));
}

IgQtTextBrowser::~IgQtTextBrowser (void)
{
    ASSERT (m_session);
    IgSelectionService::instance (m_session)->unlisten
        (create_callback (this, &IgQtTextBrowser::selectMessage));
    m_model->unlisten (IgTextModel::TextChanged, create_callback
		(this, &IgQtTextBrowser::textChanged));
    delete m_model;
}

void
IgQtTextBrowser::browse (IgRepresentable *object)
{
    if (IgRep *rep = IgRepSet::lookup (object, m_model, true))
    {
        ASSERT (dynamic_cast<IgTextRep *> (rep));
        IgSelectionService::instance (m_session)
            ->broadcast (IgSelectionMessage (rep->context ());
    }
}

void
IgQtTextBrowser::selectMessage (IgSelectionMessage message)
{
    if (! message.context ())
        m_model->set (0);
    else if (IgTextRep *rep = dynamic_cast<IgTextRep *>
             (IgRepSet::lookup (message.context (), m_model, true)))
        m_model->set (rep);
    else
        m_model->set (0);
}

void
IgQtTextBrowser::textChanged (IgTextModelEvent)
{
    QString newText;
    if (m_model->text ())
        newText = m_model->text ()->text ().c_str ();
    setText (newText);
}
				

 Add src/plugin.cc

Follow the corresponding section in the plug-in creation how-to to declare the browser class in the plug-in definition class. You do not need to specify any extended type information.

 Modify BuildFile

Nothing extraordinary here. You'll need dependencies on IgObjectBrowser, IgQtBrowser and IgTextModel. Since the latter mention the core library anyway, you don't need to add it yourself. The BuildFile in the package root directory should now look like this:
<Use name=Ig_Modules/IgTextModel>
<Use name=Ig_Modules/IgQtBrowser>
<Export>
  <Lib name=IgQtTextBrowser>
</Export>

include $(LOCALTOP)/$(projconfigdir)/plugin_makefile.mk
				

 Build the package

You are now ready to build your plug-in with scram build. You should see your library linked against other libraries and a creation of an XML file in lib/archiguana-plugins. Note that 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.

 Check the plug-in with iguana

Once your package has been built, verify that iguana can load the library and discover its properties. Running the following two commands should produce output that includes the entities from your plug-in:
$ eval `scram runtime -sh`
$ iguana --list
				
You should test the browser with a driver, some objects and representation methods to create IgTextRep objects.