Eagle Mode -
C++ API Tutorial

Copyright © 2010 Oliver Hamann. Homepage: http://eaglemode.sourceforge.net/

# Contents

1 Introduction
2 Standalone Hello World example
3 Exceptions, error handling and logging
4 Painting graphics and text
5 Keyboard and mouse input
6 Simple animation through an engine
7 Signals and timers
8 Panel tree expansion
9 The toolkit panels
10 Models and contexts
11 Plugin interface

# 1 Introduction

The Eagle Mode C++ API can be used to create both, plugin applications and standalone applications. A plugin application could show in the virtual cosmos and/or in the file manager, while a standalone application shows as an own window on the desktop.

This tutorial mainly consists of programming examples which can also be seen as design patterns for the respective topics. Originally, most of the examples are standalone applications because this makes it easier to compile and run them. Only the very last chapter brings a complete plugin example in order to explain the plugin interface. The examples can be found in the directory `$EM_DIR/doc/examples/CppApiExamples` (replace `$EM_DIR` by the Eagle Mode directory). There is also a Perl script that makes it very easy to run the standalone examples. It compiles automatically and executes a temporary binary. Here are the commands to run the Hello World example:

> `cd $EM_DIR/doc/examples/CppApiExamples`
> `perl run-example.pl HelloWorldExample.cpp`

The examples can also be found inline in this document. But except for the first and the last example, the beginnings and ends of the example sources are left out, because those fragments are almost always the same.

In order to make first programming experiments as easy as possible, this tutorial only covers the most important topics without explaining all the API classes and functions in detail. The examples should give you a good intuition of how the API works. For more details, you may have a look to the Reference Guide. That is an index to HTML-converted header files which are containing extensive comments as the reference documentation, sometimes also with tiny examples and short tutorials. If you find something is not described well enough, please do not hesitate to write to the API Support Forum.

The real header files are in `$EM_DIR/include/emCore` and the library to be linked is `$EM_DIR/lib/libemCore.so` or on Windows `$EM_DIR/lib/emCore.lib`. Thus, the API is made of a sub-project named emCore.

The writing of "makefiles" is described in another document: Make System.

Before continuing, you should have read the Elements of the user interface chapter in the General User Guide in order to know what is meant with the terms Window, View, Panel, Focus and so on.

# 2 Standalone Hello World example

Let us start with a Hello World example. Below is the source code of a small standalone application which shows a window. Therein, the user can zoom and scroll a single black panel with a "Hello World" inscription. Panels are in the Eagle Mode API what widgets are in classic APIs. It is just that they are zoomable.

The base class for panels is `emPanel`. In the example source below, you can find the class `MyPanel` derived from `emPanel`. It overloads the constructor and two methods: `GetTitle` and `Paint`. The result of `GetTitle` is a character string which is shown in the title bar of the window when the panel is active or focussed. The example has only one panel and therefore that panel is always the active one. The `Paint` method is overloaded in order to fill the background of the panel with black and to write "Hello World!" in red letters on it. More about painting is described in a later chapter.

The example source also defines the main function of the program. It does this through the macro `MAIN_OR_WINMAIN_HERE`. That macro expands to an operating system dependent main function (`WinMain` on Windows, `main` elsewhere), which simply forwards to `wrapped_main`. Therein, the example first calls `emInitLocale()` in order to enable UTF-8 encoding if possible (the declaration of that function is here). Then the example creates an `emGUIFramework` and prepares it to terminate the program automatically when it has no more windows. Afterwards the example creates an `emWindow` and prepares it to delete itself when the close button is clicked. Finally an instance of the example panel class `MyPanel` is created in that window. The `Layout` call just has effect on the height/width ratio of the panel, because it is a root panel (more about `Layout` in a later chapter). At the end, the main function calls the `Run` method of `emGUIFramework` in order to serve the user interface until termination.

What you should learn from the example is how to bring up a panel in a standalone application, because many further examples just show how to program panels.

`HelloWorldExample.cpp`

    #include <emCore/emGUIFramework.h>
    #include <emCore/emPanel.h>

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name);
            virtual emString GetTitle();
    protected:
            virtual void Paint(const emPainter & painter, emColor canvasColor);
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emPanel(parent,name)
    {
    }

    emString MyPanel::GetTitle()
    {
            return "Hello World Example";
    }

    void MyPanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            painter.Clear(emColor::BLACK);
            painter.PaintTextBoxed(0,0,1,GetHeight(),"Hello World!",.01,emColor::RED);
    }

    MAIN_OR_WINMAIN_HERE

    static int wrapped_main(int argc, char * argv[])
    {
            emInitLocale();

            emGUIFramework framework;
            framework.EnableAutoTermination();

            emWindow * window=new emWindow(framework.GetRootContext());
            window->SetWindowFlags(emWindow::WF_AUTO_DELETE);

            emPanel * panel=new MyPanel(window,"root");
            panel->Layout(0.0,0.0,4.0,3.0);

            return framework.Run();
    }

# 3 Exceptions, error handling and logging

Before continuing with more fun, some words about error handling:

Every method and function of the API which could fail, fails by throwing an exception. The exception is always an `emString` which contains a human-readable error message. In addition, the name of the method or function contains the word "Try" at the beginning, in order to make clear that it could fail and throw an exception. Examples are: `emTryOpenLib`, `emColor::TryParse`.

If you can't continue the program after a failure, then you may call `emFatalError(format, ...)` in order to report the error and exit the program. Its arguments are like with `printf`. For more about this function and about logging, please read this section.

# 4 Painting graphics and text

The Hello World example already did a little bit of painting. This chapter brings a more complex paint example, but first some explanations:

In order to paint a panel, you have to overload the `Paint` method of `emPanel`. It is called automatically whenever the panel has to be painted again, for example after a change of the visual transformation. In addition, you can call the `InvalidatePainting()` method of `emPanel`, if you want `Paint` to be called again for whatever reason.

The `Paint` method has two arguments: a painter and a canvas color. The painter (class `emPainter`) has lots of methods for painting with transformation and clipping. It is prepared for painting in the coordinate system of the panel. This means, the origin is in the upper-left corner of the panel, the X-axis points right, and the Y-axis points down. The unit is panel widths, which means that a panel has always a width of 1.0 in its own coordinate system.

The canvas color (class `emColor`) is the color of the background, or it is any non-opaque color if the background is unknown or not plain. The idea is to give the canvas color to the paint method calls in `emPainter` (as the equal-named argument). In many cases, the paint methods are faster and more exact if they get a known (opaque) canvas color. In the example below you can see that the first paint operations fills the background with white color by calling the `Clear` method. Therefore the canvas color is changed to white one line after in order to use it in the other calls. The last two paint operations do not get the canvas color, because they overlap with other objects.

Whenever you overload the `Paint` method, you should also consider to overload the `IsOpaque` method. It tells whether the panel altogether paints opaque or not. If yes, the parent panel is automatically not painted in certain situations.

