This is the old design for the ApplicationClass; it's kept here mostly because of the points raised

2010-02-22

  • the Application class should provide a "best practise" approach, not be a "one-size-fits-all" approach
  • the current best practise is the document-based model, where "document" is any object an application considers an atomic block of data
    • 1:1 mapping between documents and windows is the least common denominator
    • one (document) to many (windows) mapping is a hard requirement
    • many (documents) to one (window) mapping is a soft requirement
  • should hold a reference to every document object and to every window
  • should also be an abstract class: applications should subclass it in order to use it
  • should not be an interface, as a default base implementation is desiderable for simple applications
  • should provide hooks for the session management
  • should provide a subclass for single instance applications
  • should bind a desktop entry file, possibly using the EggLauncher object in libegg, to an application

  • should provide a way to automatically save the open documents if needed
  • should provide a way to automatically save the metadata bound to a document view

Bottom-Up

Document

  • the Document is an interface
  • async vfuncs to open and save a file
  • flags (readable, writable, printable, lockable, etc.)
  • metadata (application-specific, just hooks to set/get)

typedef void (* EggDocumentSaveCallback) (EggDocumentHandle *handle,
                                          const gchar       *uri,
                                          gboolean           overwrite,
                                          gboolean           save_backup,
                                          GError            *error,
                                          gpointer           data);
typedef void (* EggDocumentOpenCallback) (EggDocumentHandle *handle,
                                          const gchar       *uri,
                                          gboolean           read_only,
                                          GError            *error,
                                          gpointer           data);
typedef void (* EggDocumentSetInfoCallback) (EggDocumentHandle *handle,
                                             const gchar       *uri,
                                             EggDocumentInfo   *info,
                                             GError            *error,
                                             gpointer           data);
typedef void (* EggDocumentGetInfoCallback) (EggDocumentHandle *handle,
                                             const gchar       *uri,
                                             EggDocumentInfo   *info,
                                             GError            *error,
                                             gpointer           data);

struct _EggDocumentClassIface
{
  GTypeInterface g_iface;

  /* vtable */
  EggDocumentFlags  (* get_flags)     (EggDocument             *document);
  void              (* set_flags)     (EggDocument             *document,
                                       EggDocumentFlags         flags);

  EggDocumentHandle (* save_document) (EggDocument             *document,
                                       const gchar             *save_uri,
                                       gboolean                 overwrite,
                                       gboolean                 save_backup,
                                       EggDocumentSaveCallback  callback,
                                       gpointer                 data);
  EggDocumentHandle (* open_document) (EggDocument             *document,
                                       const gchar             *uri,
                                       gboolean                 read_only,
                                       EggDocumentOpenCallback  callback,
                                       gpointer                 data);
  
  EggDocumentHandle (* set_info) (EggDocument                *document,
                                  EggDocumentInfo            *info,
                                  EggDocumentSetInfoCallback  callback,
                                  gpointer                    data);
  EggDocumentHandle (* get_info) (EggDocument                *document,
                                  EggDocumentGetInfoCallback  callback,
                                  gpointer                    data);
  
  void (* cancel_operation) (EggDocumentHandle *handle);
  
  /* signals */
  void (* changed) (EggDocument  *document);
  void (* error)   (EggDocument  *document,
                    const GError *error);
};
  • GTK+ might provide some simple document implementation, like GtkTextDocument

  • the set_info and get_info functions should be used to save the metadata bound to a document
    • should EggDocumentInfo be an opaque structure with predefined data or a subclassable object?

Document Model

  • keeps a reference to every document type
  • every document has a path, in form of "element:element:element"
    • this allows doing "document:revision", or "document:section"
  • uses the GType to create documents

    • overridable or chainable
  • can set the properties to its children
    • overridable or chainable

struct _EggDocumentModel
{
  GObject parent_instance;

  /*< private >*/
  EggDocumentModelPrivate *priv;
};

struct _EggDocumentModel
{
  GObjectClass parent_class;

  /* vtable, not signals */
  EggDocument *(* create_document)       (EggDocumentModel *model,
                                          GType             document_type);
  void         (* set_document_property) (EggDocumentModel *model,
                                          EggDocument      *document,
                                          const gchar      *property_name,
                                          const GValue     *value);
  void         (* get_document_property) (EggDocumentModel *model,
                                          EggDocument      *document,
                                          const gchar      *property_name,
                                          GValue           *value);

  /* signals */
  void (* document_changed) (EggDocumentModel *model,
                             EggDocumentPath  *path,
                             EggDocument      *document);
  void (* document_add)     (EggDocumentModel *model,
                             EggDocumentPath  *path,
                             EggDocument      *document);
  void (* document_remove)  (EggDocumentModel *model,
                             EggDocumentPath  *path,
                             EggDocument      *document);

