[gnome-documents] An an edit mode for google documents



commit b8b207a1d0451db36fb8fa385cdf363e5aea9b5f
Author: William Jon McCann <jmccann redhat com>
Date:   Sun Jan 20 09:41:11 2013 -0500

    An an edit mode for google documents
    
    https://bugzilla.gnome.org/show_bug.cgi?id=692102

 configure.ac                  |    4 +
 src/Makefile-js.am            |    1 +
 src/application.js            |    4 +
 src/documents.js              |   17 +++
 src/edit.js                   |  223 +++++++++++++++++++++++++++++++++++++++++
 src/embed.js                  |   42 ++++++++-
 src/mainWindow.js             |   15 +++-
 src/resources/preview-menu.ui |    4 +
 src/windowMode.js             |   14 ++-
 9 files changed, 317 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 70f6d75..3c53adf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -50,6 +50,7 @@ AC_CHECK_LIBM
 AC_SUBST(LIBM)
 
 EVINCE_MIN_VERSION=3.7.4
+WEBKITGTK_MIN_VERSION=1.10.0
 GLIB_MIN_VERSION=2.35.1
 GTK_MIN_VERSION=3.7.7
 GOBJECT_INTROSPECTION_MIN_VERSION=1.31.6
@@ -59,16 +60,19 @@ CLUTTER_GTK_MIN_VERSION=1.4.2
 CLUTTER_MIN_VERSION=1.10
 TRACKER_MIN_VERSION=0.13.1
 ZAPOJIT_MIN_VERSION=0.0.2