`PaintExample.cpp (essential part)`

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual bool IsOpaque();
            virtual void Paint(const emPainter & painter, emColor canvasColor);
    private:
            emImage EagleIcon;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emPanel(parent,name)
    {
            EagleIcon=emGetInsResImage(GetRootContext(),"icons","eaglemode.tga");
    }

    bool MyPanel::IsOpaque()
    {
            return true;
    }

    void MyPanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            painter.Clear(emColor::WHITE,canvasColor);
            canvasColor=emColor::WHITE;

            painter.PaintImage(0.05,0.05,0.3,0.3,EagleIcon,255,canvasColor);

            painter.PaintRect(0.4,0.1,0.2,0.2,emColor::GREEN,canvasColor);

            painter.PaintRectOutline(0.69,0.09,0.22,0.22,0.01,0x0080C0FF,canvasColor);
            painter.PaintTextBoxed(
                    0.7,0.1,0.2,0.2,
                    "Centered text\nin\nthe bottom-right\nof a box",
                    0.017,0x0080C0FF,canvasColor,
                    EM_ALIGN_BOTTOM_RIGHT,EM_ALIGN_CENTER
            );

            static const double poly1[]={
                    0.1, 0.4,
                    0.05, 0.7,
                    0.3, 0.5
            };
            painter.PaintPolygon(poly1,3,emColor(255,128,0),canvasColor);

            painter.PaintLine(
                    0.3,0.4,0.5,0.7,0.05,
                    emPainter::LC_ROUND,emPainter::LC_ROUND,
                    emColor(255,0,0,128),canvasColor
            );

            painter.PaintEllipse(0.5,0.4,0.3,0.3,0x33CC88FF,canvasColor);
            painter.PaintEllipse(0.6,0.4,0.3,0.3,0xFF55AAFF);
            painter.PaintEllipse(0.55,0.35,0.3,0.3,0xFFFF3388);
    }

# 5 Keyboard and mouse input

If you want to process keyboard and mouse input, then you have to overload the `Input` method of `emPanel`. It is called whenever there is a keyboard or mouse event like a button press, or when there is a change of any key or button state, or when the mouse is moved. Therefore the Input method has two major arguments: an `emInputEvent` and an `emInputState`. Besides, it also gets the current mouse position (mx, my) in the coordinate system of the panel, because `emInputState` provides the mouse position only in pixel coordinates.

Keyboard events are given to the panel which has the keyboard focus, and mouse events are given to the panel in which the mouse pointer is. In addition, the events may also be given to ancestors of those panels (e.g. a parent panel or the window). To avoid that, a panel can eat an event by calling the `Eat` method of `emInputEvent`. That is the reason why the event argument is non-const.

When you overload the `Input` method, you should not forget to call the base class implementation. `emPanel` itself eats certain keyboard and mouse events in order to change the focus. Therefore you should not eat those events before calling the base class, but you may use them to do additional things.

The example below demonstrates how to process various input events by overloading the `Input` method. Note that there are no events for the release of a button or key. So if you want to get noticed about a release, you have to track the input state. This is also demonstrated in the example.

In addition, you should notice that `emInputEvent` describes the event in two ways: the key (which could also be a mouse button), and a character string that the key produces. Normally you should ask for the key, but sometimes you may want to ask for a character which has no key on the keyboard. An example would be the dollar sign which is produced by Shift+4 on English keyboards.

`InputExample.cpp (essential part)`

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual void Input(emInputEvent & event, const emInputState & state,
                               double mx, double my);
    private:
            bool XKeyDown;
            bool ButtonDown;
            double LastMX, LastMY;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emPanel(parent,name)
    {
            XKeyDown=false;
            ButtonDown=false;
    }

    void MyPanel::Input(
            emInputEvent & event, const emInputState & state, double mx, double my
    )
    {
            if (event.IsKey(EM_KEY_E) && state.IsNoMod()) {
                    emLog("The E key was pressed without any modifier keys.");
                    event.Eat();
            }

            if (event.IsKey(EM_KEY_G) && state.IsShiftAltMod()) {
                    emLog("The Shift+Alt+G key combination was pressed.");
                    event.Eat();
            }

            emInputHotkey hotkey(EM_KEY_CTRL, EM_KEY_V);
            if (hotkey.Match(event,state)) {
                    emLog("The %s key combination was pressed.", hotkey.GetString().Get());
                    event.Eat();
            }

            if (event.GetChars()=="$") {
                    emLog("A key combination that prints the dollar sign was pressed.");
                    event.Eat();
            }

            if (event.IsKey(EM_KEY_X)) {
                    emLog("The X key was pressed.");
                    XKeyDown=true;
                    event.Eat();
            }
            if (XKeyDown && !state.Get(EM_KEY_X)) {
                    emLog("The X key was released.");
                    XKeyDown=false;
            }

            if (event.IsKey(EM_KEY_LEFT_BUTTON)) {
                    emLog("The left mouse button was pressed at (%g, %g).", mx, my);
                    ButtonDown=true;
                    LastMX=mx;
                    LastMY=my;
                    // We do not eat the event here so that emPanel::Input still
                    // sets the focus on this panel by the button event. Otherwise
                    // we should call Focus().
            }
            if (ButtonDown && (LastMX!=mx || LastMY!=mx)) {
                    emLog("The mouse was dragged to (%g, %g).", mx, my);
                    LastMX=mx;
                    LastMY=my;
            }
            if (ButtonDown && !state.Get(EM_KEY_LEFT_BUTTON)) {
                    emLog("The left mouse button was released.");
                    ButtonDown=false;
            }

            // Call the base class. It changes the focus by certain input events.
            emPanel::Input(event,state,mx,my);
    }

# 6 Simple animation through an engine

The API provides three concepts or classes which solve the problem of sharing the CPU: `emThread`, `emProcess` and `emEngine`. As you might expect from the names, the first two are just about classic threads and processes. But the third one is somehow usual, and it is used very widely in Eagle Mode, as it is the super class of all high-level classes. Therefore, this and the next chapter are about `emEngine`.

All high-level classes are subclasses of emEngine.

`emEngine` has two possible uses: polling and signal exchange. Of course, polling is quite despised because it wastes CPU cycles, but signal exchange requires a little bit more programming effort. With `emEngine`, it is possible to quickly program a first solution with polling, and to improve that solution later by conversion into signal exchange, without any expensive restructuring.

In order to perform polling, you just need to do three things:

- Overload the method `Cycle()` of `emEngine` and implement the polling code in it. Do not forget to call the base class implementation too, if you do not know it is empty.
- Wake up the engine once at the beginning by calling `WakeUp()`, e.g. in the constructor.
- Always return `true` form `Cycle`.

This way, `Cycle` is called up to about one hundred times per second, depending on the CPU load.

The following example code demonstrates the overloading of `Cycle` in an `emPanel`, in order to show a simple animation in form of a decimal counter which increases on every cycle.

