Cantus 3 API

Samuel Abels

February 2004


Contents

Overview

Preamble

This document has been revised last April 5, 2004. Comments appreciated.

Project Objectives

Implement a complete and flexible (audio) file (Tag-)modifier. As a first step, the program should provide minimal audio tagging capabilities, as well as input plugins for OGG and ID3 tags. Later on, plugins should provide advanced features like FreeDB support and advanced filename/tag filters.

Plugins for non tag-based modifications (e.g. "Normalize") are also thinkable but not focus of the project. Tags for other types of files (non-audio) may also be provided but are not focus of the project.

Licensing

Cantus 3 will be published under the terms of the General Public License (GPL).

Policies

Cantus 3 is free, open source software and as such development related discussions will be made on public mailing lists only. All roadmap and API proposals are to be disussed openly on that mailinglist. All CVS commits should be announced to the mailinglist. The project leaders will provide status reports on a regular basis.

Libraries and Techniques

Cantus 3 will be built around several existing libraries and based on well known techniques:

Coding Style

The source code coding style is K&R, GNU-indent options: "-kr -nut -i2". Of course, CVS commits and patches should use that format both.

Object names should start with a capitol letter. Function/method names and variables will use only lowercase letters.

Types (typedef) should have at least one capitol letter.

Backend API

The Controller (Controller Object)

The Controller is an eventbus listener, managing all other components and emitting appropriate signals. The controller is also the top-level parent object for all backend objects.

  class Controller : public SigC::Object {
  protected:
    // Prevent copys.
    Controller(Controller &c) { g_assert_not_reached(); }
    // Prevent assignments.
    Controller& operator=(Controller &c) { g_assert_not_reached(); }
    
  public:
    Controller();
    ~Controller();
    
    /* Loads the preferences from the configfile.
     */
    int configfile_load(void);
    
    /* Saves the preferences to the configfile.
     */
    int configfile_save(void);
    
    PluginHandler   *pluginhandler;
    FileInfoManager *fileinfomanager;
    Preferences     *preferences;
    ConfigFile      *configfile;
    EventBus        *eventbus;
    
  private:
    /* Triggers eventbus events and queues the files to the FileInfoManager.
     */
    void on_filelist_selection_changed(void *filenames);
    
    /* Whenever a new plugin has been registered, this updates the
     * FileInfoManager.
     */
    void on_plugins_registered(Plugin *plugin);
    
    /* This signal indicates that some file's data needs to be written to the
     * filesystem. May, for example, be triggered by a plugin when a "save" button
     * has been pressed.
     */
    void on_filelist_save_request(void *list);
    
    /* Emits an eventbus event.
     */
    void emit_file_read_finished_event(FileInfo *info);
    
    /* Emits an eventbus event.
     */
    void emit_filenames_read_finished_event(void);
    
    /* Emits an eventbus event.
     */
    void emit_preferences_changed_event(std::string key);
  };

The EventBus (EventBus Object)

The EventBus interface provides a program-wide notification mechanism. Events emitted in the bus are considered of interest of several plugins. Notifications of lesser importance should be implemented using the standard GSignal mechanism (using the Glib::Dispatcher for asynchronous events).

Groups of events are selectable through patterns by the use of wildcards.

It is expected that events will be namespaced. Events emitted by the GUI have "GUI" as their toplevel namespace. E.g.: GUI:Filelist:SelectionChanged, File:Read:Finished, Preferences:Changed, Plugin:Registered, Plugin:Unregistered etc.. The event names need to be specified as a part of the Cantus 3 API too.

Events can be registered either as GClosure, or using a SigC slot.

