Document/View with Bakery

Contents

Introduction

Bakery::App_WithDoc can help you to separate your application's data and user interface. When use this Document/View interface, Bakery will take care of tedious but important generic functionality such as asking the user to select a document to load and asking the user to save modified documents. This means that you only need to write code that's specific to your application.

You will need App, Document, and View classes. And you will need to link them together. The next few sections show you how to do this. The code snippets are simplified versions of the WithDocView example in the Bakery distribution.

Create the App class

Derive an App class from Bakery::App_WithDoc_Gtk or App_WithDoc_GnomeUI. Override the new_instance() method:

e.g.

class AppExample : public Bakery::App_WithDoc_Gtk
{
public:
  AppExample();
  virtual ~AppExample();
protected:
  virtual App* new_instance(); //override
};
new_instance() should be implemented like so:
Bakery::App* AppExample::new_instance()
{
  return new AppExample();
}

Create the Document class

Derive a Document class from Bakery::Document.

e.g.

class DocExample : public Bakery::Document
{
public:
  DocExample();
  virtual ~DocExample();

  //overrides:
  virtual bool load_after();
  virtual bool save_before();

  void set_something(const std::string& strSomething);
  std::string get_something() const;

protected:
  std::string m_strSomething;
};

Implement load_afer() and save_before() to read and write your document data in whatever format you choose. Here's a very simple example:

bool DocExample::load_after()
{
  bool bTest = Bakery::Document::load_after();
  if(bTest)
  {
    //See comment in save().
  m_strSomething = get_contents();
  }
  
  return bTest;
}
bool DocExample::save_before()
{
  set_contents(m_strSomething);

  return Bakery::Document::save_before();
}

When you implement methods like set_something(), which changes data in your document, you should call Document::set_modified(true).

I suggest that you use an XML file format for your document. The Bakery::Document_XML class makes this easy.

Create the View class

The View is the user-interface that shows your Document's data and allows the user to change that data.

Derive a View class from both a Gtk-- or Gnome-- widget and Bakery::View. Notice that Bakery::View is a template which you specialize for your Document class.

For instance, this View has data in just one Entry:

class ViewExample :
  public Gtk::VBox,
  public Bakery::View<DocExample>
{
public:
  ViewExample();
  virtual ~ViewExample();

  //overrrides:
  virtual void load_from_document();
  virtual void save_to_document();
protected:
  //Signal handlers:
  virtual void on_Entry_changed();

  //Child widgets:
  Gtk::Label m_Label;
  Gtk::Entry m_Entry;
};

Your View needs to know how to show and store the Document data. So implement the load_from_document() and save_to_document() methods like so:

void ViewExample::load_from_document()
{
  m_Entry.set_text( get_document()->get_something() );
}
void ViewExample::save_to_document()
{
  const std::string& strText = m_Entry.get_text();
  
  //Don't trigger 'modified' unless it really is different:
  if(strText != get_document()->get_something()) 
    get_document()->set_something(strText);
}

Make your App use your View

Your View is a user-interface widget so you can use it in your App like any other widget. For instance, it could be a class member that is added in the App::init() method..

e.g.
class AppExample : public Bakery::App_WithDoc_Gtk
{
  ...

  ViewExample m_View;
};
and 
void AppExample::init()
{
  //Call base method:
  Bakery::App_WithDoc_Gtk::init();

  set_contents(m_View);
}

Make your App use your Document

Override App::init_create_document(), like so:

void AppExample::init_create_document()
{
  if(m_pDocument == NULL)
  {
    m_pDocument = new DocExample();

    //Tell document about view:
    m_pDocument->set_view(&m_View);

    //Tell view about document:

    m_View.set_document(static_cast<DocExample*>(m_pDocument));
  }
  Bakery::App_WithDoc_Gtk::init_create_document(); //Sets window title. Doesn't recreate doc.
}

Notice that you are also telling the View to use the Document instance.

Summary

Even if you do nothing else, your App will have a standard File menu with Open and Save items that read and write your Documents and show them in your View. However, you probably want customized menus and a toolbar so you should also read Bakery menus and toolbars.

Make sure that you call Bakery::init() and your App instance's init() method from your main() function.

Copyright © 2001-2003, Murray Cumming. Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.