`SimpleAnimationExample.cpp (essential part)`

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual bool Cycle();
            virtual void Paint(const emPainter & painter, emColor canvasColor);
    private:
            unsigned Counter;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emPanel(parent,name)
    {
            Counter=0;
            WakeUp();
    }

    bool MyPanel::Cycle()
    {
            emPanel::Cycle();
            Counter++;
            InvalidatePainting();
            return true;
    }

    void MyPanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            emString str=emString::Format("%u",Counter);
            painter.PaintTextBoxed(0,0,1,GetHeight(),str,.41,0xFFFF80FF);
    }

# 7 Signals and timers

As already said, the better use of `emEngine` is to exchange signals. The idea is to wake up an engine by a signal whenever there is something useful to do for it. Moreover, it is possible to find out which signal has woken up an engine, and thereby an engine can receive any number of different signals in order to do different things. A signal can be received by multiple engines, and the sender does not need to know anything about the receivers.

Here is how you can send a signal:

- Create an object of class `emSignal` at the beginning and make sure receivers can get a const-reference or -pointer to it.
- Call the method `Signal(signal)` in any engine, with your signal object as the argument, whenever you actually want to send the signal.

And here is how you can receive a signal in an engine:

- Make sure the engine is woken up whenever the signal is sent. This can be done by calling `AddWakeUpSignal(signal)` on the engine, e.g. in the constructor.
- Overload the `Cycle` method of `emEngine`, in order to ask for the signal by calling `IsSignaled(signal)`. Also, do not forget to call the base class implementation of `Cycle` too, if you do not know it is empty.
- Return `false` form `Cycle`, except the base class implementation returned `true`. (`false` means that the engine is not woken up regularly on next time slice, i.e. that you don't want polling.)

The following programming example demonstrates all that. It has the class `MyButton`, which is a panel that sends a signal whenever the user clicks with the mouse on it. And it has the class `MyPanel` which receives that signal in order to increase a shown counter on every button click. In addition, `MyPanel` also demonstrates the use of the class `emTimer` with another counter. `emTimer` can generate periodic and non-periodic timer signals.

Besides, this is the first example with more than one panel: `MyPanel` creates and lays out a `MyButton` as its child panel.

`SignalExample.cpp (essential part)`

    //================================== MyButton ==================================

    class MyButton : public emPanel {
    public:
            MyButton(ParentArg parent, const emString & name);
            const emSignal & GetClickSignal() const;
    protected:
            virtual void Input(emInputEvent & event, const emInputState & state,
                               double mx, double my);
            virtual void Paint(const emPainter & painter, emColor canvasColor);
    private:
            emSignal ClickSignal;
    };

    MyButton::MyButton(ParentArg parent, const emString & name)
            : emPanel(parent,name)
    {
    }

    const emSignal & MyButton::GetClickSignal() const
    {
            return ClickSignal;
    }

    void MyButton::Input(
            emInputEvent & event, const emInputState & state, double mx, double my
    )
    {
            if (event.IsLeftButton()) Signal(ClickSignal);
            emPanel::Input(event,state,mx,my);
    }

    void MyButton::Paint(const emPainter & painter, emColor canvasColor)
    {
            painter.Clear(0xC0C0C0FF,canvasColor);
            painter.PaintTextBoxed(0,0,1,GetHeight(),"Click Me",1,emColor::GREEN);
    }

    //================================== MyPanel ===================================

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual bool Cycle();
            virtual void Paint(const emPainter & painter, emColor canvasColor);
    private:
            unsigned Counter1, Counter2;
            MyButton * Button;
            emTimer Timer;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emPanel(parent,name),
            Timer(GetScheduler())
    {
            Counter1=0;
            Counter2=0;
            Button=new MyButton(this,"button");
            Button->Layout(0.1,0.1,0.8,0.1);
            Timer.Start(1000,true);
            AddWakeUpSignal(Button->GetClickSignal());
            AddWakeUpSignal(Timer.GetSignal());
    }

    bool MyPanel::Cycle()
    {
            if (IsSignaled(Button->GetClickSignal())) {
                    Counter1++;
                    InvalidatePainting();
            }
            if (IsSignaled(Timer.GetSignal())) {
                    Counter2++;
                    InvalidatePainting();
            }
            return emPanel::Cycle();
    }

    void MyPanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            emString str=emString::Format(
                    "Button Signals: %u\nTimer Signals: %u",Counter1,Counter2
            );
            painter.PaintTextBoxed(0,0.3,1,GetHeight()-0.3,str,.1,0xFFFF80FF);
    }

# 8 Panel tree expansion

A real application may have much more than one or two panels. A panel can have child panels, which also can have child panels and so on. After all, you could have a large tree of panels with extreme depth, or even with infinite depth - virtually.

In order to identify the children of a panel, each panel has a name which must be unique across its sisters. The name can be any character string and is usually given as the second argument to the constructor (first argument is the parent). The names of the panels on a path in the tree are making up the panel identification within the window or view. Mainly, these identifications are used by the bookmarks implementation of Eagle Mode.

Panels should usually be created with the `new` operator, but you do not need to care about the deletion, because the parent destructor does that for you.

An important point is that the panels of a large tree should be created only when they are needed. Otherwise your application may waste to much memory and CPU time if it shows thousands or even millions of panels. Goal is to create and destroy the panels dynamically so that there are not much more than those panels which are actually visible or which have visible descendants, and which are not too small on screen. There are two ways to solve that: manually and automatically.

The manual way would mean that you have to program all the dynamics yourself, but it gives you a little bit more freedom. If you ever want to go that way, you could start by reading the comments on the methods `GetViewCondition` and `Notice` of `emPanel`. But for now, lets continue with the easier way:

The automatic way of panel tree expansion and -shrinking is really easy. All you have to do is to call `EnableAutoExpansion()` in the constructor, and to overload the method `AutoExpand` of `emPanel` in order to create all your child panels in it with the `new` operator. There is also the counterpart method `AutoShrink`, but it is seldom to be overloaded, because its default implementation deletes exactly all the child panels which are created in `AutoExpand`. You suspect it: `AutoExpand` and `AutoShrink` are called automatically when it is a good idea to create or destroy the child panels depending on the zoom and scroll state of the view (but only if you did not forget to call `EnableAutoExpansion()` - pitfall warning!).

Another important point is to layout your child panels, which means to position and size them within the coordinate system of the parent panel. You can do this by calling `Layout(x,y,w,h,canvasColor)` on the child panel. In addition to the position and size, that method also gets the canvas color for the whole child panel. You should know about the canvas color since reading the paint chapter, as well as about the own coordinate system of a panel.