Event emission is synchronous.

  enum EVENTBUS_SIG_TYPES {
    EVENTBUS_SIG_SIGC,
    EVENTBUS_SIG_GCLOSURE
  };
  
  typedef struct {
    glong        id;        // A unique identifier for the listener.
    gboolean     invalid;   // Whether or not the listener is invalid.
    const gchar *pattern;   // An event pattern (glob).
    gshort      type;       // SigC-Slot or Closure?
    GClosure    *closure;   // The function (closure) to be called when the event
                            // pattern matches.
    SigC::Slot1<void, void*> sigcslot;  // The function (sigc) to be called when
                                        // the event pattern matches.
  } EventBusListener;
  
  
  class EventBus : public SigC::Object {
  protected:
    // Prevent copys.
    EventBus(EventBus &e) { g_assert_not_reached(); }
    // Prevent assignments.
    EventBus& operator=(EventBus &e) { g_assert_not_reached(); }
    
  public:
    EventBus();
    ~EventBus();
    
    /* Registers a listener for a specific event pattern; use the returned */
    /* id to unregister the listener. */
    glong add_listener(const gchar *event_pattern, GClosure *closure);
    
    /* Registers a listener for a specific event pattern; use the returned
     * id to unregister the listener. The function to be called is a SigC slot.
     */
    glong add_listener_sigc(const gchar *event_pattern,
                            SigC::Slot1<void, void*> slot);
    
    /* Invalidate the listener with the given id.
     */
    void remove_listener(glong id);
    
    /* Unregister all invalid listeners.
     */
    void delete_invalid_listeners(void);
    
    /* Emits an event in the bus.
     */
    void emit_event(const gchar *event, const GValue *values);
    
    /* Emits an event in the bus, convenience wrapper.
     */
    void emit_event_with_pointer(const gchar *event, gpointer value);
    
    /* Emits an event in the bus, convenience wrapper.
     */
    void emit_event_with_char(const gchar *event, const gchar *value);
    
    /* Emits an event in the bus, convenience wrapper.
     */
    void emit_event_with_int(const gchar *event, gint value);
    
  private:
    glong  idpool;          // Holds the id of the listener registered last.
    GList *listeners;       // Holds all listeners.
  };

Cantus 3 provides the following events:

  Filelist:Read:Start
  Filelist:Read:Finished
  Filelist:Save:Request
  File:Read:Finished
  Preferences:Changed
  Plugin:Responsibility:Changed
  GUI:Filelist:Selection:Changed
  GUI:PluginWidget:Destroyed

File Informations (FileInfo Object)

All informations regarding the current state of a file are stored in a FileInfo object. The object is filled with data by different plugins. Plugins are registered using the "Plugin" object.

This object holds two data structures (hashes): One representing the file as its contents currently are (refered to as the "real hash") (e.g. the current tag fields), while a second structure holds the same information as it is currently being edited (refered to as the "edited hash").

In addition, this object holds a history of the structure, needed for the "Undo" process, plus a future of the structure, needed for the "Redo" process.

The hash key names should be namespaced, e.g. ID3V2:DCOM, File:Name etc..

The object guarantees to be threadsafe.

  class FileInfo {
  protected:
    // Prevent copies.
    FileInfo(FileInfo &f) { g_assert_not_reached(); }
    // Prevent assignments.
    FileInfo& operator=(FileInfo &s) { g_assert_not_reached(); }
    
  public:
    /* Triggered whenever a file has been read. */
    SigC::Signal1<void, FileInfo*>  signal_read_finished;
    /* Triggered whenever a file has been written. */
    SigC::Signal1<void, FileInfo*>  signal_write_finished;
    
    /* The filename is being stored in the real hash and the edited hash as
     * well.
     */
    FileInfo(const gchar *filename);
    ~FileInfo();
    
    /* Returns the inode number of the file, or < 0 on an error.
     */
    glong get_inode(void);
    
    /* Read all file informations into the real hash using the given Input
     * Plugin.
     * Returns an errorcode or 0.
     */
    gint read(const ReadFunc func);
    
    /* Write all file informations from the real hash to the filesystem, using
     * the given output plugin.
     * Returns an errorcode or 0.
     */
    gint write(const WriteFunc func);
    
    /* Returns a pointer to the file's "edited_hash". Make sure to "lock()" when
     * you do this!
     */
    CantusHash *get_edited_hash(void);
    
    /* Lock the object from being accessed.
     */
    void lock(void);
    
    /* Unlock the object so that it can be accessed.
     */
    void unlock(void);
    
    /* Returns the filename from the real hash. Make sure to lock() before you
     * use this!
     */
    const gchar *get_filename(void);
    
    /* Returns the filename from the edited hash. Make sure to lock() before you
     * use this!
     */
    const gchar *get_edited_filename(void);
    
    /* Move the "real hash" onto an "UNDO" stack. Then, copy the edited hash into
     * the new real hash. Also, clear the "REDO" stack.
     */
    void commit(void);
    
    /* Copy the real hash into the edited hash.
     */
    void revert(void);
    
    /* Push the edited hash onto the "REDO" stack. Then, pop the last item from
     * the "UNDO" stack into the edited hash.
     * Returns FALSE when the "UNDO" list was empty so nothing has been changed.
     */
    gboolean undo(void);
    
    /* Push the edited hash onto the "UNDO" stack. Then, pop the last item from
     * the "REDO" stack into the edited hash.
     * Returns FALSE when the "REDO" list was empty so nothing has been changed.
     */
    gboolean redo(void);
    
  private:
    /* Push something to the history (for UNDO).
     */
    void history_push(CantusHash *hash);
    
    /* Get something from the history (for UNDO).
     */
    CantusHash *history_pop(void);
    
    /* Get something to the future (for REDO).
     */
    void future_push(CantusHash *hash);
    
    /* Get something from the future (for REDO).
     */
    CantusHash *future_pop(void);
    
    /* Clear the future (for REDO).
     */
    void future_clear(void);
    
    CantusHash *real_hash;            // The file's data as on the filesystem.
    CantusHash *edited_hash;          // The file's data as edited.
    std::list<CantusHash *> history;  // The history of the data for UNDO.
    std::list<CantusHash *> future;   // The future of the data for REDO.
    
    Glib::Mutex mutex;                // One mutex to lock the whole object.
  };

