[gnome-documents/wip/gepub] Rewrite implementation of GActions



commit f8222acc8442902602fe08b3c682c62ccd660da4
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Tue Jun 21 20:29:18 2016 -0700

    Rewrite implementation of GActions
    
    Instead of having global application actions everywhere, use an action
    group for the preview.
    This allows each view to have a much finer grained control over the
    actions, their activation and lifecycle, and removes awkward roundtrips
    to the application singleton to disconnect signals when a view is
    destroyed, since now actions live as long as their views.
    
    Finally, we can now use "hidden-when=action-missing" attributes in the
    menu definition to automatically get menu sections removed when a view
    does not support the actions.

 data/ui/preview-context-menu.ui |    2 +-
 data/ui/preview-menu.ui         |   24 ++--
 src/application.js              |  130 ++----------------
 src/edit.js                     |   23 +---
 src/embed.js                    |   34 +++--
 src/epubview.js                 |   20 ++-
 src/evinceview.js               |  277 +++++++++++++++++++++------------------
 src/lokview.js                  |  129 +++++++-----------
 src/mainWindow.js               |    2 +-
 src/preview.js                  |   68 +++++++---
 src/utils.js                    |   36 +++++
 11 files changed, 353 insertions(+), 392 deletions(-)
---
diff --git a/data/ui/preview-context-menu.ui b/data/ui/preview-context-menu.ui
index c4a7212..027f2c9 100644
--- a/data/ui/preview-context-menu.ui
+++ b/data/ui/preview-context-menu.ui
@@ -2,7 +2,7 @@
   <menu id="preview-context-menu">
     <section>
       <item>
-        <attribute name="action">app.copy</attribute>
+        <attribute name="action">view.copy</attribute>
         <attribute name="label" translatable="yes">_Copy</attribute>
         <attribute name="accel">&lt;Primary&gt;c</attribute>
       </item>
diff --git a/data/ui/preview-menu.ui b/data/ui/preview-menu.ui
index 0550027..b2e7190 100644
--- a/data/ui/preview-menu.ui
+++ b/data/ui/preview-menu.ui
@@ -2,20 +2,22 @@
   <menu id="preview-menu">
     <section id="open-section">
       <item>
-        <attribute name="action">app.open-current</attribute>
+        <attribute name="action">view.open-current</attribute>
         <attribute name="label" translatable="yes">Open</attribute>
       </item>
       <item>
-        <attribute name="action">app.edit-current</attribute>
+        <attribute name="action">view.edit-current</attribute>
         <attribute name="label" translatable="yes">Edit</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
       </item>
       <item>
-        <attribute name="action">app.print-current</attribute>
+        <attribute name="action">view.print-current</attribute>
         <attribute name="label" translatable="yes">Print…</attribute>
         <attribute name="accel">&lt;Primary&gt;p</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
       </item>
       <item>
-        <attribute name="action">app.present-current</attribute>
+        <attribute name="action">view.present-current</attribute>
         <attribute name='hidden-when'>action-missing</attribute>
         <attribute name="label" translatable="yes">Present</attribute>
         <attribute name="accel">F5</attribute>
@@ -23,31 +25,35 @@
     </section>
     <section>
       <item>
-        <attribute name="action">app.zoom-in</attribute>
+        <attribute name="action">view.zoom-in</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
         <attribute name="label" translatable="yes">Zoom In</attribute>
         <attribute name="accel">&lt;Primary&gt;plus</attribute>
       </item>
       <item>
-        <attribute name="action">app.zoom-out</attribute>
+        <attribute name="action">view.zoom-out</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
         <attribute name="label" translatable="yes">Zoom Out</attribute>
         <attribute name="accel">&lt;Primary&gt;minus</attribute>
       </item>
     </section>
     <section id='rotate-section'>
       <item>
-        <attribute name="action">app.rotate-left</attribute>
+        <attribute name="action">view.rotate-left</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
         <attribute name="label" translatable="yes">Rotate ↶</attribute>
         <attribute name="accel">&lt;Primary&gt;Left</attribute>
       </item>
       <item>
-        <attribute name="action">app.rotate-right</attribute>
+        <attribute name="action">view.rotate-right</attribute>
+        <attribute name='hidden-when'>action-missing</attribute>
         <attribute name="label" translatable="yes">Rotate ↷</attribute>
         <attribute name="accel">&lt;Primary&gt;Right</attribute>
       </item>
     </section>
     <section>
       <item>
-        <attribute name="action">app.properties</attribute>
+        <attribute name="action">view.properties</attribute>
         <attribute name="label" translatable="yes">Properties</attribute>
       </item>
     </section>