After a panel has been constructed, it is laid out outside the parent panel by default. This means the child panel is not shown by default, because child panels are always clipped by the rectangle of the parent. So you really have to call the `Layout` method if you want your child panel to be shown. One possibility is to do that directly after creating the panel, maybe in you implementation of `AutoExpand`. This is absolutely okay. But sometimes you may want to program a more dynamic layout which depends on the layout of the parent, say its height/width-ratio. Then it is a good idea to implement your `Layout` calls in an overloaded version of the method `LayoutChildren` of `emPanel`. That method is called automatically after new child panels have been created, and after the parent layout has changed.

One last word on the `Layout` method: The root panel only needs the height/width-ratio, and therefore it ignores everything else from the `Layout` call.

The following programming example demonstrates the overloading of `AutoExpand` and `LayoutChildren`. It simply shows a (virtually) infinite tree of black and white panels, where each panel has four children. Remember that the children are deleted automatically by the default implementation of `AutoShrink`. Therefore, the example uses the smart pointer class `emCrossPtr` to refer to the child panels. Such a pointer sets itself automatically to `NULL` when the object is deleted. And so we can safely test for existence of the child panels in the implementation of `LayoutChildren`. Another way would be to keep no pointers at all and to ask for the child panels by name whenever needed, using the `GetChild` method of `emPanel`. But that would be a little bit slower.

`TreeExpansionExample.cpp (essential part)`

    class MyPanel : public emPanel {
    public:
            MyPanel(ParentArg parent, const emString & name, emColor bgcolor=0xFFFFFFFF);
    protected:
            virtual bool IsOpaque();
            virtual void Paint(const emPainter & painter, emColor canvasColor);
            virtual void AutoExpand();
            virtual void LayoutChildren();
    private:
            emColor BackgroundColor;
            emCrossPtr<MyPanel> Child[4];
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name, emColor bgcolor)
            : emPanel(parent,name)
    {
            BackgroundColor=bgcolor;
            EnableAutoExpansion(); // Don't forget this one!!!
    }

    bool MyPanel::IsOpaque()
    {
            return BackgroundColor.IsOpaque();
    }

    void MyPanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            painter.Clear(BackgroundColor,canvasColor);
    }

    void MyPanel::AutoExpand()
    {
            for (int i=0; i<4; i++) {
                    Child[i]=new MyPanel(
                            this,
                            emString::Format("%d",i),
                            BackgroundColor.Get()^0xFFFFFF00
                    );
            }
    }

    void MyPanel::LayoutChildren()
    {
            for (int i=0; i<4; i++) {
                    if (Child[i]) {
                            Child[i]->Layout(
                                    0.1+(i&1)*0.5,
                                    (0.1+(i&2)*0.25)*GetHeight(),
                                    0.3,
                                    0.3*GetHeight(),
                                    BackgroundColor
                            );
                    }
            }
    }

# 9 The toolkit panels

There is a set of toolkit classes ("emTk" classes) for frequently needed user interface components like buttons, text fields and so on. Most of them are panel classes, but not all. Here is a class hierarchy diagram of all the toolkit classes:

Class chart of the toolkit classes.

As you see, the class `emTkBorder` is the super class of all the other toolkit panels. It has lots of common properties: the look, the border, the label (caption + description + icon), the how-to text, and the auxiliary area. The look is expressed as an instance of `emTkLook`, which allows to configure the colors. There is a simple derivative of `emTkBorder` which shows the label as the content instead of in the border: `emTkLabel`.

`emTkTiling` acts as a container and is able to layout its child panels automatically. It is borderless and unfocusable by default and so is meant for solving layout problems only. But its derivative `emTkGroup` is focusable and shows a group border by default, so that it can easily be used for grouping things visually together. A complete different container is `emTkTunnel` which just shows a "tunnel" with one custom child panel at the end. And another one is `emTkSplitter` which allows to have two custom child panels separated by a draggable bar, either horizontally or vertically.

The classes `emTkButton`, `emTkCheckButton`, `emTkRadioButton`, `emTkCheckBox`, and `emTkRadioBox` may not need much explanation. A button receives mouse clicks to trigger something, a check button receives mouse clicks to switch something on or off, and a radio button is similar but the idea is to have always exactly one radio button on in a group. Boxes are like buttons, but they look different.

Finally there are panel classes for data fields. An `emTkTextField` can be used for viewing or editing a plain text, either single- or multi-line. An `emTkScalarField` can be used for viewing or editing a scalar value or number, or a one-of-many choice with individual texts. `emTkColorField` is for viewing or editing colors.

`emTkDialog` is not a panel but an `emWindow`. You can use it to create a typical popup dialog with OK and Cancel buttons, or with custom buttons. It also provides a function for creating a simple popup dialog which just shows a message. Example:

> `emTkDialog::ShowMessage(GetViewContext(),"Message","Hello World!");`

Note that each window requires a parent context (contexts are explained in a later chapter). That could also be a window, or a view of a window, or the root context. In the example above we set the parent context from `GetViewContext()`, which is a method of `emPanel`.

The following programming example demonstrates the use of some toolkit panels. Best is to have it running while trying to understand the code. (Please refer to the first chapter if you do not yet know how to run the examples.)