Reading, Writing and Caching (FileInfoManager Object)

Since the program should not need to re-read all file informations every time the fileinfo objects are kept alive somewhere. This is done by a FileInfoManager object which is responsible for loading and destroying FileInfo objects.

The FileInfoManager is implemented using glibmm's thread implementations, having (in addition to the main thread) two worker threads: One writer thread and one reader thread, both locking each other. The signals sent by the worker threads must be emitted by the main thread, because the GUI toolkit is not threadsafe. Thus, an (asynchronous) dispatcher sends a signal to the main thread, which is emitting the standard GSignal.

Caching is done by hashing the filename to the inode number and the inode number to the file's data.

Since the FileInfoManager also needs to trigger the read() function of the FileInfo object, it needs to know which plugins to invoke. Thus, it holds a list of all Plugins available.

  class FileInfoManager : public SigC::Object {
  protected:
    // Prevent copies.
    FileInfoManager(FileInfoManager &f) { g_assert_not_reached(); }
    // Prevent assignments.
    FileInfoManager& operator=(FileInfoManager &f) { g_assert_not_reached(); }
    
  public:
    FileInfoManager();
    ~FileInfoManager();
    
    /* Triggered whenever a file has been read. */
    SigC::Signal1<void, FileInfo*>  signal_file_read_finished;
    /* Triggered whenever a file could not be read (e.g. wrong permissions). */
    SigC::Signal1<void, FileInfo*>  signal_file_read_failed;
    /* Triggered whenever a file has been written. */
    SigC::Signal1<void, FileInfo*>  signal_file_write_finished;
    /* Triggered whenever a file could not be written (e.g. wrong permissions). */
    SigC::Signal1<void, FileInfo*>  signal_file_write_failed;
    /* Triggered whenever a file could not be renamed. (e.g. file exists). */
    SigC::Signal1<void, FileInfo*>  signal_file_rename_failed;
    /* Triggered whenever the read queue has been finished. */
    SigC::Signal0<void>             signal_queue_read_finished;
    /* Triggered whenever the write queue has been finished. */
    SigC::Signal0<void>             signal_queue_write_finished;
    
    /* Stores a copy of the pluginlist in the object.
     * This is required because the object invokes the FileInfo read function,
     * which needs a plugin as an argument.
     */
    void register_plugins(std::list<Plugin*> *pluginlist);
    
    /* Replaces the list of files that still need to be read. (read queue)
     * (filenames == NULL is allowed).
     */
    void readqueue_set(GList *filenames);
    
    /* Clear the read queue. (=Abort read).
     */
    void readqueue_clear(void);
    
    /* Appends stuff to the list of files that still need to be
     * written. (write queue)
     */
    void writequeue_append(GList *filenames);
    
    /* Clear the write queue. (=Abort write)
     */
    void writequeue_clear(void);
    
    /* This function locks the fileinfo object with the given name and returns
     * the hash containing all file data from the fileinfo object.
     * The FileInfo object will remain locked, so make sure to unlock!!
     */
    CantusHash *get_info_locked(const gchar *filename);
    
    /* Unlocks the FileInfo object with the given name.
     */
    void unlock(const gchar *filename);
    
    /* Returns the progress in percent as a float.
     */
    float get_progress(void);
    
  private:
    /* Unregisters all plugins.
     */
    void unregister_plugins(void);
    
    /* A worker thread doing all read operations.
     */
    void reader_func(void);         
    
    /* A worker thread doing all write operations.
     */
    void writer_func(void);
    
    /* Receives one fileinfo object from the cache. If the item is not in the
     * cache yet, it will try to read it using all apropriate plugins.
     */
    FileInfo *read(const gchar *filename);
    
    /* Receives one fileinfo object from the cache. If the item is not in the
     * cache yet, read it. Then try to write it using all apropriate plugins.
     */
    void write(gint inode);
    
    /* Checks whether the filename has been edited and renames the file.
     * Returns TRUE on success, FALSE otherwise.
     */
    gboolean try_rename(FileInfo *info);
    
    /* This dispatcher is called via an asynchronous pipe.
     * Once called, it emits all queued events via a synchronous SigC signal.
     */
    void signal_dispatcher(void);
    
    Glib::Mutex   mutex;        // One mutex for parent, reader and writer.
    Glib::Cond    readcond;     // Pushed whenever there is something to read.
    Glib::Cond    writecond;    // Pushed whenever there is something to write.
    gboolean      stopthreads;  // When true, the threads will terminate themself.
    Glib::Thread *reader;       // The reader_func() thread.
    Glib::Thread *writer;       // The writer_func() thread.
    
    std::deque<FileInfoManagerEvent*> eventqueue; // The event-dispatcher's queue.
    Glib::Dispatcher signal_dispatch;             // Triggered asynchronously to
                                                  // call the dispatcher.
    
    std::list<Plugin*>       *plugins;     // A list of all available plugins.
    std::deque<const gchar *> readqueue;   // The read queue (list of filenames).
    std::deque<glong>         writequeue;  // The write queue (list of inodes).
    
                            // A hash mapping filename to inode.
    __gnu_cxx::hash_map<std::string, glong> inodemap;
                            // A hash mapping inode to FileInfo object.
    __gnu_cxx::hash_map<glong, FileInfo*>   cache;
    
    int queued_read;        // The number of files currently queued for reading.
    int processed_read;     // The number of files read.
    int queued_write;       // The number of files currently queued for writing.
    int processed_write;    // The number of files written.
  };