  /* padding for future expansion */
  void (*_egg_reserved1) (void)
  void (*_egg_reserved2) (void)
  void (*_egg_reserved3) (void)
  void (*_egg_reserved4) (void)
};
  • create a new document model:

  enum { DOCUMENT_TEXT_PLAIN, DOCUMENT_TEXT_HTML };
  EggDocumentModel *model;

  model = egg_document_model_new (2,
                                  DOCUMENT_TEXT_PLAIN, DOCUMENT_TYPE_TEXT,
                                  DOCUMENT_TEXT_HTML, DOCUMENT_TYPE_HTML);
  • create a new document:

  EggDocument *document;
  EggDocumentPath *path;
  
  document = egg_document_model_create_document (DOCUMENT_TEXT_PLAIN, "file", "foo.txt", NULL);
  path = egg_document_path_new_from_string ("myapp:foo-txt");
  egg_document_model_add_document (model, path, document);

Application

  • provides the default document model
    • can set/get the document model
  • hooks documents and windows together
  • replaces the gtk_init()/gtk_main()/gtk_main_quit() cycle
    • provides signals for handling the lifetime of the application
    • hooks into the session management

struct _EggApplicationClass
{
  GObjectClass parent_class;

  /* vfuncs, not signals */

  /* override if you wish to parse the command line arguments */
  gboolean   (* application_init) (EggApplication *application,
                                   GOptionContext *context,
                                   gint           *argc,
                                   gchar        ***argv);

  /* override to manager the widget for the document
  GtkWidget *(* create_document_view) (GtkApplication *application,
                                       GtkDocument    *document);

  /* used to control the widget type of the document view */
  GType document_view_type;

  /* signals */

  /* return FALSE to stop the emission of the event */
  gboolean   (* application_quit) (EggApplication *application);

  /* emitted when the document model changes */
  void (* document_model_set) (EggApplication   *application,
                               EggDocumentModel *old_model);
  
  void (* document_view_add)    (EggApplication *application,
                                 EggDocument    *document,
                                 GtkWidget      *widget);
  void (* document_view_remove) (EggApplication *application,
                                 EggDocument    *document,
                                 GtkWidget      *widget);
};
  • should we add a EggDocumentView interface?

struct _EggDocumentViewIface
{
  GTypeInterface g_iface;

  void    (* add_document)    (EggDocumentView *view,
                               EggDocument     *document);
  void    (* remove_document) (EggDocumentView *view,
                               EggDocument     *document);
  GSList *(* list_documents)  (EggDocumentView *view);
};

  • this would allow many-documents-to-single-view mapping

Comments

  • Support for LibUnique and/or DesktopAppsAsDBusServices

  • (RaphaelBosshard) Is there a chance to extend GtkApplication with a GtkMainMenuBar? (See http://bugzilla.gnome.org/show_bug.cgi?id=353076)

  • How can the DocumentSave and DocumentOpen callbacks indicate that an error happened during loading.

    • Will there by default implementation to show common error messages for common problems such as wrong access rights?
    • A future version should probably have API or default implementation to indicate inability to load newer versions of file formats, and to warn when re-saving a document in a later version of a file format.
    • (murrayc)
      • errors are relayed from inside the actual document implementation, by setting the GError structure and invoking the callback; the async design is similar to the GtkFileSystem async design kris did.

        • So who implements the error dialogs? GTK+ or the application author. If it's in GTK+ then it will be consistent.
        • (murrayc)
      • versioning of the file is at application level: how could GTK+ know that the version changed?
        • If the Document is a GInterface, then that can be a method of the interface, for the application author to implement.
        • (murrayc)
      • GTK+ could provide at most an error code for that, but not every application have a file format that can change (text editors?)
        • Almost every application will have this issue. The feature would not be a problem for applications that don't need it.
      • (ebassi)
  • About these document paths:
    • If its going to be used to indicate both revision numbers and document sub-sections, how do these two concepts look different in the syntax?
    • For document structure, why not just use XML, and let the application coder use libxml to navigate through the structure, maybe with some helper functions.
    • (murrayc)
      • the document path is a unique identifier for a document object inside the model: the semantics of the path elements are entirely up to the application to define; I though of using paths to offer a uniform access method instead of using the documents URI - that is why the path is an opaque structure.
      • what kind of structure? a document might also be unstructured data (text/plain, to continue the editor instance).
      • (ebassi)
      • This sounds rather ambitious and vague. There's plenty of work to do on making the basic open/save funtionality easy and consistent, without this.
      • (murrayc)

Projects/GTK/ApplicationClass/OldDesignDiscussion (last edited 2018-12-05 15:46:20 by EmmanueleBassi)