`ToolkitExample.cpp (essential part)`

    class MyPanel : public emTkGroup {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual bool Cycle();
    private:
            static void ScalarField2_TextOfValue(
                    char * buf, int bufSize, emInt64 value, emUInt64 markInterval,
                    void * context
            );
            emTkTextField * Message;
            emTkButton * Button;
            emTkCheckButton * CheckButton;
            emTkRadioButton::Group * RadioGroup;
            emTkTextField * TextField;
            emTkScalarField * ScalarField1;
            emTkScalarField * ScalarField2;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emTkGroup(parent,name,"Toolkit Example")
    {
            Message=new emTkTextField(this,"msg","Message");
            Message->SetDescription("This text field shows messages about your input.");

            Button=new emTkButton(this,"b","Button");
            AddWakeUpSignal(Button->GetClickSignal());

            CheckButton=new emTkCheckButton(this,"cb","Check Button");
            AddWakeUpSignal(CheckButton->GetCheckSignal());

            RadioGroup=new emTkRadioButton::Group(this,"rg","Radio Group");
            RadioGroup->SetBorderScaling(4.0);
            new emTkRadioBox(RadioGroup,"rb1","Radio Box 1");
            new emTkRadioBox(RadioGroup,"rb2","Radio Box 2");
            new emTkRadioBox(RadioGroup,"rb3","Radio Box 3");
            AddWakeUpSignal(RadioGroup->GetCheckSignal());

            TextField=new emTkTextField(this,"tf","Text Field");
            TextField->SetEditable();
            TextField->SetMultiLineMode();
            AddWakeUpSignal(TextField->GetTextSignal());

            ScalarField1=new emTkScalarField(this,"sf1","Scalar Field 1");
            ScalarField1->SetEditable();
            ScalarField1->SetMinMaxValues(0,100);
            ScalarField1->SetScaleMarkIntervals(50,10,5,1,0);
            AddWakeUpSignal(ScalarField1->GetValueSignal());

            ScalarField2=new emTkScalarField(this,"sf2","Scalar Field 2");
            ScalarField2->SetEditable();
            ScalarField2->SetMinMaxValues(-1,1);
            ScalarField2->SetTextOfValueFunc(ScalarField2_TextOfValue);
            AddWakeUpSignal(ScalarField2->GetValueSignal());
    }

    bool MyPanel::Cycle()
    {
            if (IsSignaled(Button->GetClickSignal())) {
                    Message->SetText("Button clicked");
            }
            if (IsSignaled(CheckButton->GetCheckSignal())) {
                    Message->SetText(emString::Format(
                            "Check Button switched %s",
                            CheckButton->IsChecked() ? "on" : "off"
                    ));
            }
            if (IsSignaled(RadioGroup->GetCheckSignal())) {
                    Message->SetText(emString::Format(
                            "Radio Box %d set",
                            RadioGroup->GetCheckIndex() + 1
                    ));
            }
            if (IsSignaled(TextField->GetTextSignal())) {
                    Message->SetText(emString::Format(
                            "Text Field changed to \"%s\"",
                            TextField->GetText().Get()
                    ));
            }
            if (IsSignaled(ScalarField1->GetValueSignal())) {
                    Message->SetText(emString::Format(
                            "Scalar Field 1 changed to %d",
                            (int)ScalarField1->GetValue()
                    ));
            }
            if (IsSignaled(ScalarField2->GetValueSignal())) {
                    Message->SetText(emString::Format(
                            "Scalar Field 2 changed to %d",
                            (int)ScalarField2->GetValue()
                    ));
            }
            return emTkGroup::Cycle();
    }

    void MyPanel::ScalarField2_TextOfValue(
            char * buf, int bufSize, emInt64 value, emUInt64 markInterval, void * context
    )
    {
            if (value<0) strcpy(buf,"left");
            else if (value==0) strcpy(buf,"middle");
            else strcpy(buf,"right");
    }

# 10 Models and contexts

Now after telling you so much about programming the front end of an application with `emPanel` and its derivatives, it is time to introduce the concepts for programming the things that work behind the scenes.

Usually, an application manages some data which is shown or edited through the panels. There are some reasons why it could be a bad idea to hold the data directly in the panels. The most important reason is that there could be multiple panels which show the same data. Remember that Eagle Mode supports to work with multiple windows, where every window shows the same virtual cosmos. This way, every panel of the virtual cosmos can exist multiple times, but they should not duplicate the data. Though, sometimes you may have some kind of data which shall be duplicated for every window, but which shall still be shared by multiple panels within a window (example: view settings like the directory sorting in the file manager).

Another reason to program the data outside the panels could be caching, because panels can be created and destroyed quite often through zooming and scrolling by the user. In addition, the data could lie outside the application, maybe in a database or on a remote computer. Then it is required to have an internal data interface which can be used by multiple panels. Finally, we need a good concept for binding the panels with their data at run-time, in order to keep the framework/plugin structure as simple as possible.

All those problems of sharing, caching, interfacing and late binding can be solved quite easily with the data class `emModel`, its container class `emContext`, and its referrer class `emRef`. That is what this chapter is about.

The idea is to program the data or data interface in a derivative of `emModel`. Either you could fill it with public member variables which make up the data, or you could define nice methods in order to create an abstract data type. You have all freedom there. But there is one important peculiarity of `emModel`: Constructors and destructors should always be kept protected. Instead of public constructors and destructors, a model class always has a public static method named `Acquire`, which is called if one wants to get a reference to a model. `Acquire` searches for an already existing model by an identification, and if does not find one, it creates and registers a new model.

The identification of a model consists of three things:

- The final class of the model (not any base class!).
- The context of the model (instance of `emContext`).
- The name of the model (any character string).

In order to hand the whole identification to the `Acquire` call, the declaration of `Acquire` typically looks like in the following example:

      class MyModel : public emModel {
      public:
          static emRef<MyModel> Acquire(
              emContext & context, const emString & name, bool common=true
          );
          ...

The additional argument `common` allows to create a non-common model which is not shared. But that is needed very seldom, and therefore many derivatives do not have that argument. Some derivatives also suppress the `name` argument, and they may require a certain context class.

Because the implementations of `Acquire` are so similar, there are two macros in `emModel` which can help there: `EM_IMPL_ACQUIRE` and `EM_IMPL_ACQUIRE_COMMON`. Therewith a typical implementation looks like this:

      static emRef<MyModel> MyModel::Acquire(
          emContext & context, const emString & name, bool common
      )
      {
          EM_IMPL_ACQUIRE(MyModel,context,name,common)
      }

After you have programmed your data in a model and defined an `Acquire` method, you must decide in which `emContext` your model should live. If you want the model to be shared by all panels, it would have to be the instance of `emRootContext`. This is the context for things that could also be seen as "global variables", and it can be get by calling `GetRootContext()` in all high-level classes. Another possible context would be the view or the window in which your panel lives (`emView` and `emWindow` are derivatives of `emContext`). You can get them by calling `GetView()` or `GetWindow()` in `emPanel`.

Finally you may want your model to have a cache effect. Therefore you can simply call the method `SetMinCommonLifetime(seconds)` of `emModel`. It tells how long the model shall be hold by the context when there are no referrers. Normally a model is deleted as soon as the last reference (of class `emRef`) is destructed.

That is also the reason why you should normally not use any other pointer or reference type than `emRef` for models. But if you want a weak reference, you can use `emCrossPtr`.

The API also provides some useful derivatives of `emModel`. One is `emFileModel`, which is a powerful base class for file interfaces. Another is the template class `emVarModel`, which allows to acquire and use a one-variable model without programming a model class. Below is a diagram that shows the class hierarchies of `emContext` and `emModel`, and it completes the hierarchy of `emPanel` (when seeing it together with the chart from the toolkit chapter).

Class chart of contexts, models and panels.

Now to the programming example at the end of this chapter. When it is running, it shows a window with a group panel which has a button and a scalar field. The scalar field allows to view and edit an integer variable, which lies in a model in the root context. The button allows to create additional windows which show the same things. The important effect is that if the user sets the scalar field of one window to another value, the scalar fields of the other windows follow that change automatically so that all scalar field always show the same value.

The code of the example consists of two classes: a model class (`MyModel`) and a panel class (`MyPanel`). The model class simply contains an integer as the data. It defines a nice interface with a getter and setter, and with a signal for tracking changes. The panel refers to that model through an `emRef``<MyModel>`, and it creates or finds the model in the root context by calling `MyModel::Acquire(GetRootContext(),"test")`. Quiz: What happens if you replace `GetRootContext()` by `GetView()` in that call? (Answer: each window gets its own `MyModel` and so the scalar field values are no longer the same).

Note that the example could be shortened by sparing the definition of `MyModel` and using `emVarSigModel` instead, but only as long as the data is just a single variable.

`ModelExample.cpp (essential part)`

    //================================== MyModel ===================================

    class MyModel : public emModel {
    public:
            static emRef<MyModel> Acquire(
                    emContext & context, const emString & name, bool common=true
            );
            int GetData() const;
            void SetData(int data);
            const emSignal & GetChangeSignal() const;
    protected:
            MyModel(emContext & context, const emString & name);
    private:
            int Data;
            emSignal ChangeSignal;
    };

    emRef<MyModel> MyModel::Acquire(
            emContext & context, const emString & name, bool common
    )
    {
            EM_IMPL_ACQUIRE(MyModel,context,name,common)
    }

    int MyModel::GetData() const
    {
            return Data;
    }

    void MyModel::SetData(int data)
    {
            if (Data!=data) {
                    Data=data;
                    Signal(ChangeSignal);
            }
    }

    const emSignal & MyModel::GetChangeSignal() const
    {
            return ChangeSignal;
    }

    MyModel::MyModel(emContext & context, const emString & name)
            : emModel(context,name)
    {
            Data=0;
    }

    //================================== MyPanel ===================================

    class MyPanel : public emTkGroup {
    public:
            MyPanel(ParentArg parent, const emString & name);
    protected:
            virtual bool Cycle();
    private:
            emRef<MyModel> Model;
            emTkButton * BtNewWin;
            emTkScalarField * SField;
    };

    MyPanel::MyPanel(ParentArg parent, const emString & name)
            : emTkGroup(parent,name,"Model Example")
    {
            Model=MyModel::Acquire(GetRootContext(),"test");
            AddWakeUpSignal(Model->GetChangeSignal());

            BtNewWin=new emTkButton(this,"bnw","New Window");
            AddWakeUpSignal(BtNewWin->GetClickSignal());

            SField=new emTkScalarField(this,"sf","Data");
            SField->SetEditable();
            SField->SetValue(Model->GetData());
            AddWakeUpSignal(SField->GetValueSignal());

            SetSpace(0.2,0.8,0.2,0.8);
    }

    bool MyPanel::Cycle()
    {
            if (IsSignaled(BtNewWin->GetClickSignal())) {
                    emWindow * window=new emWindow(GetRootContext());
                    window->SetWindowFlags(emWindow::WF_AUTO_DELETE);
                    window->SetViewFlags(emView::VF_ROOT_SAME_TALLNESS);
                    new MyPanel(window,"root");
            }
            if (IsSignaled(Model->GetChangeSignal())) {
                    SField->SetValue(Model->GetData());
            }
            if (IsSignaled(SField->GetValueSignal())) {
                    Model->SetData((int)SField->GetValue());
            }
            return emTkGroup::Cycle();
    }

# 11 Plugin interface

This chapter finally explains the plugin interface and brings a complete example for that. The name of the interface is `emFpPlugin`. That is a shortcut for "Eagle Mode File Panel Plugin", and it shall say that a plugin of this type creates a panel which acts as a user interface for viewing or editing a file. Each plugin is assigned to a file name ending, and so Eagle Mode knows which plugin to take for which file.

Thus, before programming an `emFpPlugin`, you have to thing about what type of file you want to interface. A picture viewer would interface picture files, and a video player would interface video files - that is easy. But often you may have to invent an individual file type for the plugin. For example, a game could interface a special save game file in order to make the game state persistent. However, if you do not need any file to be interfaced by the plugin, you would have to invent at least a unique file name ending and create an empty file with it.

The next step would be to program the plugin itself. It must be a shared link library in the `$EM_DIR/lib` directory, and it must provide a function of type `emFpPluginFunc`. That function is called by Eagle Mode when a new panel shall be created by the plugin. The function must have C linkage, not C++. Therefore the function has to be defined in an `extern "C"` block. The function must have a unique name and it must have exactly the arguments described in `emFpPluginFunc`. Please check that carefully, because the interface is not type-safe there.

Furthermore, a small configuration file must be written. It tells at least about the library and the file name ending of interfaced files. The configuration file format is described in the emFpPlugin chapter of the Advanced Configuration document. Please read there. Within the program, such a configuration is represented by an `emFpPlugin`.

Finally, if the plugin shall be shown in the virtual cosmos, a cosmos item must be written. This is described in the chapter about virtual cosmos configuration in the Advanced Configuration document.

Now to the plugin example:

The plugin example is a small vector drawing program, in which the user can draw just lines. The drawing is shown in the virtual cosmos, and the lines are drawn by dragging with the left mouse button. There is also a control panel with a little help text, a color field for setting the color of the next line to be drawn, and a button in a tunnel for clearing the drawing. The drawing is automatically stored in a file in order to keep all changes persistent. The plugin and the file format is given the name `PlEx` (shortcut for Plugin Example).

The source files of the plugin can be found in `$EM_DIR/doc/examples/CppApiExamples/PluginExample`, but they are also shown more below in this document. First, here comes an overview of all the files:

> File | Description
> `src/PlEx/PlEx.cpp` | C++ source file.
> `makers/PlEx.maker.pm` | Maker script that compiles and links the source.
> `etc/emCore/FpPlugins/PlEx.emFpPlugin` | Plugin configuration file.
> `etc/emMain/VcItems/PlEx1.emVcItem` | Virtual cosmos item configuration.
> `etc/emMain/VcItemFiles/PlEx1.PlEx` | Virtual cosmos item file.
> `etcw/emCore/FpPlugins/PlEx.emFpPlugin etcw/emMain/VcItems/PlEx1.emVcItem etcw/emMain/VcItemFiles/PlEx1.PlEx` | These files are identical with the above in `etc`, because our example is 100% portable (`etc` is for UNIX/Linux, `etcw` is for Windows).

The Eagle Mode project is made so that plugin installation does not need to modify any existing files. It just requires to add files. So if you want to install the plugin example and play with it, simply copy the plugin source files with their relative paths into the directory tree of the Eagle Mode source package, and build, install, and run Eagle Mode as usual. You should find the drawing in the virtual cosmos then.

The rest of this chapter describes and shows all the plugin source files in detail. The description always comes before the file. First, we have the C++ source file. It has four sections and it uses some API elements not yet described. Therefore it requires some more words:

PlExFileModel

The first class in the C++ source file is `PlExFileModel`. It defines a file format for the drawing and provides an interface for loading, holding and saving the file. It is derived from the classes `emRecFileModel` and `emStructRec` by multiple inheritance. This requires a few explanations:

"Rec" is a shortcut for "recordable data type" or just "record". The header file `emRec.h` contains lots of classes which can be used to define data structures that can easily be saved and loaded to and from files (it could be seen as a replacement for XML). For details, please read the comments of the base class `emRec` first.

`emStructRec` is an `emRec` that stands for a structured data type (like a C `struct`). `PlExFileModel` is derived from it in order to give it two members: an `emColorRec` for the current color, and an `emTArrayRec` for the array of lines. And for the elements of that array, `PlExFileModel` defines the member class `LineRec` as another `emStructRec`.

`emFileModel` is an abstract `emModel` that can be used to interface files. It does not define the file format, but it provides everything else: loading states, automatic loading, parallelism, priorities, memory limits.

`emRecFileModel` is simply an `emFileModel` that uses an `emRec` as the file format definition. One just has to give it the `emRec` by calling its method `PostConstruct(rec)`, and the file model is complete. `PlExFileModel` gives itself by calling `PostConstruct(*this)` because - you remember - it is also a derivative of `emStructRec`.

PlExControlPanel

The class `PlExControlPanel` is for the control panel. It uses the toolkit classes to show a help text, a color field, and a clear button. It connects the color field and the clear button to an `PlExFileModel` through listening to signals of both ends and reacting accordingly.

PlExFilePanel

A drawing itself is shown and edited through an instance of `PlExFilePanel`. That class uses many concepts already explained in this tutorial, but there is also something new:

`PlExFilePanel` is derived from `emFilePanel`, because the counterpart, `PlExFileModel`, is indirectly derived from `emFileModel`. An `emFilePanel` solves all the problems of managing an `emFileModelClient`, showing the progress of loading the file, displaying error messages, and so on. Therefore the implementation of `PlExFilePanel` only has to care about situations where the file model is in a fine loaded state. This can be asked with the `IsVFSGood()` method of `emFilePanel`. A signal for that state can be get with the `GetVirFileStateSignal()` method of the same class. See that the implementation of `PlExFilePanel` calls `IsVFSGood()` at the beginnings of `Input`, `IsOpaque`, `Paint` and `CreateControlPanel`. If the result is false, it simply forwards the calls to the original implementation of `emFilePanel` without doing anything else.

Another new point is the overloading of the `CreateControlPanel` method of `emPanel`. It is called by Eagle Mode when the panel gets active in order to create and return a control panel for it. Returning `NULL` means to have no control panel. A change of the results can be indicated by calling `InvalidateControlPanel()` so that the old control panel is deleted and that `CreateControlPanel` is called again. Therefore `PlExFilePanel` calls `InvalidateControlPanel()` on every change of `IsVFSGood()`. It does that in the implementation of `Cycle()`.

emPlExFpPluginFunc

The last section of the C++ source file defines the function `emPlExFpPluginFunc` which matches the `emFpPluginFunc` function type. It simply acquires a `PlExFileModel` in the root context for a given file path and creates an `PlExFilePanel` on that model.

`src/PlEx/PlEx.cpp`

    #include <emCore/emRecFileModel.h>
    #include <emCore/emFilePanel.h>
    #include <emCore/emToolkit.h>
    #include <emCore/emFpPlugin.h>

    //=============================== PlExFileModel ================================

    class PlExFileModel : public emRecFileModel, public emStructRec
    {
    public:

            static emRef<PlExFileModel> Acquire(
                    emContext & context, const emString & name, bool common=true
            );

            virtual const char * GetFormatName() const;

            class LineRec : public emStructRec {
            public:
                    LineRec();
                    emDoubleRec X1,Y1,X2,Y2;
                    emDoubleRec Thickness;
                    emColorRec Color;
            };

            emColorRec CurrentColor;
            emTArrayRec<LineRec> Lines;

    protected:

            PlExFileModel(emContext & context, const emString & name);
    };

    emRef<PlExFileModel> PlExFileModel::Acquire(
            emContext & context, const emString & name, bool common
    )
    {
            EM_IMPL_ACQUIRE(PlExFileModel,context,name,common)
    }

    const char * PlExFileModel::GetFormatName() const
    {
            return "PlEx";
    }

    PlExFileModel::LineRec::LineRec()
            : emStructRec(),
            X1(this,"X1"),
            Y1(this,"Y1"),
            X2(this,"X2"),
            Y2(this,"Y2"),
            Thickness(this,"Thickness"),
            Color(this,"Color")
    {
    }

    PlExFileModel::PlExFileModel(emContext & context, const emString & name)
            : emRecFileModel(context,name), emStructRec(),
            CurrentColor(this,"CurrentColor"),
            Lines(this,"Lines")
    {
            PostConstruct(*this);
    }

    //============================== PlExControlPanel ==============================

    class PlExControlPanel : public emTkGroup {
    public:
            PlExControlPanel(ParentArg parent, const emString & name,
                             emRef<PlExFileModel> model);
    protected:
            virtual bool Cycle();
    private:
            emRef<PlExFileModel> Model;
            emTkColorField * ColorField;
            emTkButton * ClearButton;
    };

    PlExControlPanel::PlExControlPanel(
            ParentArg parent, const emString & name, emRef<PlExFileModel> model
    )
            : emTkGroup(parent,name,"PlEx")
    {
            Model=model;
            SetPrefChildTallness(0.2);
            SetPrefChildTallness(0.4,1);
            new emTkLabel(
                    new emTkGroup(this,"about","About"),
                    "about",
                    "This is PlEx - a plugin example.\n"
                    "It allows to draw lines with the\n"
                    "left mouse button.\n"
            );
            ColorField=new emTkColorField(this,"color","Color");
            ColorField->SetColor(Model->CurrentColor);
            ColorField->SetEditable();
            ClearButton=new emTkButton(
                    new emTkTunnel(this,"tunnel","Clear"),
                    "clear","Clear"
            );
            AddWakeUpSignal(ColorField->GetColorSignal());
            AddWakeUpSignal(ClearButton->GetClickSignal());
            AddWakeUpSignal(Model->GetChangeSignal());
    }

    bool PlExControlPanel::Cycle()
    {
            if (IsSignaled(ColorField->GetColorSignal())) {
                    Model->CurrentColor.Set(ColorField->GetColor());
                    Model->Save(true);
            }
            if (IsSignaled(ClearButton->GetClickSignal())) {
                    Model->Lines.SetCount(0);
                    Model->Save(true);
            }
            if (IsSignaled(Model->GetChangeSignal())) {
                    ColorField->SetColor(Model->CurrentColor);
            }
            return emTkGroup::Cycle();
    }

    //=============================== PlExFilePanel ================================

    class PlExFilePanel : public emFilePanel {
    public:
            PlExFilePanel(ParentArg parent, const emString & name,
                          emRef<PlExFileModel> model);
    protected:
            virtual bool Cycle();
            virtual void Input(emInputEvent & event, const emInputState & state,
                               double mx, double my);
            virtual bool IsOpaque();
            virtual void Paint(const emPainter & painter, emColor canvasColor);
            virtual emPanel * CreateControlPanel(ParentArg parent,
                                                 const emString & name);
    private:
            emRef<PlExFileModel> Model;
            bool Drawing;
            double X1,Y1,X2,Y2;
            double Thickness;
            bool ControlPanelShown;
    };

    PlExFilePanel::PlExFilePanel(
            ParentArg parent, const emString & name,
            emRef<PlExFileModel> model
    )
            : emFilePanel(parent,name,model)
    {
            Model=model;
            Drawing=false;
            ControlPanelShown=false;
            AddWakeUpSignal(GetVirFileStateSignal());
            AddWakeUpSignal(Model->GetChangeSignal());
    }

    bool PlExFilePanel::Cycle()
    {
            if (IsSignaled(GetVirFileStateSignal())) {
                    if (ControlPanelShown!=IsVFSGood()) {
                            ControlPanelShown=IsVFSGood();
                            InvalidateControlPanel();
                    }
            }
            if (IsSignaled(Model->GetChangeSignal())) {
                    InvalidatePainting();
            }
            return emFilePanel::Cycle();
    }

    void PlExFilePanel::Input(
            emInputEvent & event, const emInputState & state, double mx, double my
    )
    {
            if (IsVFSGood()) {
                    if (event.IsKey(EM_KEY_LEFT_BUTTON)) {
                            Drawing=true;
                            X1=X2=mx;
                            Y1=Y2=my;
                            Thickness=5.0/(GetViewedWidth()+GetViewedHeight());
                            InvalidatePainting();
                    }
                    if (Drawing && (X2!=mx || Y2!=mx)) {
                            X2=mx;
                            Y2=my;
                            InvalidatePainting();
                    }
                    if (Drawing && !state.Get(EM_KEY_LEFT_BUTTON)) {
                            Drawing=false;
                            int i=Model->Lines.GetCount();
                            Model->Lines.SetCount(i+1);
                            Model->Lines[i].X1=X1;
                            Model->Lines[i].Y1=Y1;
                            Model->Lines[i].X2=X2;
                            Model->Lines[i].Y2=Y2;
                            Model->Lines[i].Thickness=Thickness;
                            Model->Lines[i].Color=Model->CurrentColor.Get();
                            Model->Save(true);
                            InvalidatePainting();
                    }
            }
            emFilePanel::Input(event,state,mx,my);
    }

    bool PlExFilePanel::IsOpaque()
    {
            if (IsVFSGood()) return true;
            else return emFilePanel::IsOpaque();
    }

    void PlExFilePanel::Paint(const emPainter & painter, emColor canvasColor)
    {
            if (IsVFSGood()) {
                    painter.Clear(emColor::WHITE,canvasColor);
                    for (int i=0; i<Model->Lines.GetCount(); i++) {
                            painter.PaintLine(
                                    Model->Lines[i].X1,
                                    Model->Lines[i].Y1,
                                    Model->Lines[i].X2,
                                    Model->Lines[i].Y2,
                                    Model->Lines[i].Thickness,
                                    emPainter::LC_ROUND,emPainter::LC_ROUND,
                                    Model->Lines[i].Color
                            );
                    }
                    if (Drawing) {
                            painter.PaintLine(
                                    X1,Y1,X2,Y2,Thickness,
                                    emPainter::LC_ROUND,emPainter::LC_ROUND,
                                    Model->CurrentColor
                            );
                    }
            }
            else {
                    emFilePanel::Paint(painter,canvasColor);
            }
    }

    emPanel * PlExFilePanel::CreateControlPanel(
            ParentArg parent, const emString & name
    )
    {
            if (IsVFSGood()) {
                    return new PlExControlPanel(parent,name,Model);
            }
            else {
                    return emFilePanel::CreateControlPanel(parent,name);
            }
    }

    //============================== PlExFpPluginFunc ==============================

    extern "C" {
            emPanel * PlExFpPluginFunc(
                    emPanel::ParentArg parent, const emString & name,
                    const emString & path, emFpPlugin * plugin,
                    emString * errorBuf
            )
            {
                    return new PlExFilePanel(
                            parent,
                            name,
                            PlExFileModel::Acquire(parent.GetRootContext(),path)
                    );
            }
    }

The next file we need, is the maker script. It is called by the make system to compile and link the source in order to create the shared library. For details about this, please read the Make System document.

`makers/PlEx.maker.pm`

    package PlEx;

    use strict;
    use warnings;

    sub GetDepedencies
    {
    	return ('emCore');
    }

    sub IsEssential
    {
    	return 0;
    }

    sub GetFileHandlingRules
    {
    	return ();
    }

    sub GetExtraBuildOptions
    {
    	return ();
    }

    sub Build
    {
    	shift;
    	my %options=@_;

    	system(
    		@{$options{'unicc_call'}},
    		"--math",
    		"--rtti",
    		"--exceptions",
    		"--bin-dir"       , "bin",
    		"--lib-dir"       , "lib",
    		"--obj-dir"       , "obj",
    		"--inc-search-dir", "include",
    		"--link"          , "emCore",
    		"--type"          , "dynlib",
    		"--name"          , "PlEx",
    		"src/PlEx/PlEx.cpp"
    	)==0 or return 0;

    	return 1;
    }

Furthermore, here comes the plugin configuration file. It assigns the plugin to the file name ending `.PlEx`. The plugin configuration file format is described in the emFpPlugin chapter of the Advanced Configuration document.

`etc[w]/emCore/FpPlugins/PlEx.emFpPlugin`

    #%rec:emFpPlugin%#

    FileTypes = { ".PlEx" }
    Priority = 1.0
    Library = "PlEx"
    Function = "PlExFpPluginFunc"

Next is the cosmos item configuration file. It tells where and how the plugin shall appear in the virtual cosmos, and which file to interface. Note the last line: `CopyToUser = true`. It says that the file shall be copied into a configuration directory owned by the user, because in the original place, the user, and thus the plugin, may not have permission to modify it. For details of the cosmos item file format see the chapter about virtual cosmos configuration in the Advanced Configuration document.

`etc[w]/emMain/VcItems/PlEx1.emVcItem`

    #%rec:emVirtualCosmosItem%#

    Title = "Plugin Example (PlEx)"
    PosX  = 0.3
    PosY  = 0.28
    Width = 0.05
    ContentTallness = 0.7
    BorderScaling = 2.0
    BackgroundColor = "#fff"
    BorderColor = "#c0a"
    TitleColor = "#ff0"
    FileName = "PlEx1.PlEx"
    CopyToUser = true

Last but not least, here comes the file which contains the initial drawing (a single blue line). On first start, it is copied automatically into a user directory, where it is read and modified by the plugin. The file has an `emRec` format as defined by the `PlExFileModel` class. Only the first line is required (i.e. `#%rec:PlEx%#`), which tells about the file format. Everything else is optional and has defaults.

`etc[w]/emMain/VcItemFiles/PlEx1.PlEx`

    #%rec:PlEx%#

    CurrentColor = {0 0 0}
    Lines = {
    	{
    		X1 = 0.1
    		Y1 = 0.6
    		X2 = 0.9
    		Y2 = 0.1
    		Thickness = 0.02
    		Color = {0 0 255}
    	}
    }