Preferences (Preferences Object)

The preferences objects holds two data structures: One holding the currently active preferences, the other one holding the preferences as currently being edited. Confirming the preferences will copy the edited structure into the active structure.

  class Preferences : public SigC::Object {
  protected:
    // Prevent copys.
    Preferences(Preferences &p) { g_assert_not_reached(); }
    // Prevent assignments.
    Preferences& operator=(Preferences &p) { g_assert_not_reached(); }
    
  public:
    Preferences(void);
    ~Preferences(void);
    
    /* Triggered whenever something has been changed. */
    SigC::Signal1<void, std::string> signal_changed;
    
    /* Tests all edited preferences for sanity.
     */
    bool check_commit(void);
    
    /* Tests the preference with the given key for sanity.
     */
    bool check_commit(std::string key);
    
    /* Confirms all preferences. This also triggers a Preferences:Changed event.
     */
    bool commit(void);
    
    /* Confirms the preference with the given key. This also triggers a
     * Preferences:Changed event.
     */
    bool commit(std::string key);
    
    /* Set one preference value. Returns always TRUE.
     */
    bool set(std::string key, int type, void *value);
    
    /* Set one preference value (integer).
     */
    void set_int(std::string key, int value);
    
    /* Get one preference value (int).
     */
    int get_int(std::string key);
    
    /* Set one preference value (boolean).
     */
    void set_bool(std::string key, bool value);
    
    /* Get one preference value (char).
     */
    bool get_bool(std::string key);
    
    /* Set one preference value (char).
     */
    void set_char(std::string key, std::string value);
    
    /* Get one preference value (char).
     */
    std::string get_char(std::string key);
    
    /* Walks through all preferences calling the given slot.
     * Args passed to the slot are: The preference name, type (e.g. G_TYPE_INT)
     * and its value.
     * If the slot returns FALSE, list evaluation will be stopped.
     */
    void foreach(SigC::Slot3<bool, std::string, int, void*> slot);
    
  protected:
    
    std::map<std::string, CantusHashValue*> prefs;
    std::map<std::string, CantusHashValue*> editedprefs;
  };

The Configfile (Configfile Object)

The configfile is currently over-simplified and ugly: FIXME.

  class ConfigFile : public SigC::Object {
  public:
    ConfigFile();
    ~ConfigFile();
    
    /* Defines which file to read/write from.
     */
    void set_filename(std::string newfilename);
    
    /* Check, whether or not the file exists and is read-/writable.
     * Returns  0 if the file is read/writeable.
     *         -1 if no filename has been set yet or the file does not exist.
     *         -2 if the file is not readable.
     *         -3 if the file is not writeable.
     */
    int check(void);
    
    /* This function opens the configfile and saves all option/value pairs in the
     * given map. Returns an errorcode, or 0 on success.
     */
    int load(void);
    
    /* This function writes the configfile contents from the memory to the
     * configfile. Returns an errorcode, or 0 on success.
     */
    int save(void);
    
    /* Clear the configfile content (only in the memory).
     */
    void clear(void);
    
    /* Appends one key/value pair to the configfile (only in the memory).
     * Args passed to the method are: The key name, the value type (e.g.
     * G_TYPE_INT) and the value.
     * Returns always TRUE.
     */
    bool append(std::string key, int type, void *value);
    
    /* Walks through all lines of the file, passing every option to the slot.
     * Args passed to the slot are: The key name, the value type (e.g.
     * G_TYPE_INT) and the value.
     */
    void foreach_line(SigC::Slot3<bool, std::string, int, void*> slot);
    
  protected:
    /* Given one line of a file, this function returns the extracted key/value
     * pair.
     */
    std::pair<std::string, std::string> get_pair(const gchar *pline);
    
    /* Given one value string, this function extracts the value in the right data
     * type.
     */
    std::pair<int, void*> grab_value(std::string value);
    
    std::list<std::pair<std::string, std::string> > content;  // The file content.
    std::string filename;                                     // The file name.
  };