+SOUP_MIN_VERSION=2.41.3
 
 PKG_CHECK_MODULES(DOCUMENTS,
                   clutter-gtk-1.0 >= $CLUTTER_GTK_MIN_VERSION
                   clutter-1.0 >= $CLUTTER_MIN_VERSION
                   evince-document-3.0 >= $EVINCE_MIN_VERSION
                   evince-view-3.0 >= $EVINCE_MIN_VERSION
+                  webkitgtk-3.0 >= $WEBKITGTK_MIN_VERSION
                   gjs-1.0
                   glib-2.0 >= $GLIB_MIN_VERSION
                   gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
                   gtk+-3.0 >= $GTK_MIN_VERSION
+                  libsoup-2.4 >= $SOUP_MIN_VERSION
                   gnome-desktop-3.0
                   tracker-sparql-0.16 >= $TRACKER_MIN_VERSION
                   goa-1.0 >= $GOA_MIN_VERSION
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index d8eb073..3efdd44 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -3,6 +3,7 @@ dist_js_DATA = \
     application.js \
     changeMonitor.js \
     documents.js \
+    edit.js \
     embed.js \
     main.js \
     mainToolbar.js \
diff --git a/src/application.js b/src/application.js
index 7838a6e..4129ce2 100644
--- a/src/application.js
+++ b/src/application.js
@@ -372,6 +372,10 @@ const Application = new Lang.Class({
             { name: 'open-current',
               callback: this._onActionOpenCurrent,
               window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'edit-current',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'view-current',
+              window_mode: WindowMode.WindowMode.EDIT },
             { name: 'print-current', accel: '<Primary>p',
               callback: this._onActionPrintCurrent,
               window_mode: WindowMode.WindowMode.PREVIEW },
diff --git a/src/documents.js b/src/documents.js
index 14eaff4..bb6eee3 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -944,6 +944,23 @@ const DocumentManager = new Lang.Class({
         this._connectMetadata(docModel);
     },
 
+    reloadActiveItem: function() {
+        let doc = this.getActiveItem();
+
+        if (!doc)
+            return;
+
+        if (doc.collection)
+            return;
+
+        // cleanup any state we have for previously loaded model
+        this._clearActiveDocModel();
+
+        this._loaderCancellable = new Gio.Cancellable();
+        doc.load(this._loaderCancellable, Lang.bind(this, this._onDocumentLoaded));
+        this.emit('load-started', doc);
+    },
+
     setActiveItem: function(doc) {
         if (!this.parent(doc))
             return;
diff --git a/src/edit.js b/src/edit.js
new file mode 100644
index 0000000..1fad4a6
--- /dev/null
+++ b/src/edit.js
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+const Clutter = imports.gi.Clutter;
+const WebKit = imports.gi.WebKit;
+const Soup = imports.gi.Soup;
+const GdPrivate = imports.gi.GdPrivate;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const GtkClutter = imports.gi.GtkClutter;
+const _ = imports.gettext.gettext;
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
+const Application = imports.application;
+const Tweener = imports.util.tweener;
+const MainToolbar = imports.mainToolbar;
+const Searchbar = imports.searchbar;
+const Utils = imports.utils;
+const View = imports.view;
+const WindowMode = imports.windowMode;
+const Documents = imports.documents;
+
+const _BLANK_URI = "about:blank";
+
+const EditView = new Lang.Class({
+    Name: 'EditView',
+
+    _init: function(overlayLayout) {
+        this._uri = null;
+
+        this.widget = new Gtk.Overlay();
+
+        this._scrolledWindow = new Gtk.ScrolledWindow({ hexpand: true,
+                                                        vexpand: true,
+                                                        shadow_type: Gtk.ShadowType.IN });
+        this.widget.get_style_context().add_class('documents-scrolledwin');
+        this.widget.add(this._scrolledWindow);
+
+        this._session = WebKit.get_default_session ();
+        Soup.Session.prototype.add_feature.call(this._session, new Soup.ProxyResolverDefault());
+        Soup.Session.prototype.remove_feature.call(this._session, new Soup.CookieJar());
+        let jarfile = GLib.build_filenamev([GLib.get_user_cache_dir(), 'gnome-documents', 'cookies.sqlite'])
+        this._cookieJar = new Soup.CookieJarDB({ filename: jarfile, read_only: false });
+        Soup.Session.prototype.add_feature.call(this._session, this._cookieJar);
+
+        this._progressBar = new Gtk.ProgressBar({ halign: Gtk.Align.FILL,
+                                                  valign: Gtk.Align.START });
+        this._progressBar.get_style_context().add_class('osd');
+        this.widget.add_overlay(this._progressBar);
+
+        this._createView();
+
+        this.widget.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,
+            function() {
+                Application.modeController.setWindowMode(WindowMode.WindowMode.PREVIEW);
+            }));
+
+        Application.documentManager.connect('load-started',
+                                            Lang.bind(this, this._onLoadStarted));
+        Application.documentManager.connect('load-finished',
+                                            Lang.bind(this, this._onLoadFinished));
+
+    },
+
+    _onLoadStarted: function() {
+        this._editAction.enabled = false;
+        this._viewAction.enabled = false;
+    },
+
+    _onLoadFinished: function(manager, doc, docModel) {
+        if (doc.uri) {
+            if (doc instanceof Documents.GoogleDocument)
+                this._editAction.enabled = true;
+            this._viewAction.enabled = true;
+        }
+    },
+
+    _createView: function() {
+        this.view = new WebKit.WebView();
+        this._scrolledWindow.add(this.view);
+        this.view.show();
+        this.view.connect('notify::progress', Lang.bind(this, this._onProgressChanged));
+    },
+
+    _isLoading: function() {
+        let status = this.view.load_status;
+        if ((status == WebKit.LoadStatus.finished
+            || status == WebKit.LoadStatus.failed)
+            && status != WebKit.LoadStatus.provisional)
+            return false;
+
+        return status != WebKit.LoadStatus.finished
+            && status != WebKit.LoadStatus.failed;
+    },
+
+    _onProgressChanged: function() {
+        if (!this.view.uri || this.view.uri == _BLANK_URI)
+            return;
+
+        let progress = this.view.progress;
+        let loading = this._isLoading();
+
+        if (progress == 1.0 || !loading) {
+            if (!this._timeoutId)
+                this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, this._onTimeoutExpired));
+        } else {
+            if (this._timeoutId) {
+                Mainloop.source_remove(this._timeoutId);
+                this._timeoutId = 0;
+            }
+            this._progressBar.show();
+        }
+        let value = 0.0
+        if (loading || progress == 1.0)
+            value = progress;
+        this._progressBar.fraction = value;
+    },
+
+    _onTimeoutExpired: function() {
+        this._timeoutId = 0;
+        this._progressBar.hide();
+        return false;
+    },
+
+    setUri: function(uri) {
+        if (this._uri == uri)
+            return;
+
+        if (!uri)
+            uri = _BLANK_URI;
+
+        this._uri = uri;
+        this.view.load_uri (uri);
+    },
+
+    getUri: function() {
+        return this._uri;
+    },
+});
+Signals.addSignalMethods(EditView.prototype);
+
+const EditToolbar = new Lang.Class({
+    Name: 'EditToolbar',
+    Extends: MainToolbar.MainToolbar,
+
+    _init: function(editView) {
+        this._editView = editView;
+
+        this.parent();
+
+        // back button, on the left of the toolbar
+        let iconName =
+            (this.widget.get_direction() == Gtk.TextDirection.RTL) ?
+            'go-next-symbolic' : 'go-previous-symbolic';
+        let backButton =
+            this.widget.add_button(iconName, _("Back"), true);
+        backButton.connect('clicked', Lang.bind(this,
+            function() {
+                Application.documentManager.setActiveItem(null);
+            }));
+
+        let viewButton =
+            this.widget.add_button(null, _("View"), false);
+        viewButton.get_style_context().add_class('suggested-action');
+        viewButton.set_action_name('app.view-current');
+
+        this._setToolbarTitle();
+        this.widget.show_all();
+    },
+
+    createSearchbar: function() {
+    },
+
+    handleEvent: function(event) {
+        return false;
+    },
+
+    _setToolbarTitle: function() {
+        let primary = null;
+        let doc = Application.documentManager.getActiveItem();
+
+        if (doc)
+            primary = doc.name;
+
+        this.widget.set_labels(primary, null);
+    }
+});
diff --git a/src/embed.js b/src/embed.js
index b5a371d..489693c 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -27,9 +27,11 @@ const Application = imports.application;
 const MainToolbar = imports.mainToolbar;
 const Notifications = imports.notifications;
 const Preview = imports.preview;
+const Edit = imports.edit;
 const Selections = imports.selections;
 const View = imports.view;
 const WindowMode = imports.windowMode;
+const Documents = imports.documents;
 
 const Clutter = imports.gi.Clutter;
 const EvView = imports.gi.EvinceView;
@@ -377,6 +379,9 @@ const Embed = new Lang.Class({
         this._preview = new Preview.PreviewView(this._overlayLayout);
         this._previewPage = this._notebook.append_page(this._preview.widget, null);
 
+        this._edit = new Edit.EditView(this._overlayLayout);
+        this._editPage = this._notebook.append_page(this._edit.widget, null);
+
         Application.modeController.connect('window-mode-changed',
                                            Lang.bind(this, this._onWindowModeChanged));
 
@@ -458,10 +463,24 @@ const Embed = new Lang.Class({
     },
 
     _onWindowModeChanged: function(object, newMode, oldMode) {
-        if (newMode == WindowMode.WindowMode.OVERVIEW)
+        switch (newMode) {
+        case WindowMode.WindowMode.OVERVIEW:
             this._prepareForOverview();
-        else
+            break;
+        case WindowMode.WindowMode.PREVIEW:
+            if (oldMode == WindowMode.WindowMode.EDIT)
+                Application.documentManager.reloadActiveItem();
             this._prepareForPreview();
+            break;
+        case WindowMode.WindowMode.EDIT:
+            this._prepareForEdit();
+            break;
+        case WindowMode.WindowMode.NONE:
+            break;
+         default:
+            throw(new Error('Not handled'))
+            break;
+        }
     },
 
     _onActiveItemChanged: function(manager, doc) {
@@ -500,6 +519,8 @@ const Embed = new Lang.Class({
     _prepareForOverview: function() {
         if (this._preview)
             this._preview.setModel(null);
+        if (this._edit)
+            this._edit.setUri(null);
 
         if (this._toolbar)
             this._toolbar.actor.destroy();
@@ -517,6 +538,8 @@ const Embed = new Lang.Class({
     },
 
     _prepareForPreview: function() {
+        if (this._edit)
+            this._edit.setUri(null);
         if (this._toolbar)
             this._toolbar.actor.destroy();
 
@@ -529,6 +552,21 @@ const Embed = new Lang.Class({
         this._notebook.set_current_page(this._previewPage);
     },
 
+    _prepareForEdit: function() {
+        if (this._preview)
+            this._preview.setModel(null);
+        if (this._toolbar)
+            this._toolbar.actor.destroy();
+
+        // pack the toolbar
+        this._toolbar = new Edit.EditToolbar(this._preview);
+        this._contentsLayout.pack_start = true;
+        this._contentsActor.add_actor(this._toolbar.actor);
+        this._contentsLayout.set_fill(this._toolbar.actor, true, false);
+
+        this._notebook.set_current_page(this._editPage);
+    },
+
     _setError: function(primary, secondary) {
         this._errorBox.update(primary, secondary);
         this._errorBox.moveIn();
diff --git a/src/mainWindow.js b/src/mainWindow.js
index e3ff560..c6a510a 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -149,10 +149,21 @@ const MainWindow = new Lang.Class({
         if (toolbar.handleEvent(event))
             return true;
 
-        if (Application.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW)
+        switch (Application.modeController.getWindowMode()) {
+        case WindowMode.WindowMode.NONE:
+            return false;
+        case WindowMode.WindowMode.PREVIEW:
             return this._handleKeyPreview(event);
-        else
+        case WindowMode.WindowMode.OVERVIEW:
             return this._handleKeyOverview(event);
+        case WindowMode.WindowMode.EDIT:
+            return false;
+        default:
+            throw(new Error('Not handled'))
+            break;
+        }
+
+        return false;
     },
 
     _handleKeyPreview: function(event) {
diff --git a/src/resources/preview-menu.ui b/src/resources/preview-menu.ui
index d9bf848..27590f5 100644
--- a/src/resources/preview-menu.ui
+++ b/src/resources/preview-menu.ui
@@ -6,6 +6,10 @@
         <attribute name="label" translatable="yes">Open</attribute>
       </item>
       <item>
+        <attribute name="action">app.edit-current</attribute>
+        <attribute name="label" translatable="yes">Edit</attribute>
+      </item>
+      <item>
         <attribute name="action">app.print-current</attribute>
         <attribute name="label" translatable="yes">Printâ</attribute>
         <attribute name="accel">&lt;Primary&gt;p</attribute>
diff --git a/src/windowMode.js b/src/windowMode.js
index c60eec0..4a09235 100644
--- a/src/windowMode.js
+++ b/src/windowMode.js
@@ -29,7 +29,8 @@ const Application = imports.application;
 const WindowMode = {
     NONE: 0,
     OVERVIEW: 1,
-    PREVIEW: 2
+    PREVIEW: 2,
+    EDIT: 3
 };
 
 const ModeController = new Lang.Class({
@@ -47,7 +48,13 @@ const ModeController = new Lang.Class({
         if (oldMode == mode)
             return;
 
-        this.setCanFullscreen(mode == WindowMode.PREVIEW);
+        if (mode == WindowMode.PREVIEW
+            || mode == WindowMode.EDIT) {
+            this.setCanFullscreen(true);
+        } else {
+            this.setCanFullscreen(false);
+        }
+
         this._mode = mode;
 
         this.emit('window-mode-changed', this._mode, oldMode);
@@ -80,7 +87,8 @@ const ModeController = new Lang.Class({
     },
 
     setFullscreen: function(fullscreen) {
-        if (this._mode != WindowMode.PREVIEW)
+        if (this._mode != WindowMode.PREVIEW
+            && this._mode != WindowMode.EDIT)
             return;
 
         if (this._fullscreen == fullscreen)



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