diff --git a/src/application.js b/src/application.js
index ba84ec3..58c2005 100644
--- a/src/application.js
+++ b/src/application.js
@@ -248,63 +248,6 @@ const Application = new Lang.Class({
             settings.set_value('sort-by', parameter);
     },
 
-    _onActionOpenCurrent: function() {
-        let doc = documentManager.getActiveItem();
-        if (doc)
-            doc.open(this._mainWindow.get_screen(), Gtk.get_current_event_time());
-    },
-
-    _onActionPrintCurrent: function() {
-        let doc = documentManager.getActiveItem();
-        if (doc)
-            doc.print(this._mainWindow);
-    },
-
-    _onActionToggle: function(action) {
-        let state = action.get_state();
-        action.change_state(GLib.Variant.new('b', !state.get_boolean()));
-    },
-
-    _onActionProperties: function() {
-        let doc = documentManager.getActiveItem();
-        if (!doc)
-            return;
-
-        let dialog = new Properties.PropertiesDialog(doc.id);
-        dialog.connect('response', Lang.bind(this,
-            function(widget, response) {
-                widget.destroy();
-            }));
-    },
-
-    _initActions: function() {
-        this._actionEntries.forEach(Lang.bind(this,
-            function(actionEntry) {
-                let state = actionEntry.state;
-                let parameterType = actionEntry.parameter_type ?
-                    GLib.VariantType.new(actionEntry.parameter_type) : null;
-                let action;
-
-                if (state)
-                    action = Gio.SimpleAction.new_stateful(actionEntry.name,
-                        parameterType, actionEntry.state);
-                else
-                    action = new Gio.SimpleAction({ name: actionEntry.name,
-                        parameter_type: parameterType });
-
-                if (actionEntry.create_hook)
-                    actionEntry.create_hook.apply(this, [action]);
-
-                if (actionEntry.callback)
-                    action.connect('activate', Lang.bind(this, actionEntry.callback));
-
-                if (actionEntry.accels)
-                    this.set_accels_for_action('app.' + actionEntry.name, actionEntry.accels);
-
-                this.add_action(action);
-            }));
-    },
-
     _connectActionsToMode: function() {
         this._actionEntries.forEach(Lang.bind(this,
             function(actionEntry) {
@@ -519,70 +462,49 @@ const Application = new Lang.Class({
 
         this._actionEntries = [
             { name: 'quit',
-              callback: this._onActionQuit,
+              callback: Lang.bind(this, this._onActionQuit),
               accels: ['<Primary>q'] },
             { name: 'about',
-              callback: this._onActionAbout },
+              callback: Lang.bind(this, this._onActionAbout) },
             { name: 'help',
-              callback: this._onActionHelp,
+              callback: Lang.bind(this, this._onActionHelp),
               accels: ['F1'] },
             { name: 'fullscreen',
-              callback: this._onActionFullscreen,
+              callback: Lang.bind(this, this._onActionFullscreen),
               state: GLib.Variant.new('b', false),
-              create_hook: this._fullscreenCreateHook,
+              create_hook: Lang.bind(this, this._fullscreenCreateHook),
               accels: ['F11'],
               window_mode: WindowMode.WindowMode.PREVIEW_EV },
             { name: 'night-mode',
-              callback: this._onActionNightMode,
-              create_hook: this._nightModeCreateHook,
+              callback: Lang.bind(this, this._onActionNightMode),
+              create_hook: Lang.bind(this, this._nightModeCreateHook),
               state: settings.get_value('night-mode') },
             { name: 'gear-menu',
-              callback: this._onActionToggle,
+              callback: Utils.actionToggleCallback,
               state: GLib.Variant.new('b', false),
               accels: ['F10'] },
             { name: 'view-as',
-              callback: this._onActionViewAs,
-              create_hook: this._viewAsCreateHook,
+              callback: Lang.bind(this, this._onActionViewAs),
+              create_hook: Lang.bind(this, this._viewAsCreateHook),
               parameter_type: 's',
               state: settings.get_value('view-as'),
               window_modes: [WindowMode.WindowMode.COLLECTIONS,
                              WindowMode.WindowMode.DOCUMENTS,
                              WindowMode.WindowMode.SEARCH] },
             { name: 'sort-by',
-              callback: this._onActionSortBy,
-              create_hook: this._sortByCreateHook,
+              callback: Lang.bind(this, this._onActionSortBy),
+              create_hook: Lang.bind(this, this._sortByCreateHook),
               parameter_type: 's',
               state: settings.get_value('sort-by'),
               window_modes: [WindowMode.WindowMode.COLLECTIONS,
                              WindowMode.WindowMode.DOCUMENTS,
                              WindowMode.WindowMode.SEARCH] },
-            { name: 'open-current',
-              callback: this._onActionOpenCurrent,
-              window_modes: [WindowMode.WindowMode.PREVIEW_EV,
-                             WindowMode.WindowMode.PREVIEW_LOK] },
-            { name: 'edit-current' },
             { name: 'view-current',
               window_mode: WindowMode.WindowMode.EDIT },
-            { name: 'print-current', accels: ['<Primary>p'],
-              callback: this._onActionPrintCurrent },
             { name: 'search',
-              callback: this._onActionToggle,
+              callback: Utils.actionToggleCallback,
               state: GLib.Variant.new('b', false),
               accels: ['<Primary>f'] },
-            { name: 'find-next', accels: ['<Primary>g'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
-            { name: 'find-prev', accels: ['<Shift><Primary>g'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
-            { name: 'zoom-in', accels: ['<Primary>plus', '<Primary>equal'],
-              window_modes: [WindowMode.WindowMode.PREVIEW_EV,
-                             WindowMode.WindowMode.PREVIEW_LOK] },
-            { name: 'zoom-out', accels: ['<Primary>minus'],
-              window_modes: [WindowMode.WindowMode.PREVIEW_EV,
-                             WindowMode.WindowMode.PREVIEW_LOK] },
-            { name: 'rotate-left', accels: ['<Primary>Left'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
-            { name: 'rotate-right', accels: ['<Primary>Right'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
             { name: 'select-all', accels: ['<Primary>a'],
               window_modes: [WindowMode.WindowMode.COLLECTIONS,
                              WindowMode.WindowMode.DOCUMENTS,
@@ -591,21 +513,6 @@ const Application = new Lang.Class({
               window_modes: [WindowMode.WindowMode.COLLECTIONS,
                              WindowMode.WindowMode.DOCUMENTS,
                              WindowMode.WindowMode.SEARCH] },
-            { name: 'properties',
-              callback: this._onActionProperties,
-              window_modes: [WindowMode.WindowMode.PREVIEW_EV,
-                             WindowMode.WindowMode.PREVIEW_LOK] },
-            { name: 'bookmark-page',
-              callback: this._onActionToggle,
-              state: GLib.Variant.new('b', false),
-              accels: ['<Primary>d'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
-            { name: 'places',
-              accels: ['<Primary>b'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
-            { name: 'copy',
-              accels: ['<Primary>c'],
-              window_mode: WindowMode.WindowMode.PREVIEW_EV },
             { name: 'search-source',
               parameter_type: 's',
               state: GLib.Variant.new('s', Search.SearchSourceStock.ALL),
@@ -626,17 +533,10 @@ const Application = new Lang.Class({
                              WindowMode.WindowMode.SEARCH] }
         ];
 
-        if (!this.isBooks) {
-            this._actionEntries.push (
-            { name: 'present-current',
-              window_mode: WindowMode.WindowMode.PREVIEW_EV,
-              callback: this._onActionToggle,
-              state: GLib.Variant.new('b', false),
-              accels: ['F5'] });
+        if (!this.isBooks)
             this._initGettingStarted();
-        }
 
-        this._initActions();
+        Utils.populateActionGroup(this, this._actionEntries, 'app');
     },
 
     _createWindow: function() {
diff --git a/src/edit.js b/src/edit.js
index 85d063e..187b1ea 100644
--- a/src/edit.js
+++ b/src/edit.js
@@ -59,17 +59,6 @@ const EditView = new Lang.Class({
 
         this.show_all();
 
-        this._editAction = Application.application.lookup_action('edit-current');
-        this._editAction.enabled = false;
-        this._editAction.connect('activate', Lang.bind(this,
-            function() {
-                let doc = Application.documentManager.getActiveItem();
-                if (!doc)
-                    return;
-                Application.modeController.setWindowMode(WindowMode.WindowMode.EDIT);
-                this.setUri (doc.uri);
-            }));
-
         this._viewAction = Application.application.lookup_action('view-current');
         this._viewAction.enabled = false;
         this._viewAction.connect('activate', Lang.bind(this,
@@ -77,9 +66,6 @@ const EditView = new Lang.Class({
                 Application.modeController.goBack();
             }));
 
-        this._printAction = Application.application.lookup_action('print-current');
-        this._printAction.set_enabled(false);
-
         Application.documentManager.connect('load-started',
                                             Lang.bind(this, this._onLoadStarted));
         Application.documentManager.connect('load-finished',
@@ -88,19 +74,12 @@ const EditView = new Lang.Class({
     },
 
     _onLoadStarted: function() {
-        this._editAction.enabled = false;
         this._viewAction.enabled = false;
-        this._printAction.set_enabled(false);
     },
 
     _onLoadFinished: function(manager, doc, docModel) {
-        if (doc.uri) {
-            if (doc.canEdit())
-                this._editAction.enabled = true;
+        if (doc.uri)
             this._viewAction.enabled = true;
-            if (doc.canPrint(docModel))
-                this._printAction.set_enabled(true);
-        }
     },
 
     _createView: function() {
diff --git a/src/embed.js b/src/embed.js
index acf40e1..2ab34fd 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -47,9 +47,10 @@ const Embed = new Lang.Class({
     Name: 'Embed',
     Extends: Gtk.Box,
 
-    _init: function() {
+    _init: function(mainWindow) {
         this._loadShowId = 0;
         this._searchState = null;
+        this._window = mainWindow;
 
         this.parent({ orientation: Gtk.Orientation.VERTICAL,
                       visible: true });
@@ -316,6 +317,7 @@ const Embed = new Lang.Class({
         let windowMode = Application.modeController.getWindowMode();
         let showSearch = (windowMode == WindowMode.WindowMode.PREVIEW_EV && !doc
                           || windowMode == WindowMode.WindowMode.SEARCH && !doc);
+
         if (showSearch)
             this._restoreSearch();
         else
@@ -375,6 +377,15 @@ const Embed = new Lang.Class({
             }));
     },
 
+    _clearPreview: function() {
+        if (this._preview) {
+            this._preview.destroy();
+            this._preview = null;
+        }
+
+        this._window.insert_action_group('view', null);
+    },
+
     _prepareForOverview: function(newMode, oldMode) {
         let createToolbar = (oldMode != WindowMode.WindowMode.COLLECTIONS &&
                              oldMode != WindowMode.WindowMode.DOCUMENTS &&
@@ -397,11 +408,7 @@ const Embed = new Lang.Class({
             break;
         }
 
-        if (this._preview) {
-            this._preview.destroy();
-            this._preview = null;
-        }
-
+        this._clearPreview();
         if (this._edit)
             this._edit.setUri(null);
 
@@ -419,16 +426,14 @@ const Embed = new Lang.Class({
     },
 
     _prepareForPreview: function(constructor) {
-        if (this._preview) {
-            this._preview.destroy();
-            this._preview = null;
-        }
+        this._clearPreview();
         if (this._edit)
             this._edit.setUri(null);
         if (this._toolbar)
             this._toolbar.destroy();
 
-        this._preview = new constructor(this._stackOverlay);
+        this._preview = new constructor(this._stackOverlay, this._window);
+        this._window.insert_action_group('view', this._preview.actionGroup);
         this._stack.add_named(this._preview, 'preview');
 
         // pack the toolbar
@@ -439,10 +444,7 @@ const Embed = new Lang.Class({
     },
 
     _prepareForEdit: function() {
-        if (this._preview) {
-            this._preview.destroy();
-            this._preview = null;
-        }
+        this._clearPreview();
         if (this._toolbar)
             this._toolbar.destroy();
 
@@ -450,6 +452,8 @@ const Embed = new Lang.Class({
         this._toolbar = new Edit.EditToolbar(this._preview);
         this._titlebar.add(this._toolbar);
 
+        let doc = Application.documentManager.getActiveItem();
+        this._edit.setUri(doc.uri);
         this._stack.set_visible_child_name('edit');
     },
 
diff --git a/src/epubview.js b/src/epubview.js
index 0417d41..4e19a39 100644
--- a/src/epubview.js
+++ b/src/epubview.js
@@ -44,6 +44,17 @@ const EPUBView = new Lang.Class({
     Name: 'EPUBView',
     Extends: Preview.Preview,
 
+    createActions: function() {
+        return [
+            { name: 'find-prev',
+              callback: Lang.bind(this, this.findPrev),
+              accels: ['<Shift><Primary>g'] },
+            { name: 'find-next',
+              callback: Lang.bind(this, this.findNext),
+              accels: ['<Primary>g'] },
+        ];
+    },
+
     createToolbar: function() {
         return new EPUBViewToolbar(this);
     },
@@ -119,10 +130,8 @@ const EPUBSearchbar = new Lang.Class({
     },
 
     _onSearchChanged: function(view, hasResults) {
-        let findPrev = Application.application.lookup_action('find-prev');
-        let findNext = Application.application.lookup_action('find-next');
-        findPrev.enabled = hasResults;
-        findNext.enabled = hasResults;
+        this.preview.getAction('find-prev').enabled = hasResults;
+        this.preview.getAction('find-next').enabled = hasResults;
     },
 
     conceal: function() {
@@ -149,9 +158,6 @@ const EPUBViewToolbar = new Lang.Class({
         this._searchAction = Application.application.lookup_action('search');
         this._searchAction.enabled = true;
 
-        this._gearMenu = Application.application.lookup_action('gear-menu');
-        this._gearMenu.enabled = true;
-
         // back button, on the left of the toolbar
         let backButton = this.addBackButton();
         backButton.connect('clicked', Lang.bind(this, function() {
diff --git a/src/evinceview.js b/src/evinceview.js
index c922e18..04c8ad6 100644
--- a/src/evinceview.js
+++ b/src/evinceview.js
@@ -46,7 +46,7 @@ const EvinceView = new Lang.Class({
     Name: 'EvinceView',
     Extends: Preview.Preview,
 
-    _init: function(overlay) {
+    _init: function(overlay, mainWindow) {
         this._model = null;
         this._jobFind = null;
         this._controlsFlipId = 0;
@@ -56,7 +56,7 @@ const EvinceView = new Lang.Class({
         this._viewSelectionChanged = false;
         this._fsToolbar = null;
 
-        this.parent(overlay);
+        this.parent(overlay, mainWindow);
 
         Application.modeController.connect('fullscreen-changed', Lang.bind(this,
             this._onFullscreenChanged));
@@ -68,71 +68,136 @@ const EvinceView = new Lang.Class({
         this._previewContextMenu = Gtk.Menu.new_from_model(model);
         this._previewContextMenu.attach_to_widget(this.view, null);
 
-        this._bookmarkPage = Application.application.lookup_action('bookmark-page');
-        this._bookmarkPage.enabled = false;
-        let bookmarkPageId = Application.application.connect('action-state-changed::bookmark-page',
-            Lang.bind(this, this._onActionStateChanged));
+        this.getAction('bookmark-page').enabled = false;
 
-        this._zoomIn = Application.application.lookup_action('zoom-in');
-        let zoomInId = this._zoomIn.connect('activate', Lang.bind(this,
-            function() {
-                if (!this._model)
-                    return;
-                this._model.set_sizing_mode(EvView.SizingMode.FREE);
-                this._evView.zoom_in();
-            }));
+        let nightModeId = Application.application.connect('action-state-changed::night-mode',
+            Lang.bind(this, this._updateNightMode));
 
-        this._zoomOut = Application.application.lookup_action('zoom-out');
-        let zoomOutId = this._zoomOut.connect('activate', Lang.bind(this,
+        this.connect('destroy', Lang.bind(this,
             function() {
-                if (!this._model)
-                    return;
-                this._model.set_sizing_mode(EvView.SizingMode.FREE);
-                this._evView.zoom_out();
+                Application.application.disconnect(nightModeId);
             }));
+    },
 
-        this._copy = Application.application.lookup_action('copy');
-        let copyId = this._copy.connect('activate', Lang.bind(this,
-            function() {
-                this._evView.copy();
-            }));
+    _copy: function() {
+        this._evView.copy();
+    },
 
-        let rotLeft = Application.application.lookup_action('rotate-left');
-        let rotLeftId = rotLeft.connect('activate', Lang.bind(this,
-            function() {
-                this._changeRotation(-90);
-            }));
-        let rotRight = Application.application.lookup_action('rotate-right');
-        let rotRightId = rotRight.connect('activate', Lang.bind(this,
-            function() {
-                this._changeRotation(90);
-            }));
-        this._places = Application.application.lookup_action('places');
-        let placesId = this._places.connect('activate', Lang.bind(this, this._showPlaces));
+    _zoomIn: function() {
+        if (!this._model)
+            return;
+        this._model.set_sizing_mode(EvView.SizingMode.FREE);
+        this._evView.zoom_in();
+    },
 
-        let nightModeId = Application.application.connect('action-state-changed::night-mode',
-            Lang.bind(this, this._updateNightMode));
+    _zoomOut: function() {
+        if (!this._model)
+            return;
+        this._model.set_sizing_mode(EvView.SizingMode.FREE);
+        this._evView.zoom_out();
+    },
 
-        this._togglePresentation = Application.application.lookup_action('present-current');
-        if (!Application.application.isBooks) {
-            var presentCurrentId = Application.application.connect('action-state-changed::present-current',
-                Lang.bind(this, this._onPresentStateChanged));
-        }
+    _rotateLeft: function() {
+        let rotation = this._model.get_rotation();
+        this._model.set_rotation(rotation - 90);
+    },
 
-        this.connect('destroy', Lang.bind(this,
-            function() {
-                this._zoomIn.disconnect(zoomInId);
-                this._zoomOut.disconnect(zoomOutId);
-                this._copy.disconnect(copyId);
-                rotLeft.disconnect(rotLeftId);
-                rotRight.disconnect(rotRightId);
-                this._places.disconnect(placesId);
-                if (!Application.application.isBooks)
-                    Application.application.disconnect(presentCurrentId);
-
-                Application.application.disconnect(bookmarkPageId);
-                Application.application.disconnect(nightModeId);
-            }));
+    _rotateRight: function() {
+        let rotation = this._model.get_rotation();
+        this._model.set_rotation(rotation + 90);
+    },
+
+    findPrev: function() {
+        this._evView.find_previous();
+    },
+
+    findNext: function() {
+        this._evView.find_next();
+    },
+
+    _places: function() {
+        let dialog = new Places.PlacesDialog(this._model, this._bookmarks);
+        dialog.connect('response', Lang.bind(this, function(widget, response) {
+            widget.destroy();
+        }));
+    },
+
+    _bookmarkStateChanged: function(action) {
+        let pageNumber = this._model.page;
+        let bookmark = new GdPrivate.Bookmark({ page_number: pageNumber });
+
+        if (action.state.get_boolean())
+            this._bookmarks.add(bookmark);
+        else
+            this._bookmarks.remove(bookmark);
+    },
+
+    _presentStateChanged: function(action) {
+        if (!this._model)
+            return;
+
+        if (action.state.get_boolean())
+            this._promptPresentation();
+        else
+            this._hidePresentation();
+    },
+
+    _edit: function() {
+        Application.modeController.setWindowMode(WindowMode.WindowMode.EDIT);
+    },
+
+    _print: function() {
+        let doc = Application.documentManager.getActiveItem();
+        if (doc)
+            doc.print(this.mainWindow);
+    },
+
+    createActions: function() {
+        let actions = [
+            { name: 'zoom-in',
+              callback: Lang.bind(this, this._zoomIn),
+              accels: ['<Primary>plus', '<Primary>equal'] },
+            { name: 'zoom-out',
+              callback: Lang.bind(this, this._zoomOut),
+              accels: ['<Primary>minus'] },
+            { name: 'copy',
+              callback: Lang.bind(this, this._copy),
+              accels: ['<Primary>c'] },
+            { name: 'rotate-left',
+              callback: Lang.bind(this, this._rotateLeft),
+              accels: ['<Primary>Left'] },
+            { name: 'rotate-right',
+              callback: Lang.bind(this, this._rotateRight),
+              accels: ['<Primary>Right'] },
+            { name: 'find-prev',
+              callback: Lang.bind(this, this.findPrev),
+              accels: ['<Shift><Primary>g'] },
+            { name: 'find-next',
+              callback: Lang.bind(this, this.findNext),
+              accels: ['<Primary>g'] },
+            { name: 'places',
+              callback: Lang.bind(this, this._places),
+              accels: ['<Primary>b'] },
+            { name: 'bookmark-page',
+              callback: Utils.actionToggleCallback,
+              state: GLib.Variant.new('b', false),
+              stateChanged: Lang.bind(this, this._bookmarkStateChanged),
+              accels: ['<Primary>d'] },
+            { name: 'edit-current',
+              callback: Lang.bind(this, this._edit) },
+            { name: 'print-current',
+              callback: Lang.bind(this, this._print),
+              accels: ['<Primary>p'] }
+        ];
+
+        if (!Application.application.isBooks)
+            actions.push({ name: 'present-current',
+                           callback: Utils.actionToggleCallback,
+                           state: GLib.Variant.new('b', false),
+                           stateChanged: Lang.bind(this, this._presentStateChanged),
+                           accels: ['F5'] });
+
+        return actions;
     },
 
     createNavControls: function() {
@@ -176,9 +241,9 @@ const EvinceView = new Lang.Class({
     onLoadStarted: function(manager, doc) {
         if (doc.viewType != Documents.ViewType.EV)
             return;
-        this._bookmarkPage.enabled = false;
-        this._places.enabled = false;
-        this._copy.enabled = false;
+
+        this.getAction('bookmark-page').enabled = false;
+        this.getAction('places').enabled = false;
     },
 
     onLoadFinished: function(manager, doc, docModel) {
@@ -187,6 +252,10 @@ const EvinceView = new Lang.Class({
         if (doc.viewType != Documents.ViewType.EV)
             return;
 
+        this.getAction('copy').enabled = false;
+        this.getAction('edit-current').enabled = doc.canEdit();
+        this.getAction('print-current').enabled = doc.canPrint(docModel);
+
         if (Application.application.isBooks)
             docModel.set_sizing_mode(EvView.SizingMode.FIT_PAGE);
         else
@@ -203,48 +272,17 @@ const EvinceView = new Lang.Class({
         this.parent(manager, doc, message, exception);
     },
 
-    _onActionStateChanged: function(source, actionName, state) {
-        if (!this._model)
-            return;
-
-        let page_number = this._model.page;
-        let bookmark = new GdPrivate.Bookmark({ page_number: page_number });
-
-        if (state.get_boolean())
-            this._bookmarks.add(bookmark);
-        else
-            this._bookmarks.remove(bookmark);
-    },
-
-    _onPresentStateChanged: function(source, actionName, state) {
-        if (!this._model)
-            return;
-
-        if (state.get_boolean())
-            this._promptPresentation();
-        else
-            this._hidePresentation();
-    },
-
     _onPageChanged: function() {
         this._pageChanged = true;
 
         if (!this._bookmarks)
             return;
 
-        let page_number = this._model.page;
-        let bookmark = new GdPrivate.Bookmark({ page_number: page_number });
+        let pageNumber = this._model.page;
+        let bookmark = new GdPrivate.Bookmark({ page_number: pageNumber });
         let hasBookmark = (this._bookmarks.find_bookmark(bookmark) != null);
 
-        this._bookmarkPage.change_state(GLib.Variant.new('b', hasBookmark));
-    },
-
-    _showPlaces: function() {
-        let dialog = new Places.PlacesDialog(this._model, this._bookmarks);
-        dialog.connect('response', Lang.bind(this,
-            function(widget, response) {
-                widget.destroy();
-            }));
+        this.getAction('bookmark-page').change_state(GLib.Variant.new('b', hasBookmark));
     },
 
     _hidePresentation: function() {
@@ -253,7 +291,7 @@ const EvinceView = new Lang.Class({
             this._presentation = null;
         }
 
-        Application.application.change_action_state('present-current', GLib.Variant.new('b', false));
+        this.getAction('present-current').change_state(GLib.Variant.new('b', false));
     },
 
     _showPresentation: function(output) {
@@ -283,7 +321,7 @@ const EvinceView = new Lang.Class({
 
     _onViewSelectionChanged: function() {
         let hasSelection = this._evView.get_has_selection();
-        this._copy.enabled = hasSelection;
+        this.getAction('copy').enabled = hasSelection;
 
         if (!hasSelection &&
             hasSelection == this._hasSelection) {
@@ -354,11 +392,11 @@ const EvinceView = new Lang.Class({
     },
 
     _onCanZoomInChanged: function() {
-        this._zoomIn.enabled = this._evView.can_zoom_in;
+        this.getAction('zoom-in').enabled = this._evView.can_zoom_in;
     },
 
     _onCanZoomOutChanged: function() {
-        this._zoomOut.enabled = this._evView.can_zoom_out;
+        this.getAction('zoom-out').enabled = this._evView.can_zoom_out;
     },
 
     _getEvinceViewContextMenu: function() {
@@ -471,11 +509,6 @@ const EvinceView = new Lang.Class({
         this._pageChanged = false;
     },
 
-    _changeRotation: function(offset) {
-        let rotation = this._model.get_rotation();
-        this._model.set_rotation(rotation + offset);
-    },
-
     get controlsVisible() {
         return this._controlsVisible;
     },
@@ -536,15 +569,16 @@ const EvinceView = new Lang.Class({
         this._toolbar.setModel(this._model);
 
         if (this._model) {
-            if (this._togglePresentation)
-                this._togglePresentation.enabled = true;
+            let presentCurrent = this.getAction('present-current');
+            if (presentCurrent)
+                presentCurrent.enabled = true;
 
             if (Application.documentManager.metadata)
                 this._bookmarks = new GdPrivate.Bookmarks({ metadata: Application.documentManager.metadata 
});
 
             let hasMultiplePages = (this._model.document.get_n_pages() > 1);
-            this._bookmarkPage.enabled = hasMultiplePages && this._bookmarks;
-            this._places.enabled = hasMultiplePages;
+            this.getAction('bookmark-page').enabled = hasMultiplePages && this._bookmarks;
+            this.getAction('places').enabled = hasMultiplePages;
 
             this._model.connect('page-changed', Lang.bind(this, this._onPageChanged));
             this._onPageChanged();
@@ -596,14 +630,6 @@ const EvinceView = new Lang.Class({
         return this._model ? this._model.document.get_n_pages() : 0;
     },
 
-    findPrev: function() {
-        this._evView.find_previous();
-    },
-
-    findNext: function() {
-        this._evView.find_next();
-    },
-
     scroll: function(direction) {
         this._evView.scroll(direction, false);
     },
@@ -635,7 +661,7 @@ const EvinceViewNavControls = new Lang.Class({
 
         let buttonArea = barWidget.get_button_area();
 
-        let button = new Gtk.Button({ action_name: 'app.places',
+        let button = new Gtk.Button({ action_name: 'view.places',
                                       image: new Gtk.Image({ icon_name: 'view-list-symbolic',
                                                              pixel_size: 16 }),
                                       valign: Gtk.Align.CENTER,
@@ -643,7 +669,7 @@ const EvinceViewNavControls = new Lang.Class({
                                     });
         buttonArea.pack_start(button, false, false, 0);
 
-        button = new Gtk.ToggleButton({ action_name: 'app.bookmark-page',
+        button = new Gtk.ToggleButton({ action_name: 'view.bookmark-page',
                                         image: new Gtk.Image({ icon_name: 'bookmark-new-symbolic',
                                                                pixel_size: 16 }),
                                         valign: Gtk.Align.CENTER,
@@ -683,8 +709,7 @@ const EvinceViewToolbar = new Lang.Class({
         this._searchAction = Application.application.lookup_action('search');
         this._searchAction.enabled = false;
 
-        this._gearMenu = Application.application.lookup_action('gear-menu');
-        this._gearMenu.enabled = false;
+        this._previewView.getAction('gear-menu').enabled = false;
 
         // back button, on the left of the toolbar
         let backButton = this.addBackButton();
@@ -699,7 +724,7 @@ const EvinceViewToolbar = new Lang.Class({
         let previewMenu = this._getEvinceViewMenu();
         let menuButton = new Gtk.MenuButton({ image: new Gtk.Image ({ icon_name: 'open-menu-symbolic' }),
                                               menu_model: previewMenu,
-                                              action_name: 'app.gear-menu' });
+                                              action_name: 'view.gear-menu' });
         this.toolbar.pack_end(menuButton);
 
         // search button, on the right of the toolbar
@@ -746,7 +771,7 @@ const EvinceViewToolbar = new Lang.Class({
         if (doc && doc.defaultAppName) {
             let section = builder.get_object('open-section');
             section.remove(0);
-            section.prepend(_("Open with %s").format(doc.defaultAppName), 'app.open-current');
+            section.prepend(_("Open with %s").format(doc.defaultAppName), 'view.open-current');
         }
 
         return menu;
@@ -767,7 +792,7 @@ const EvinceViewToolbar = new Lang.Class({
     },
 
     setModel: function() {
-        this._gearMenu.enabled = true;
+        this._previewView.getAction('gear-menu').enabled = true;
         this._enableSearch();
         this._setToolbarTitle();
     }
@@ -785,10 +810,8 @@ const EvinceViewSearchbar = new Lang.Class({
     },
 
     _onSearchChanged: function(view, hasResults) {
-        let findPrev = Application.application.lookup_action('find-prev');
-        let findNext = Application.application.lookup_action('find-next');
-        findPrev.enabled = hasResults;
-        findNext.enabled = hasResults;
+        this.preview.getAction('find-prev').enabled = hasResults;
+        this.preview.getAction('find-next').enabled = hasResults;
     },
 
     entryChanged: function() {
diff --git a/src/lokview.js b/src/lokview.js
index 580baeb..892922c 100644
--- a/src/lokview.js
+++ b/src/lokview.js
@@ -27,6 +27,7 @@ try {
 }
 
 const Gdk = imports.gi.Gdk;
+const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 const _ = imports.gettext.gettext;
 
@@ -37,6 +38,7 @@ const Application = imports.application;
 const MainToolbar = imports.mainToolbar;
 const Preview = imports.preview;
 const Documents = imports.documents;
+const Utils = imports.utils;
 
 const ZOOM_IN_FACTOR = 1.2;
 const ZOOM_OUT_FACTOR = (1.0/ZOOM_IN_FACTOR);
@@ -96,8 +98,8 @@ const LOKView = new Lang.Class({
     Name: 'LOKView',
     Extends: Preview.Preview,
 
-    _init: function(overlay) {
-        this.parent(overlay);
+    _init: function(overlay, mainWindow) {
+        this.parent(overlay, mainWindow);
 
         this._uri = null;
 
@@ -110,46 +112,20 @@ const LOKView = new Lang.Class({
         let model = this._getPreviewContextMenu();
         this._previewContextMenu = Gtk.Menu.new_from_model(model);
         this._previewContextMenu.attach_to_widget(this.view, null);
-
-        this._zoomIn = Application.application.lookup_action('zoom-in');
-        this._zoomInId = this._zoomIn.connect('activate', Lang.bind(this,
-            function() {
-                // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97301
-                if (!this._doc)
-                    return;
-                let zoomLevel = this._lokview.get_zoom() * ZOOM_IN_FACTOR;
-                this._lokview.set_zoom(zoomLevel);
-            }));
-
-        this._zoomOut = Application.application.lookup_action('zoom-out');
-        this._zoomOutId = this._zoomOut.connect('activate', Lang.bind(this,
-            function() {
-                // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97301
-                if (!this._doc)
-                    return;
-                let zoomLevel = this._lokview.get_zoom() * ZOOM_OUT_FACTOR;
-                this._lokview.set_zoom(zoomLevel);
-            }));
-
-        this._copy = Application.application.lookup_action('copy');
-        this._copyId = this._copy.connect('activate', Lang.bind(this, this._onCopyActivated));
     },
 
-    vfunc_destroy: function() {
-        if (this._zoomInId > 0) {
-            this._zoomIn.disconnect(this._zoomInId);
-            this._zoomInId = 0;
-        }
-        if (this._zoomOutId > 0) {
-            this._zoomOut.disconnect(this._zoomOutId);
-            this._zoomOutId = 0;
-        }
-        if (this._copyId > 0) {
-            this._copy.disconnect(this._copyId);
-            this._copyId = 0;
-        }
-
-        this.parent();
+    createActions: function() {
+        return [
+            { name: 'zoom-in',
+              callback: Lang.bind(this, this._zoomIn),
+              accels: ['<Primary>plus', '<Primary>equal'] },
+            { name: 'zoom-out',
+              callback: Lang.bind(this, this._zoomOut),
+              accels: ['<Primary>minus'] },
+            { name: 'copy',
+              callback: Lang.bind(this, this._copy),
+              accels: ['<Primary>c'] }
+        ];
     },
 
     createToolbar: function() {
@@ -176,22 +152,6 @@ const LOKView = new Lang.Class({
         return sw;
     },
 
-    _onCanZoomInChanged: function() {
-        this._zoomIn.enabled = this._lokview.can_zoom_in;
-    },
-
-    _onCanZoomOutChanged: function() {
-        this._zoomOut.enabled = this._lokview.can_zoom_out;
-    },
-
-    _onCopyActivated: function() {
-        let [selectedText, mimeType] = this._lokview.copy_selection('text/plain;charset=utf-8');
-        let display = Gdk.Display.get_default();
-        let clipboard = Gtk.Clipboard.get_default(display);
-
-        clipboard.set_text(selectedText, selectedText.length);
-    },
-
     onLoadFinished: function(manager, doc) {
         this.parent(manager, doc);
 
@@ -200,7 +160,6 @@ const LOKView = new Lang.Class({
         if (!isAvailable())
             return;
         this._doc = doc;
-        this._copy.enabled = false;
         this._lokview.open_document(doc.uri, "{}", null, Lang.bind(this, this.open_document_cb));
         this._progressBar.show();
     },
@@ -212,6 +171,38 @@ const LOKView = new Lang.Class({
         this._lokview.set_edit(false);
     },
 
+    _copy: function() {
+        let [selectedText, mimeType] = this._lokview.copy_selection('text/plain;charset=utf-8');
+        let display = Gdk.Display.get_default();
+        let clipboard = Gtk.Clipboard.get_default(display);
+
+        clipboard.set_text(selectedText, selectedText.length);
+    },
+
+    _zoomIn: function() {
+        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97301
+        if (!this._doc)
+            return;
+        let zoomLevel = this._lokview.get_zoom() * ZOOM_IN_FACTOR;
+        this._lokview.set_zoom(zoomLevel);
+    },
+
+    _zoomOut: function() {
+        // FIXME: https://bugs.documentfoundation.org/show_bug.cgi?id=97301
+        if (!this._doc)
+            return;
+        let zoomLevel = this._lokview.get_zoom() * ZOOM_OUT_FACTOR;
+        this._lokview.set_zoom(zoomLevel);
+    },
+
+    _onCanZoomInChanged: function() {
+        this.getAction('zoom-in').enabled = this._lokview.can_zoom_in;
+    },
+
+    _onCanZoomOutChanged: function() {
+        this.getAction('zoom-out').enabled = this._lokview.can_zoom_out;
+    },
+
     _getPreviewContextMenu: function() {
         let builder = new Gtk.Builder();
         builder.add_from_resource('/org/gnome/Documents/ui/preview-context-menu.ui');
@@ -235,7 +226,7 @@ const LOKView = new Lang.Class({
     },
 
     _onTextSelection: function(hasSelection) {
-        this._copy.enabled = hasSelection;
+        this.getAction('copy').enabled = hasSelection;
     },
 
     goPrev: function() {
@@ -283,12 +274,6 @@ const LOKViewToolbar = new Lang.Class({
         this.parent();
         this.toolbar.set_show_close_button(true);
 
-        this._gearMenu = Application.application.lookup_action('gear-menu');
-        this._gearMenu.enabled = true;
-
-        this._lokView._zoomIn.enabled = true;
-        this._lokView._zoomOut.enabled = true;
-
         // back button, on the left of the toolbar
         let backButton = this.addBackButton();
         backButton.connect('clicked', Lang.bind(this,
@@ -301,7 +286,7 @@ const LOKViewToolbar = new Lang.Class({
         let lokViewMenu = this._getLOKViewMenu();
         let menuButton = new Gtk.MenuButton({ image: new Gtk.Image ({ icon_name: 'open-menu-symbolic' }),
                                               menu_model: lokViewMenu,
-                                              action_name: 'app.gear-menu' });
+                                              action_name: 'view.gear-menu' });
         this.toolbar.pack_end(menuButton);
 
         this._setToolbarTitle();
@@ -317,21 +302,9 @@ const LOKViewToolbar = new Lang.Class({
         let doc = Application.documentManager.getActiveItem();
         if (doc && doc.defaultAppName) {
             section.remove(0);
-            section.prepend(_("Open with %s").format(doc.defaultAppName), 'app.open-current');
+            section.prepend(_("Open with %s").format(doc.defaultAppName), 'view.open-current');
         }
 
-        // No edit support yet
-        section.remove(1);
-        // No print support yet
-        section.remove(1);
-        // No present support yet
-        section.remove(1);
-
-        // No rotate support
-        section = builder.get_object('rotate-section');
-        section.remove(0);
-        section.remove(0);
-
         return menu;
     },
 
diff --git a/src/mainWindow.js b/src/mainWindow.js
index d1656ef..4974267 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -80,7 +80,7 @@ const MainWindow = new Lang.Class({
         this._fsId = Application.modeController.connect('fullscreen-changed',
             Lang.bind(this, this._onFullscreenChanged));
 
-        this._embed = new Embed.Embed();
+        this._embed = new Embed.Embed(this);
         this.add(this._embed);
     },
 
diff --git a/src/preview.js b/src/preview.js
index 5f66844..a380d58 100644
--- a/src/preview.js
+++ b/src/preview.js
@@ -1,5 +1,7 @@
 const GdPrivate = imports.gi.GdPrivate;
+const Gio = imports.gi.Gio;
 const Gdk = imports.gi.Gdk;
+const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 
 const Lang = imports.lang;
@@ -8,24 +10,25 @@ const Tweener = imports.tweener.tweener;
 
 const Application = imports.application;
 const ErrorBox = imports.errorBox;
+const Properties = imports.properties;
 const Searchbar = imports.searchbar;
+const Utils = imports.utils;
 
 const Preview = new Lang.Class({
     Name: 'Preview',
     Extends: Gtk.Stack,
 
-    _init: function(overlay) {
+    _init: function(overlay, mainWindow) {
         this._lastSearch = '';
         this.overlay = overlay;
+        this.mainWindow = mainWindow;
 
         this.parent({ homogeneous: true,
                       transition_type: Gtk.StackTransitionType.CROSSFADE });
 
-        this._findPrev = Application.application.lookup_action('find-prev');
-        this._findPrevId = this._findPrev.connect('activate', Lang.bind(this, this.findPrev));
-
-        this._findNext = Application.application.lookup_action('find-next');
-        this._findNextId = this._findNext.connect('activate', Lang.bind(this, this.findNext));
+        let actions = this.createActions().concat(this._getDefaultActions());
+        this.actionGroup = new Gio.SimpleActionGroup();
+        Utils.populateActionGroup(this.actionGroup, actions, 'view');
 
         this._errorBox = new ErrorBox.ErrorBox();
         this.add_named(this._errorBox, 'error');
@@ -47,15 +50,37 @@ const Preview = new Lang.Class({
                                                                 Lang.bind(this, this.onLoadError));
     },
 
+    _getDefaultActions: function() {
+        return [
+            { name: 'gear-menu',
+              callback: Utils.actionToggleCallback,
+              state: GLib.Variant.new('b', false),
+              accels: ['F10'] },
+            { name: 'properties',
+              callback: Lang.bind(this, this._properties) },
+            { name: 'open-current',
+              callback: Lang.bind(this, this._openCurrent) }
+        ];
+    },
+
+    _properties: function() {
+        let doc = Application.documentManager.getActiveItem();
+        if (!doc)
+            return;
+
+        let dialog = new Properties.PropertiesDialog(doc.id);
+        dialog.connect('response', Lang.bind(this, function(widget, response) {
+            widget.destroy();
+        }));
+    },
+
+    _openCurrent: function() {
+        let doc = Application.documentManager.getActiveItem();
+        if (doc)
+            doc.open(this.mainWindow.get_screen(), Gtk.get_current_event_time());
+    },
+
     vfunc_destroy: function() {
-        if (this._findPrevId > 0) {
-            this._findPrev.disconnect(this._findPrevId);
-            this._findPrevId = 0;
-        }
-        if (this._findNextId > 0) {
-            this._findNext.disconnect(this._findNextId);
-            this._findNextId = 0;
-        }
         if (this._loadStartedId > 0) {
             Application.documentManager.disconnect(this._loadStartedId);
             this._loadStartedId = 0;
@@ -76,6 +101,10 @@ const Preview = new Lang.Class({
         this.parent();
     },
 
+    createActions: function() {
+        return [];
+    },
+
     createNavControls: function() {
         return new PreviewNavControls(this, this.overlay);
     },
@@ -92,6 +121,7 @@ const Preview = new Lang.Class({
     },
 
     onLoadFinished: function(manager, doc, docModel) {
+        this.getAction('open-current').enabled = (doc.defaultApp != null);
     },
 
     onLoadError: function(manager, doc, message, exception) {
@@ -99,6 +129,10 @@ const Preview = new Lang.Class({
         this.set_visible_child_name('error');
     },
 
+    getAction: function(name) {
+        return this.actionGroup.lookup_action(name);
+    },
+
     goPrev: function() {
         throw (new Error('Not implemented'));
     },
@@ -360,17 +394,17 @@ const PreviewSearchbar = new Lang.Class({
 
         this.searchEntry = new Gtk.SearchEntry({ width_request: 500 });
         this.searchEntry.connect('activate', Lang.bind(this, function() {
-            Application.application.activate_action('find-next', null);
+            this.preview.activateResult();
         }));
         box.add(this.searchEntry);
 
-        this._prev = new Gtk.Button({ action_name: 'app.find-prev' });
+        this._prev = new Gtk.Button({ action_name: 'view.find-prev' });
         this._prev.set_image(new Gtk.Image({ icon_name: 'go-up-symbolic',
                                              icon_size: Gtk.IconSize.MENU }));
         this._prev.set_tooltip_text(_("Find Previous"));
         box.add(this._prev);
 
-        this._next = new Gtk.Button({ action_name: 'app.find-next' });
+        this._next = new Gtk.Button({ action_name: 'view.find-next' });
         this._next.set_image(new Gtk.Image({ icon_name: 'go-down-symbolic',
                                              icon_size: Gtk.IconSize.MENU }));
         this._next.set_tooltip_text(_("Find Next"));
diff --git a/src/utils.js b/src/utils.js
index 475137f..e781af9 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -120,3 +120,39 @@ function addJSSignalMethods(proto) {
     proto.emitJS = Signals._emit;
     proto.disconnectAllJS = Signals._disconnectAll;
 }
+
+function actionToggleCallback(action) {
+    let state = action.get_state();
+    action.change_state(GLib.Variant.new('b', !state.get_boolean()));
+}
+
+function populateActionGroup(actionGroup, actionEntries, prefix)
+{
+    actionEntries.forEach(function(actionEntry) {
+        let state = actionEntry.state;
+        let parameterType = actionEntry.parameter_type ?
+            GLib.VariantType.new(actionEntry.parameter_type) : null;
+        let action;
+
+        if (state)
+            action = Gio.SimpleAction.new_stateful(actionEntry.name,
+                                                   parameterType, actionEntry.state);
+        else
+            action = new Gio.SimpleAction({ name: actionEntry.name,
+                                            parameter_type: parameterType });
+
+        if (actionEntry.create_hook)
+            actionEntry.create_hook(action);
+
+        if (actionEntry.callback)
+            action.connect('activate', actionEntry.callback);
+
+        if (actionEntry.stateChanged)
+            action.connect('notify::state', actionEntry.stateChanged);
+
+        if (actionEntry.accels)
+            Application.application.set_accels_for_action(prefix + '.' + actionEntry.name, 
actionEntry.accels);
+
+        actionGroup.add_action(action);
+    });
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]