Plugins (Plugin Object)

This object is used to load and unload a single plugin.

The plugin can set several options (like its name etc.) by filling the C-compatible hash passed to its initializer.

  class Plugin : public SigC::Object {
  public:
    Plugin();
    ~Plugin();
    
    /* Triggered directly before the plugin will be deleted (= when the */
    /* refcounter has reached a value <= 0). */
    SigC::Signal1<void, Plugin*> signal_plugin_deleted;
    
    /* Loads a plugin and all its symbols into the object.
     */
    gint load(const gchar *filename);
    
    /* Increase a plugin's refcounter. When the refcounter is <= 0 the plugin will
     * be unloaded. On object creation, the refcounter is 1.
     */
    void ref(void);
    
    /* Decrease a plugin's refcounter.
     */
    void unref(void);
    
    /* Define the plugin's priority.
     */
    void set_priority(gint priority);
    
    /* Returns the plugin's priority.
     */
    gint get_priority(void);
    
    /* Returns the plugin name.
     */
    const gchar *get_name(void);
    
    /* Returns the name of the tags handled by this plugin.
     */
    const gchar *get_tagname(void);
    
    /* Returns the title for the plugin's notebook tab label.
     */
    const gchar *get_label(void);
    
    /* Returns the the plugin description (should be 200 chars max.).
     */
    const gchar *get_description(void);
    
    /* Returns the plugin's major version number.
     */
    gint get_majorversion(void);
    
    /* Returns the plugin's minor version number.
     */
    gint get_minorversion(void);
    
    /* Returns a function pointer to the plugin's read() function.
     */
    const ReadFunc get_readfunc(void);
    
    /* Returns a function pointer to the plugin's write() function.
     */
    const WriteFunc get_writefunc(void);
    
    /* Calls the plugin's uiwidget() function.
     */
    void *get_uiwidget(gboolean vertical);
    
    /* Given a file name, this function returns TRUE if the plugin is responsible
     * for handling this filetype, otherwise FALSE.
     */
    gboolean handles(const gchar *filename);
    
  private:
    /* Checks, whether the plugin meets all mandatory requirements. Returns TRUE
     * if the plugin is valid, otherwise FALSE.
     */
    gboolean check_plugin(void);
    
    void       *dlhandle;    // The handle for dl_open.
    CantusHash *plugindata;  // A pointer to the C-compatible plugin data.
    gint        refct;       // A refcounter indicating whether the plugin is
                             // still needed.
    gint        priority;    // A priority, to be used by the backend only.
  };

Plugin Handler (PluginHandler Object)

The plugin handler registers and unregisters plugins, as well as it returns informations regarding the available plugins.

Plugins are stored in two maps: One map is mapping filename to plugin, while the second hash is ordered by a numerical id to provide a priority mechanism.

  class PluginHandler : public SigC::Object {
  protected:
    // Prevent copys.
    PluginHandler(PluginHandler &p) { g_assert_not_reached(); }
    // Prevent assignments.
    PluginHandler& operator=(PluginHandler &p) { g_assert_not_reached(); }
    
  public:
    PluginHandler();
    ~PluginHandler();
    
    /* Triggered whenever a plugin has successfully been loaded. */
    SigC::Signal1<void, Plugin*> signal_plugin_loaded;
    /* Triggered whenever a plugin has been removed (= when the refcounter has */
    /* been decreased). */
    SigC::Signal1<void, Plugin*> signal_plugin_removed;
    /* Triggered straight before a plugin is being deleted (= when the */
    /* refcounter has reached a value <= 0). */
    SigC::Signal1<void, Plugin*> signal_plugin_deleted;
    
    /* Register a new input plugin.
     * Returns 0 on success, an errorcode < 0 otherwise.
     * Emits the Plugin::Registered signal.
     */
    gint reg(const gchar *filename, gint priority);
    
    /* Unregister an input plugin (=decrease the plugin's refcounter).
     * Emits the Plugin::Unregistered signal.
     */
    void unreg(const gchar *filename);
    
    /* Return an std::list of all plugins responsible for the given file type.
     * Don't forget to delete the list AND UNREF EVERY PLUGIN!!
     */
    std::list<Plugin*> *get_responsible_plugins(const gchar *filename);
    
    /* Given a list of filenames, this function returns a std::list of all
     * responsible plugins for the given filetypes.
     * Don't forget to delete the list AND UNREF EVERY PLUGIN!!
     */
    std::list<Plugin*> *get_responsible_plugins_from_list(GList *filenames);
    
    /* This function returns a list of all plugins. Don't forget to delete the
     * list AND UNREF EVERY PLUGIN!!
     */
    std::list<Plugin*> *get_plugins(void);
    
  protected:
            // Holds mappings between plugin filenames and "Plugin" objects.
    std::map<std::string, Plugin*> plugins_str;
    std::map<int, Plugin*>         plugins_int;
  };

Frontend API

Mainwindow (Mainwindow Object)

The Mainwindow is the GUI's parent object, invoking all other GUI objects.

  class Mainwindow : public SigC::Object {
  public:
    Mainwindow();
    ~Mainwindow();
    
    /* Triggered whenever the filebrowser selection changed.
     * The GList contains a list of all filenames currently selected.
     * Warning: The GList is only valid for the duration of the signal.
     */
    SigC::Signal1<void, GList*>   signal_filebrowser_selection_changed;
    /* Triggered whenever the "save" menu has been activated. */
    SigC::Signal0<void>           signal_menu_file_save_activate;
    /* Triggered whenever the "preferences" item has been clicked. */
    SigC::Signal0<void>           signal_menu_edit_preferences_activate;
    /* Triggered whenever the pluginarea1 visibility changed. */
    SigC::Signal1<void, gboolean> signal_pluginarea1_visibility_changed;
    /* Triggered whenever the pluginarea2 visibility changed. */
    SigC::Signal1<void, gboolean> signal_pluginarea2_visibility_changed;
    /* Triggered whenever a widget has been removed from any pluginarea. */
    SigC::Signal1<void, Plugin*>  signal_pluginarea_widget_removed;
    
    /* Builds the complete mainwindow. */
    Gtk::Window *create(void);
    
    /* Update the plugins to be shown in the pluginarea. */
    void pluginarea_update(std::list<Plugin*> *plugins);
    
    /* Updates the progressbar. */
    void set_progress(double percent);
    
    /* Make the pluginarea visible or invisible. */
    void pluginarea1_set_active(gboolean active);
    
    /* Make the pluginarea visible or invisible. */
    void pluginarea2_set_active(gboolean active);
    
    FileBrowser *filebrowser;
    
  private:
    void connect_signals(void);
    void on_menu_help_about_activate(void);
    void on_menu_view_pluginarea1_activate(void);
    void on_menu_view_pluginarea2_activate(void);
    
    Glib::RefPtr<Gnome::Glade::Xml> refXml;
    
    gboolean           lockevents;
    Gtk::Window       *pwindow;
    Gtk::Viewport     *viewport;
    Gtk::Table        *table;
    Gtk::ProgressBar  *progressbar;
    Gtk::HPaned       *hpaned;
    Gtk::Notebook     *notebook;
    PluginArea        *pluginarea1;
    PluginArea        *pluginarea2;
    AboutBox          *aboutbox;
  };

Plugin Area (PluginArea Object)

The Plugin Area provides a widget for attaching GUI plugins. It is derived from the Gtk::Notebook class.

  class PluginArea : public Gtk::Notebook {
  public:
    PluginArea(gboolean is_vertical);
    ~PluginArea();
    
    /* Triggered whenever a widget has been removed. */
    SigC::Signal1<void, Plugin *> signal_widget_removed;
    
    // Operates on single plugins.
    void      plugin_attach(Plugin *plugin);
    void      plugin_remove(Plugin *plugin);
    gboolean  plugin_is_visible(Plugin *plugin);
    
    // Operates on a list of plugins.
    void      plugins_set(std::list<Plugin*> *plugins);
    void      plugins_attach(std::list<Plugin*> *plugins);
    void      plugins_remove(std::list<Plugin*> *plugins);
    
  protected:
    std::map<Plugin*, Gtk::Widget*> visibleplugins;
    gboolean  vertical;  // Whether or not the plugin is shown in vertical pos.
    
    void      remove_unneeded(std::list<Plugin*> *required);
    gboolean  in_list(Plugin *plugin, std::list<Plugin*> *required);
  };

File Browser (FileBrowser Object)

The Filebrowser is derived from the Gtk::HPaned and consists of a directory tree and a filelist.

  class FileBrowser : public Gtk::HPaned {
  public:
    FileBrowser();
    ~FileBrowser();
    
    /* Triggered when the filelist selection has changed. */
    SigC::Signal1<void, GList*> signal_filelist_selection_changed;
    
    /* Jump to the given directory. */
    void set_dir(std::string dir);
    
    /* Specify a list of patterns that represent filenames to be shown in the
     * filelist.
     */
    void set_filepattern(const gchar **pfilepattern);
    
    /* Specify a comma-sperated list of patterns that represent filenames to be
     * shown in the filelist.
     */
    void set_filepattern(std::string str);
    
    /* Specify whether or not hidden files and directories should be visible.
     */
    void set_showhidden(gboolean show_hidden);
    
    /* Manually trigger a re-read of the current directory.
     */
    void update(void);
    
  private:
    void _on_dirtree_row_activated(
                              const Gtk::TreeModel::Path& path,
                              Gtk::TreeViewColumn *column);
    void _on_dirtree_row_expanded(
                              const Gtk::TreeModel::iterator& iter,
                              const Gtk::TreeModel::Path& path);
    void _on_dirtree_row_collapsed(
                              const Gtk::TreeModel::iterator& iter,
                              const Gtk::TreeModel::Path& path);
    void _on_dirtree_selection_changed(void);
    void _on_filelist_selection_changed(void);
    
    Gtk::TreeView *dirtree_view;
    GtkTreeStore  *dirtree;
    Gtk::TreeView *filelist_view;
    GtkListStore  *filelist;
    std::string    curdir;
    const gchar  **filepattern;
    std::string    filepattern_str;
    gboolean       showhidden;
  };

Aboutbox (AboutBox Object)

The Aboutbox is derived from the Gtk::Dialog.

  class AboutBox : public Gtk::Dialog {
  public:
    AboutBox();
    ~AboutBox();
    
    void create(void);
    
  protected:
    Gtk::Image *image;
  };

Preferences Dialog (PreferencesWindow Object)

The preferences window is mainly loaded from Glade so to provide an easy and fast way to add new preferences. Adding new fields does not require any change on the object; any events are connected automatically by an iteration over all widgets.

  class PreferencesWindow : public SigC::Object {
  public:
    PreferencesWindow(std::string gladefile, std::string gladedomain);
    ~PreferencesWindow();
    
    /* Triggered whenever the CLOSE button has been clicked. */
    SigC::Signal0<void>                             signal_button_close_clicked;
    /* Triggered whenever any preference has been changed. */
    /* Emits the preference name, type (e.g. G_TYPE_BOOL) and its value. */
    SigC::Signal3<void, std::string, gint, void *>  signal_preference_changed;
    
    /* Make the window visible. If it does not exist yet, it will be created.
     */
    void show(void);
    
    /* Hide the window.
     */
    void hide(void);
    
    /* Updates one widget according to the given value.
     */
    bool update_widget(std::string name, int type, void *value);
    
  private:
    /* Builds the complete preferences window and makes it visible.
     */
    void create(void);
    
    /* Adds all categories by iterating through the notebook tab labels.
     */
    void fill_tree(void);
    
    /* Connects all signals.
     */
    void connect_signals(void);
    
    /* Callback handler, triggered whenever another preferences category has been
     * selected.
     */
    void on_preftree_selection_changed(Gtk::TreeIter iter);
    
    /* Callback handler, triggered whenever the "close" button has been clicked.
     */
    void on_button_close_clicked(void);
    
    /* Callback handler, triggered whenever any of the preferences has been
     * edited.
     */
    void on_widget_changed(Gtk::Widget *widget);
    
    /* Given a container, this function returns a list of all children.
     * Other than "Gtk::Object::get_children()", this function works recursive.
     */
    void get_children_recursive(
                    Gtk::Container *container, std::list<Gtk::Widget*> *children);
    
    Glib::RefPtr<Gnome::Glade::Xml> refXml;       // Glade's XML-structure.
    std::string                     glade_file;   // The .glade filename.
    std::string                     glade_domain; // The .glade file domain.
    Gtk::Window                    *window;       // The toplevel window.
    Gtk::Notebook                  *notebook;     // The notebook holding prefs.
    Gtk::Viewport                  *viewport;     // Preftree parent.
    PreferencesTree                 preftree;     // Preftree.
  };

Preference Categories (PreferencesTree Object)

This object builds a treeview holding category names/icons for the preferences window.

  class PreferencesTree : public Gtk::TreeView {
  public:
    PreferencesTree();
    ~PreferencesTree();
    
    /* Triggered whenever an item has been selected. */
    SigC::Signal1<void, Gtk::TreeIter> signal_selection_changed;
    
    /* Add a category with an icon. */
    void add(std::string catname, std::string pixmapfile);
    
  private:
    void on_selection_changed(void);
    
    // List model columns.
    class ModelColumns : public Gtk::TreeModel::ColumnRecord {
    public:
      //Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > pixbuf;
      Gtk::TreeModelColumn<Glib::ustring>               caption;
      ModelColumns() {
        //add(pixbuf);
        add(caption);
      }
    };
    Glib::RefPtr<Gtk::ListStore> store;
    Gtk::CellRendererText        renderer_text;
    ModelColumns                 columns;
  };

GUI Controller (GUIController Object)

This object controls the GUI by listening to EventBus events and sending appropriate signals.

  class GUIController : public SigC::Object {
  protected:
    // Prevent copys.
    GUIController(GUIController &c) { g_assert_not_reached(); }
    // Prevent assignments.
    GUIController& operator=(GUIController &c) { g_assert_not_reached(); }
    
  public:
    GUIController();
    ~GUIController();
    
    void init(void);
    
  private:
    void on_preferences_preference_changed(std::string key,
                                           gint type,
                                           void *data);
    void on_menu_file_save_clicked(void);
    void on_menu_edit_preferences_clicked(void);
    void on_preferences_changed(void *key);
    void on_plugin_responsibility_changed(void *data);
    void on_filebrowser_selection_changed(GList *selection);
    void on_pluginarea_widget_removed(Plugin *plugin);
    void on_pluginarea1_visibility_changed(gboolean visible);
    void on_pluginarea2_visibility_changed(gboolean visible);
    bool on_progressbar_update_poll(void);
    
    PreferencesWindow *preferences;
    bool lock_on_preferences_changed;
  };

Plugin API

One focus of the program is to make plugin writing as easy as possible. Plugin initializers have several functions passed as an argument, which enables them to do operations as flexible as possible.

Plugins can be written in C++ and C as well. Thus, the initializer arguments (including the function pointers) are passed in a C-compatible g_hash_table. Plugins store their name (and other specifications) in that hash during the initialisation so that the engine is informed.

Beneath the initializer (InitFunc) and the destroyer (DestroyFunc) there are optional symbols that the plugin can supply. Refer to the list below.

  // Functions provided by plugins.
  typedef void    *(*AnyFunc)      (void);
  typedef gint     (*InitFunc)     (CantusHash *specs);
  typedef gint     (*DestroyFunc)  (void);
  typedef gboolean (*HandlesFunc)  (const gchar *filename);
  typedef gint     (*ReadFunc)     (const gchar *filename, CantusHash *data);
  typedef gint     (*WriteFunc)    (const gchar *filename, CantusHash *data);
  typedef void    *(*UIPrefsFunc)  (void);
  typedef void    *(*UIWidgetFunc) (gboolean vertical);
  typedef void    *(*UIIconFunc)   (void);
  
  // EventBus functions, provided by Cantus.
  typedef void    *(*CantusPrefGetFunc)        (const gchar *); // FIXME.
  typedef glong    (*CantusAddListenerFunc)    (const gchar *, GClosure *);
  #ifdef _CPLUSPLUS_
  typedef glong    (*CantusAddListenerSigCFunc)(const gchar *,
                                                SigC::Slot1<void, void*>);
  #endif
  typedef glong    (*CantusRemoveListenerFunc) (glong id);
  typedef glong    (*CantusEmitFunc)           (const gchar *, const GValue *);
  
  // Preferences functions, provided by Cantus.
  typedef void         (*CantusPrefSet_intFunc)    (const gchar *key, gint value);
  typedef gint         (*CantusPrefGet_intFunc)    (const gchar *key);
  typedef void         (*CantusPrefSet_boolFunc)   (const gchar *key,
                                                    gboolean value);
  typedef gboolean     (*CantusPrefGet_boolFunc)   (const gchar *key);
  typedef void         (*CantusPrefSet_charFunc)   (const gchar *key,
                                                    const gchar *value);
  typedef const gchar* (*CantusPrefGet_charFunc)   (const gchar *key);
  
  // FileInfoManager functions, provided by Cantus.
  typedef CantusHash  *(*CantusFileInfoGetFunc)   (const gchar *filename);
  typedef void         (*CantusFileInfoUnlockFunc)(const gchar *filename);

End User Documentation

Documentation for end users will be provided using LaTeX for easy conversion into other document formats.

About this Document

Written by Samuel Abels. Please refer comments to the author.

Current Revision: 1.0



Sam 2004-04-05