← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor into lp:ubuntu-docviewer-app

 

Stefano Verzegnassi has proposed merging lp:~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor into lp:ubuntu-docviewer-app.

Commit message:
Introducing a brand-new TextEditor, from DocumentViewer.Text module

Requested reviews:
  Ubuntu Document Viewer Developers (ubuntu-docviewer-dev)
Related bugs:
  Bug #1398773 in Ubuntu Document Viewer App: "Use a C++ class to read content from plain text files"
  https://bugs.launchpad.net/ubuntu-docviewer-app/+bug/1398773
  Bug #1450466 in Ubuntu Document Viewer App: "[textView] Add line numbers for TextEdit component"
  https://bugs.launchpad.net/ubuntu-docviewer-app/+bug/1450466
  Bug #1450467 in Ubuntu Document Viewer App: "[textView] Add textWrap option"
  https://bugs.launchpad.net/ubuntu-docviewer-app/+bug/1450467

For more details, see:
https://code.launchpad.net/~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor/+merge/257943

- Added a new QML module that provides a new TextArea component, which will be extended in future with a syntax highlighter.

- Bumped app version to 0.4, since this update introduces a new feature.

### Details

Text files are now read by a C++ class, that works asynchronously.

Default header in TextView has been updated, and now provides a "search" action (not enabled yet), a "selection" mode and a settings page.

We provide a separated "selection" mode for the header, because of a number of issues in the UITK. See "Notes" section in this description of the change.

Text settings - the new TextEditor component supports the following settings: show line numbers, wrap text, highlight current line and show right margin at column xx.

Line numbers in TextEditor are provided by a C++ class. I preferred this implementation for keeping the code clean and fast.

DocumentHandler class is BSD licensed, and it's a fork of a demo project from Digia. BSD license is compatible with GPL v3, so there should be no issue with it.

### Notes

Here's some thoughts about the current UITK.

I had to partially fork the upstream TextArea component, because it does not provide a way to access to the "textDocument" property of the internal TextEdit (known issue).

Another issue I found is that it wasn't possible to implement the text selection used in UITK.TextArea: the components are declared as internals and/or depends on some features which are not exported via the Ubuntu.Components module.

I'd like to see the internal components of Ubuntu.Components module exposed as Ubuntu.Components.Private and/or having a blank TextEdit that uses all the goodies from UITK.

About the "text selection" mode:
There are two issues here.
A first implementation of the selection mode used the standard PageHead.actions. It seems that the actions popover in the header forwards mouse events to the component below: if an user clicks on  an action (e.g. "copy), that click also affects the TextEditor below the popover, deselecting the text before it's effectively copied.

The second issue is a known one, and it's about the alignment of PageHead.contents. Actions in TextViewSelectionHeader are not aligned because of some issue in getting the parent of the layout item.
-- 
Your team Ubuntu Document Viewer Developers is requested to review the proposed merge of lp:~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor into lp:ubuntu-docviewer-app.
=== modified file 'debian/changelog'
--- debian/changelog	2015-02-23 12:49:52 +0000
+++ debian/changelog	2015-04-30 17:36:20 +0000
@@ -1,3 +1,9 @@
+ubuntu-docviewer-app (0.4.0) utopic; urgency=medium
+
+  * Improved text editor
+
+ -- Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>  Thu, 30 Apr 2015 19:12:24 +0200
+
 ubuntu-docviewer-app (0.3.0) utopic; urgency=medium
 
   * Improved content-hub support

=== modified file 'debian/control'
--- debian/control	2015-02-23 12:49:52 +0000
+++ debian/control	2015-04-30 17:36:20 +0000
@@ -28,6 +28,7 @@
 Architecture: any
 Depends: qtdeclarative5-qtquick2-plugin,
          qtdeclarative5-ubuntu-ui-toolkit-plugin,
+         qml-module-qt-labs-settings,
          ${misc:Depends}
 Description: Document Viewer application
  Core Document Viewer application

=== modified file 'manifest.json.in'
--- manifest.json.in	2015-02-13 15:30:01 +0000
+++ manifest.json.in	2015-04-30 17:36:20 +0000
@@ -13,7 +13,7 @@
             "urls": "@URLS_FILE@"
         }
     },
-    "version": "0.3.@BZR_REVNO@",
+    "version": "0.4.@BZR_REVNO@",
     "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@xxxxxxxxxxxxxxxxxxx>",
     "x-source": {
         "vcs-bzr": "@BZR_SOURCE@",

=== modified file 'po/com.ubuntu.docviewer.pot'
--- po/com.ubuntu.docviewer.pot	2015-04-27 16:02:40 +0000
+++ po/com.ubuntu.docviewer.pot	2015-04-30 17:36:20 +0000
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-04-27 18:02+0200\n"
+"POT-Creation-Date: 2015-04-30 19:09+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -34,13 +34,13 @@
 
 #: ../src/app/docviewer-application.cpp:164
 #: ../src/app/qml/documentPage/DocumentPage.qml:25
-#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
+#: /home/stefano/Progetti/doc-viewer/build-advanced-text-editor-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
 msgid "Document Viewer"
 msgstr ""
 
 #: ../src/app/qml/common/DetailsPage.qml:27
 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:97
-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:83
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:104
 msgid "Details"
 msgstr ""
 
@@ -77,7 +77,7 @@
 #: ../src/app/qml/common/RejectedImportDialog.qml:38
 #: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:32
 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
 msgid "Close"
 msgstr ""
 
@@ -274,7 +274,7 @@
 msgstr ""
 
 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
 msgid "Back"
 msgstr ""
 
@@ -283,12 +283,12 @@
 msgstr ""
 
 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
 msgid "Disable night mode"
 msgstr ""
 
 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
 msgid "Enable night mode"
 msgstr ""
 
@@ -305,20 +305,88 @@
 msgid "GO!"
 msgstr ""
 
-#: ../src/app/qml/textView/TextView.qml:42
-msgid "Loading..."
-msgstr ""
-
-#: ../src/app/qml/ubuntu-docviewer-app.qml:183
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:86
+msgid "Enable selection mode"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:98
+msgid "Settings"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:36
+msgid "Select all"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:42
+msgid "Paste"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:48
+msgid "Cut"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:54
+msgid "Copy"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:60
+msgid "Redo"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:67
+msgid "Undo"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:75
+msgid "Exit selection mode"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:24
+msgid "Text settings"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:45
+msgid "Show line numbers"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:61
+msgid "Enable text wrapping"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:77
+msgid "Highlight current line"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:88
+msgid "Right margin settings"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:95
+msgid "Show right margin"
+msgstr ""
+
+#: ../src/app/qml/textView/TextViewSettingsPage.qml:111
+msgid "Show at column (default = 80):"
+msgstr ""
+
+#: ../src/app/qml/ubuntu-docviewer-app.qml:185
 msgid "Document successfully imported!"
 msgid_plural "Documents successfully imported!"
 msgstr[0] ""
 msgstr[1] ""
 
-#: ../src/app/qml/ubuntu-docviewer-app.qml:186
+#: ../src/app/qml/ubuntu-docviewer-app.qml:188
 msgid "Open"
 msgstr ""
 
-#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
+#: ../src/plugin/text-qml-plugin/filereader.cpp:90
+msgid "Could not open file for reading."
+msgstr ""
+
+#: ../src/plugin/text-qml-plugin/filereader.cpp:107
+msgid "No file specified."
+msgstr ""
+
+#: /home/stefano/Progetti/doc-viewer/build-advanced-text-editor-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
 msgid "documents;viewer;pdf;reader;"
 msgstr ""

=== modified file 'src/app/qml/textView/TextView.qml'
--- src/app/qml/textView/TextView.qml	2015-03-26 13:58:31 +0000
+++ src/app/qml/textView/TextView.qml	2015-04-30 17:36:20 +0000
@@ -16,7 +16,8 @@
 
 import QtQuick 2.3
 import Ubuntu.Components 1.1
-import Ubuntu.Components.Themes.Ambiance 0.1
+import DocumentViewer.Text 1.0
+import Qt.labs.settings 1.0
 
 import "../common/utils.js" as Utils
 
@@ -24,50 +25,48 @@
     id: textPage
     title: Utils.getNameOfFile(file.path);
 
+    property bool selectionModeEnabled: false
+    property alias editor: editor
+
     // Reset night mode shader settings when closing the page
     // Component.onDestruction: mainView.nightModeEnabled = false
 
-    TextArea {
-        id: textAreaMain
-        objectName: "textAreaMain"
-
-        property bool isLoading: true
-
+    Settings {
+        property alias displayLineNumbers: editor.displayLineNumbers
+        property alias textWrap: editor.textWrap
+        property alias rightMarginAtColumn: editor.rightMarginAtColumn
+        property alias showRightMargin: editor.showRightMargin
+        property alias highlightCurrentLine: editor.highlightCurrentLine
+    }
+
+    TextReader {
+        id: reader
+        path: file.path
+
+        onErrorStringChanged: console.log(errorString)
+    }
+
+    TextEditor {
+        id: editor
         anchors.fill: parent
 
-        // FIXME: If set to true, some of the keyboard hooks are disabled
-        // And it's not possible to move the cursor with arrow keys.
-        readOnly: true
-
-        text: i18n.tr("Loading...")
-        font.family: "UbuntuMono"
-
-        Component.onCompleted: {
-            var xhr = new XMLHttpRequest;
-
-            xhr.open("GET", file.path);
-            xhr.onreadystatechange = function() {
-                if (xhr.readyState === XMLHttpRequest.DONE) {
-                    textAreaMain.text = xhr.responseText;
-                    textAreaMain.isLoading = false
-                }
-            };
-
-            xhr.send();
-        }
-
-        style: TextAreaStyle {
-            background: Rectangle { color: "white" }
-        }
+        selectionMode: textPage.selectionModeEnabled
+        text: reader.fileString
     }
 
     // *** HEADER ***
-    state: "default"
     states: [
         TextViewDefaultHeader {
-            name: "default"
-            targetPage: textPage
-            activityRunning: textAreaMain.isLoading
+            when: !textPage.selectionModeEnabled
+
+            targetPage: textPage
+            activityRunning: reader.status == TextReader.Loading
+        },
+
+        TextViewSelectionHeader {
+            when: textPage.selectionModeEnabled
+
+            targetPage: textPage
         }
     ]
 }

=== modified file 'src/app/qml/textView/TextViewDefaultHeader.qml'
--- src/app/qml/textView/TextViewDefaultHeader.qml	2015-03-26 13:58:31 +0000
+++ src/app/qml/textView/TextViewDefaultHeader.qml	2015-04-30 17:36:20 +0000
@@ -31,6 +31,8 @@
         anchors.fill: parent
         spacing: units.gu(1)
 
+        visible: !targetPage.selectionModeEnabled
+
         ActivityIndicator { id: activity }
 
         Column {
@@ -73,12 +75,31 @@
 
     actions: [
         Action {
+            iconName: "search"
+            // onTriggered: target.state = "search"
+            //Disable it until we provide search in Text plugin.
+            enabled: false
+        },
+
+        Action {
+            iconName: "edit-select-all"
+            text: i18n.tr("Enable selection mode")
+            onTriggered: targetPage.selectionModeEnabled = true
+        },
+
+        Action {
             iconName: "night-mode"
             text: mainView.nightModeEnabled ? i18n.tr("Disable night mode") : i18n.tr("Enable night mode")
             onTriggered: mainView.nightModeEnabled = !mainView.nightModeEnabled
         },
 
         Action {
+            iconName: "settings"
+            text: i18n.tr("Settings")
+            onTriggered: pageStack.push(Qt.resolvedUrl("TextViewSettingsPage.qml"), { textPage: targetPage })
+        },
+
+        Action {
             objectName: "detailsAction"
             text: i18n.tr("Details")
             iconName: "info"

=== added file 'src/app/qml/textView/TextViewSelectionHeader.qml'
--- src/app/qml/textView/TextViewSelectionHeader.qml	1970-01-01 00:00:00 +0000
+++ src/app/qml/textView/TextViewSelectionHeader.qml	2015-04-30 17:36:20 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014-2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import "../upstreamComponents"
+
+PageHeadState {
+    id: rootItem
+
+    property Page targetPage
+    head: targetPage.head
+
+    contents: Row {
+        width: parent ? parent.width : 0
+        height: parent ? parent.height : 0
+
+        layoutDirection: Qt.RightToLeft
+        visible: targetPage.selectionModeEnabled
+
+        HeaderButton {
+            iconName: "edit-select-all"
+            text: i18n.tr("Select all")
+            onTriggered: targetPage.editor.selectAll()
+        }
+
+        HeaderButton {
+            iconName: "edit-paste"
+            text: i18n.tr("Paste")
+            onTriggered: targetPage.editor.paste()
+        }
+
+        HeaderButton {
+            iconName: "edit-cut"
+            text: i18n.tr("Cut")
+            onTriggered: targetPage.editor.cut()
+        }
+
+        HeaderButton {
+            iconName: "edit-copy"
+            text: i18n.tr("Copy")
+            onTriggered: targetPage.editor.copy()
+        }
+
+        HeaderButton {
+            iconName: "edit-redo"
+            text: i18n.tr("Redo")
+            onTriggered: targetPage.editor.redo()
+            enabled: targetPage.editor.canRedo
+        }
+
+        HeaderButton {
+            iconName: "edit-undo"
+            text: i18n.tr("Undo")
+            onTriggered: targetPage.editor.undo()
+            enabled: targetPage.editor.canUndo
+        }
+    }
+
+    backAction: Action {
+        iconName: "back"
+        text: i18n.tr("Exit selection mode")
+        onTriggered: {
+            targetPage.editor.deselect()
+            targetPage.selectionModeEnabled = false;
+        }
+    }
+}

=== added file 'src/app/qml/textView/TextViewSettingsPage.qml'
--- src/app/qml/textView/TextViewSettingsPage.qml	1970-01-01 00:00:00 +0000
+++ src/app/qml/textView/TextViewSettingsPage.qml	2015-04-30 17:36:20 +0000
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import QtQuick.Layouts 1.1
+import Ubuntu.Components.ListItems 1.0 as ListItem
+
+Page {
+    id: textSettingsPage
+    title: i18n.tr("Text settings")
+
+    property var textPage
+
+    Component.onCompleted: {
+        // Get settings
+        lineNumbersCheckBox.checked = textPage.editor.displayLineNumbers
+        textWrapCheckbox.checked = textPage.editor.textWrap
+        highlightCurrentLineCheckbox.checked = textPage.editor.highlightCurrentLine
+        rightMarginCheckbox.checked = textPage.editor.showRightMargin
+        rightMarginTextField.text = textPage.editor.rightMarginAtColumn
+    }
+
+    Column {
+        anchors.fill: parent
+
+        ListItem.Base {
+            RowLayout {
+                anchors.fill: parent
+
+                Label {
+                    text: i18n.tr("Show line numbers")
+                    Layout.fillWidth: true
+                }
+
+                CheckBox {
+                    id: lineNumbersCheckBox
+                    onCheckedChanged: textPage.editor.displayLineNumbers = checked;
+                }
+            }
+        }
+
+        ListItem.Base {
+            RowLayout {
+                anchors.fill: parent
+
+                Label {
+                    text: i18n.tr("Enable text wrapping")
+                    Layout.fillWidth: true
+                }
+
+                CheckBox {
+                    id: textWrapCheckbox
+                    onCheckedChanged: textPage.editor.textWrap = checked
+                }
+            }
+        }
+
+        ListItem.Base {
+            RowLayout {
+                anchors.fill: parent
+
+                Label {
+                    text: i18n.tr("Highlight current line")
+                    Layout.fillWidth: true
+                }
+
+                CheckBox {
+                    id: highlightCurrentLineCheckbox
+                    onCheckedChanged: textPage.editor.highlightCurrentLine = checked
+                }
+            }
+        }
+
+        ListItem.Header { text: i18n.tr("Right margin settings") }
+
+        ListItem.Base {
+            RowLayout {
+                anchors.fill: parent
+
+                Label {
+                    text: i18n.tr("Show right margin")
+                    Layout.fillWidth: true
+                }
+
+                CheckBox {
+                    id: rightMarginCheckbox
+                    onCheckedChanged: textPage.editor.showRightMargin = checked
+                }
+            }
+        }
+
+        ListItem.Base {
+            RowLayout {
+                anchors.fill: parent
+
+                Label {
+                    text: i18n.tr("Show at column (default = 80):")
+
+                    Layout.fillWidth: true
+                }
+
+                TextField {
+                    id: rightMarginTextField
+                    implicitWidth: units.gu(12)
+                    inputMethodHints: Qt.ImhDigitsOnly
+                    validator: IntValidator { bottom: 1; top: 150 }
+
+                    onTextChanged: textPage.editor.rightMarginAtColumn = parseInt(text)
+                }
+            }
+        }
+    }
+}

=== modified file 'src/app/qml/ubuntu-docviewer-app.qml'
--- src/app/qml/ubuntu-docviewer-app.qml	2015-04-14 14:16:16 +0000
+++ src/app/qml/ubuntu-docviewer-app.qml	2015-04-30 17:36:20 +0000
@@ -34,7 +34,9 @@
 
     applicationName: "com.ubuntu.docviewer"
     useDeprecatedToolbar: false   
+
     automaticOrientation: true
+    anchorToKeyboard: true
 
     width: units.gu(50)
     height: units.gu(75)

=== modified file 'src/plugin/CMakeLists.txt'
--- src/plugin/CMakeLists.txt	2014-10-20 21:38:36 +0000
+++ src/plugin/CMakeLists.txt	2015-04-30 17:36:20 +0000
@@ -1,2 +1,3 @@
 add_subdirectory(file-qml-plugin)
 add_subdirectory(poppler-qml-plugin)
+add_subdirectory(text-qml-plugin)

=== added directory 'src/plugin/text-qml-plugin'
=== added file 'src/plugin/text-qml-plugin/CMakeLists.txt'
--- src/plugin/text-qml-plugin/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/CMakeLists.txt	2015-04-30 17:36:20 +0000
@@ -0,0 +1,38 @@
+set(PLUGIN_DIR DocumentViewer/Text)
+include_directories(
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+#add QML resources
+file(GLOB_RECURSE QML_SRCS
+     RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+     *.qml *.js qmldir
+)
+
+#add the sources to compile
+set(textqmlplugin_SRCS
+    backend.cpp
+    documenthandler.cpp
+    filereader.cpp
+    ${QML_SRCS}
+)
+
+add_library(textqmlplugin MODULE ${textqmlplugin_SRCS})
+qt5_use_modules(textqmlplugin Qml Quick)
+
+# Copy the plugin, the qmldir file and other assets to the build dir for running in QtCreator
+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
+    add_custom_command(TARGET textqmlplugin POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/LineNumberColumn.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditor.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditorStyle.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:textqmlplugin> ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
+    )
+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
+
+# Install plugin file
+install(TARGETS textqmlplugin DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})
+install(FILES ${QML_SRCS} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})

=== added file 'src/plugin/text-qml-plugin/LineNumberColumn.qml'
--- src/plugin/text-qml-plugin/LineNumberColumn.qml	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/LineNumberColumn.qml	2015-04-30 17:36:20 +0000
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+Rectangle {
+    id: lineColumn
+
+    width: visible ? units.gu(3.5) + lineNumberRepeater.itemAt(lineNumberRepeater.count - 1).paintedWidth : 0
+
+    property int topMargin
+    property alias model: lineNumberRepeater.model
+    property var font
+
+    property alias dividerColor: divider.color
+    property alias dividerWidth: divider.width
+    property color fontColor
+
+    Rectangle {
+        id: divider
+        height: parent.height
+        anchors.right: parent.right
+        color: lineColumn.dividerColor
+    }
+
+    Column {
+        id: layout
+        anchors {
+            top: parent.top
+            left: parent.left
+            right: parent.right
+            topMargin: lineColumn.topMargin
+            rightMargin: units.gu(2)
+            leftMargin: units.gu(1.5)
+        }
+
+        Repeater {
+            id: lineNumberRepeater
+
+            delegate: Text {
+                width: layout.width
+                height: modelData.lineHeight
+                text: modelData.lineNumber + 1
+
+                horizontalAlignment: Text.AlignRight
+
+                font: lineColumn.font
+                color: lineColumn.fontColor
+            }
+        }
+    }
+}

=== added file 'src/plugin/text-qml-plugin/TextEditor.qml'
--- src/plugin/text-qml-plugin/TextEditor.qml	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/TextEditor.qml	2015-04-30 17:36:20 +0000
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import DocumentViewer.Text 1.0
+
+StyledItem {
+    id: rootItem
+
+    property alias  text:                   textArea.text
+    property alias  readOnly:               textArea.readOnly
+
+    property alias  mouseSelectionMode:     textArea.mouseSelectionMode
+    property alias  selectionMode:          textArea.selectByMouse
+
+    property bool   displayLineNumbers:     true
+    property bool   textWrap:               false
+    property int    rightMarginAtColumn:    80
+    property bool   showRightMargin:        true
+    property bool   highlightCurrentLine:   false
+
+    property alias  canPaste:               textArea.canPaste
+    property alias  canRedo:                textArea.canRedo
+    property alias  canUndo:                textArea.canUndo
+
+    // functions
+    /*!
+        Copies the currently selected text to the system clipboard.
+    */
+    function copy()
+    {
+        textArea.copy();
+    }
+
+    /*!
+        Moves the currently selected text to the system clipboard.
+    */
+    function cut()
+    {
+        textArea.cut();
+    }
+
+    /*!
+        Removes active text selection.
+    */
+    function deselect()
+    {
+        textArea.deselect();
+    }
+
+    /*!
+        Inserts text into the TextArea at position.
+    */
+    function insert(position, text)
+    {
+        textArea.insert(position, text);
+    }
+
+    /*!
+        Returns the text position closest to pixel position (x, y).
+
+        Position 0 is before the first character, position 1 is after the first
+        character but before the second, and so on until position text.length,
+        which is after all characters.
+    */
+    function positionAt(x, y)
+    {
+        return textArea.positionAt(x, y);
+    }
+
+    /*!
+        Returns true if the natural reading direction of the textArea text found
+        between positions start and end is right to left.
+    */
+    function isRightToLeft(start, end)
+    {
+        return textArea.isRightToLeft(start, end)
+    }
+
+    /*!
+        Moves the cursor to position and updates the selection according to the
+        optional mode parameter. (To only move the cursor, set the cursorPosition
+        property.)
+
+        When this method is called it additionally sets either the selectionStart
+        or the selectionEnd (whichever was at the previous cursor position) to the
+        specified position. This allows you to easily extend and contract the selected
+        text range.
+
+        The selection mode specifies whether the selection is updated on a per character
+        or a per word basis. If not specified the selection mode will default to whatever
+        is given in the mouseSelectionMode property.
+    */
+    function moveCursorSelection(position, mode)
+    {
+        if (mode === undefined)
+            textArea.moveCursorSelection(position, mouseSelectionMode);
+        else
+            textArea.moveCursorSelection(position, mode);
+    }
+
+    /*!
+        Places the clipboard or the data given as parameter into the text input.
+        The selected text will be replaces with the data.
+    */
+    function paste(data) {
+        if ((data !== undefined) && (typeof data === "string") && !textArea.readOnly) {
+            var selTxt = textArea.selectedText;
+            var txt = textArea.text;
+            var pos = (selTxt !== "") ? txt.indexOf(selTxt) : textArea.cursorPosition
+            if (selTxt !== "") {
+                textArea.text = txt.substring(0, pos) + data + txt.substr(pos + selTxt.length);
+            } else {
+                textArea.text = txt.substring(0, pos) + data + txt.substr(pos);
+            }
+            textArea.cursorPosition = pos + data.length;
+        } else
+            textArea.paste();
+    }
+
+    /*!
+        Returns the rectangle at the given position in the text. The x, y, and height
+        properties correspond to the cursor that would describe that position.
+    */
+    function positionToRectangle(position)
+    {
+        return textArea.positionToRectangle(position);
+    }
+
+    /*!
+        Redoes the last operation if redo is \l{canRedo}{available}.
+    */
+    function redo()
+    {
+        textArea.redo();
+    }
+
+    /*!
+        Causes the text from start to end to be selected.
+
+        If either start or end is out of range, the selection is not changed.
+
+        After calling this, selectionStart will become the lesser and selectionEnd
+        will become the greater (regardless of the order passed to this method).
+
+        See also selectionStart and selectionEnd.
+    */
+    function select(start, end)
+    {
+        textArea.select(start, end);
+    }
+
+    /*!
+        Causes all text to be selected.
+    */
+    function selectAll()
+    {
+        textArea.selectAll();
+    }
+
+    /*!
+        Causes the word closest to the current cursor position to be selected.
+    */
+    function selectWord()
+    {
+        textArea.selectWord();
+    }
+
+    /*!
+        Returns the section of text that is between the start and end positions.
+
+        The returned text does not include any rich text formatting. A getText(0, length)
+        will result in the same value as displayText.
+    */
+    function getText(start, end)
+    {
+        return textArea.getText(start, end);
+    }
+
+    /*!
+        Removes the section of text that is between the start and end positions
+        from the TextEditor.
+    */
+    function remove(start, end)
+    {
+        return textArea.remove(start, end);
+    }
+
+    /*!
+        Undoes the last operation if undo is \l{canUndo}{available}. Deselects
+        any current selection, and updates the selection start to the current
+        cursor position.
+    */
+    function undo()
+    {
+        textArea.undo();
+    }
+
+    clip: true
+    style: Qt.createComponent("TextEditorStyle.qml", rootItem)
+
+    LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
+    LayoutMirroring.childrenInherit: true
+
+    Rectangle {
+        color: rootItem.__styleInstance.backgroundColor
+        anchors.fill: parent
+
+        Rectangle {
+            visible: rootItem.showRightMargin && (rootItem.rightMarginAtColumn > 0)
+            color: rootItem.__styleInstance.secondaryBackgroundColor
+
+            anchors {
+                fill: parent
+                leftMargin: dummyFont.paintedWidth * rootItem.rightMarginAtColumn + lineColumn.width + flicker.margin - flicker.contentX
+            }
+
+            Rectangle {
+                height: parent.height
+                anchors.left: parent.left
+                width: rootItem.__styleInstance.dividerWidth
+                color: rootItem.__styleInstance.dividerColor
+            }
+        }
+
+        Rectangle {
+            width: parent.width
+            height: textArea.cursorRectangle.height
+
+            y: textArea.cursorRectangle.y + flicker.margin - flicker.contentY
+            color: rootItem.__styleInstance.currentLineHighlightColor
+            opacity: 0.3
+
+            visible: rootItem.highlightCurrentLine && textArea.cursorVisible
+        }
+    }
+
+    Flickable {
+        id: flicker
+        anchors.fill: parent
+
+        property int margin: rootItem.__styleInstance.frameSpacing
+
+        contentWidth: textArea.paintedWidth + lineColumn.width
+        contentHeight: textArea.paintedHeight + flicker.margin * 2
+
+        boundsBehavior: Flickable.StopAtBounds
+
+        LineNumberColumn {
+            id: lineColumn
+
+            color: rootItem.__styleInstance.secondaryBackgroundColor
+            dividerColor: rootItem.__styleInstance.dividerColor
+            fontColor: rootItem.__styleInstance.selectedTextColor
+            dividerWidth: rootItem.__styleInstance.dividerWidth
+
+            visible: rootItem.displayLineNumbers
+            model: documentHandler.lineBlocks
+            font: textArea.font
+
+            height: Math.max(flicker.contentHeight, flicker.height)
+            topMargin: flicker.margin
+        }
+
+        TextEdit {
+            id: textArea
+
+            focus: true
+            width: rootItem.width - lineColumn.width
+            height: Math.max(textArea.contentHeight, textArea.contentHeight)
+            anchors {
+                left: lineColumn.right
+                top: parent.top
+                margins: flicker.margin
+            }
+
+            mouseSelectionMode: TextEdit.SelectWords
+            selectByMouse: false
+            activeFocusOnPress: true
+
+            color: rootItem.__styleInstance.color
+            selectedTextColor: rootItem.__styleInstance.selectedTextColor
+            selectionColor: rootItem.__styleInstance.selectionColor
+
+            // Work as a code editor
+            textFormat: TextEdit.PlainText
+            font.family: "UbuntuMono"
+            font.pixelSize: FontUtils.sizeToPixels("medium")
+
+            wrapMode: rootItem.textWrap ? TextEdit.Wrap
+                                        : TextEdit.NoWrap
+        }
+    }
+
+    Label {
+        id: dummyFont
+        text: "a"
+        font: textArea.font
+        visible: false
+    }
+
+    DocumentHandler {
+        id: documentHandler
+        textEditItem: textArea
+    }
+
+    Scrollbar {
+        id: rightScrollbar
+        flickableItem: flicker
+    }
+
+    Scrollbar {
+        id: bottomScrollbar
+        flickableItem: flicker
+        align: Qt.AlignBottom
+    }
+}
+

=== added file 'src/plugin/text-qml-plugin/TextEditorStyle.qml'
--- src/plugin/text-qml-plugin/TextEditorStyle.qml	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/TextEditorStyle.qml	2015-04-30 17:36:20 +0000
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+Item {
+    id: visuals
+
+    property color backgroundColor: "white"
+    property color secondaryBackgroundColor: "#f2f2f2"
+    property color dividerColor: "#ddd"
+    property color currentLineHighlightColor: UbuntuColors.blue
+
+    property color color: styledItem.activeFocus ? Theme.palette.selected.fieldText : Theme.palette.normal.fieldText
+    property color selectedTextColor: Theme.palette.selected.foregroundText
+    property color selectionColor: Theme.palette.selected.foreground
+
+    property real frameSpacing: units.gu(1)
+    property real dividerWidth: units.gu(0.1)
+}
+

=== added file 'src/plugin/text-qml-plugin/backend.cpp'
--- src/plugin/text-qml-plugin/backend.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/backend.cpp	2015-04-30 17:36:20 +0000
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <QtQml>
+#include <QtQml/QQmlContext>
+
+#include "backend.h"
+#include "documenthandler.h"
+#include "filereader.h"
+
+void BackendPlugin::registerTypes(const char *uri)
+{
+    Q_ASSERT(uri == QLatin1String("DocumentViewer.Text"));
+    
+    //@uri DocumentViewer.Text
+    qmlRegisterType<FileReader>(uri, 1, 0, "TextReader");
+    qmlRegisterType<DocumentHandler>(uri, 1, 0, "DocumentHandler");
+}
+
+void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
+{
+    QQmlExtensionPlugin::initializeEngine(engine, uri);
+}

=== added file 'src/plugin/text-qml-plugin/backend.h'
--- src/plugin/text-qml-plugin/backend.h	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/backend.h	2015-04-30 17:36:20 +0000
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKEND_PLUGIN_H
+#define BACKEND_PLUGIN_H
+
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlExtensionPlugin>
+
+class BackendPlugin : public QQmlExtensionPlugin
+{
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+    
+public:
+    void registerTypes(const char *uri);
+    void initializeEngine(QQmlEngine *engine, const char *uri);
+};
+#endif // BACKEND_PLUGIN_H
+

=== added file 'src/plugin/text-qml-plugin/documenthandler.cpp'
--- src/plugin/text-qml-plugin/documenthandler.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/documenthandler.cpp	2015-04-30 17:36:20 +0000
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "documenthandler.h"
+
+#include <QtGui/QTextDocument>
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextLayout>
+
+DocumentHandler::DocumentHandler() :
+    m_textEditItem(nullptr),
+    m_doc(nullptr)
+{
+    //
+}
+
+void DocumentHandler::setTextEditItem(QQuickItem *textEditItem)
+{
+    if (textEditItem == m_textEditItem)
+        return;
+
+    QString className(textEditItem->metaObject()->className());
+    if (className != "QQuickTextEdit") {
+        qWarning() << "Item passed as argument is not a QQuickTextEdit. Aborting...";
+        return;
+    }
+
+    m_doc = nullptr;
+    m_textEditItem = textEditItem;
+
+    QVariant doc = m_textEditItem->property("textDocument");
+    if (doc.canConvert<QQuickTextDocument*>()) {
+        QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument*>();
+
+        if (qqdoc) {
+            m_doc = qqdoc->textDocument();
+
+            // TODO: We can also use lengthChanged() from TextEdit?
+            connect(m_doc, SIGNAL(contentsChanged()), this, SLOT(updateLineBlocks()));
+            connect(m_textEditItem, SIGNAL(contentSizeChanged()), this, SLOT(updateLineBlocks()));
+            connect(m_textEditItem, SIGNAL(wrapModeChanged()), this, SLOT(updateLineBlocks()));
+        }
+    }
+
+    Q_EMIT textEditItemChanged();
+}
+
+void DocumentHandler::updateLineBlocks()
+{
+    m_lineBlocks.clear();
+
+    QTextBlock block = m_doc->firstBlock();
+    int blockNumber = block.blockNumber();
+    qreal blockHeight;
+
+    while (blockNumber < m_doc->blockCount()) {
+        blockHeight = block.layout()->boundingRect().height();
+
+        QVariantMap map;
+        map["lineNumber"] = blockNumber;
+        map["lineHeight"] = blockHeight;
+
+        m_lineBlocks.append(map);
+
+        block = block.next();
+        blockNumber++;
+    }
+
+    Q_EMIT lineBlocksChanged();
+}
+

=== added file 'src/plugin/text-qml-plugin/documenthandler.h'
--- src/plugin/text-qml-plugin/documenthandler.h	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/documenthandler.h	2015-04-30 17:36:20 +0000
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DOCUMENTHANDLER_H
+#define DOCUMENTHANDLER_H
+
+#include <QQuickTextDocument>
+
+#include <QtGui/QTextCharFormat>
+#include <QtCore/QTextCodec>
+
+#include <qqmlfile.h>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
+class DocumentHandler : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QQuickItem *textEditItem READ textEditItem WRITE setTextEditItem NOTIFY textEditItemChanged)
+    Q_PROPERTY(QVariantList lineBlocks READ lineBlocks NOTIFY lineBlocksChanged)
+
+public:
+    DocumentHandler();
+
+    QQuickItem *textEditItem() { return m_textEditItem; }
+    void setTextEditItem(QQuickItem *textEditItem);
+
+    QVariantList lineBlocks() { return m_lineBlocks; }
+
+Q_SIGNALS:
+    void textEditItemChanged();
+    void lineBlocksChanged();
+    void highlighterChanged();
+
+private slots:
+    void updateLineBlocks();
+
+private:
+    QQuickItem *m_textEditItem;
+    QTextDocument *m_doc;
+
+    QVariantList m_lineBlocks;
+};
+
+#endif

=== added file 'src/plugin/text-qml-plugin/filereader.cpp'
--- src/plugin/text-qml-plugin/filereader.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/filereader.cpp	2015-04-30 17:36:20 +0000
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013, 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QFile>
+#include <QObject>
+#include <QDebug>
+#include <QTextStream>
+
+#include "filereader.h"
+
+FileReader::FileReader(QObject *parent) :
+    QObject(parent),
+    m_path(""),
+    m_status(Status::Null)
+{
+    qRegisterMetaType<Status>("Status");
+}
+
+FileReader::~FileReader() {
+
+}
+
+void FileReader::setPath(QString path) {
+
+    if (path == m_path && path.isEmpty())
+        return;
+
+    m_path = path;
+    Q_EMIT pathChanged();
+
+    ReaderThread *p = new ReaderThread();
+    p->setFilePath(path);
+
+    connect(p, SIGNAL(fileRead(QString)), this, SLOT(setFileString(QString)));
+    connect(p, SIGNAL(errorChanged(QString)), this, SLOT(setErrorString(QString)));
+    connect(p, SIGNAL(statusChanged(Status)), this, SLOT(setStatus(Status)));
+    connect(p, SIGNAL(finished()), p, SLOT(deleteLater()));
+
+    p->start();
+}
+
+void FileReader::setFileString(QString string)
+{
+    if (m_fileString != string) {
+        m_fileString = string;
+        Q_EMIT fileStringChanged();
+    }
+}
+
+void FileReader::setStatus(Status status)
+{
+    if (m_status != status) {
+        m_status = status;
+        Q_EMIT statusChanged();
+    }
+}
+
+void FileReader::setErrorString(QString errorString)
+{
+    if (m_errorString != errorString) {
+        m_errorString = errorString;
+        Q_EMIT errorStringChanged();
+    }
+}
+
+void ReaderThread::run() {
+    if (!m_path.isEmpty()) {
+        Q_EMIT statusChanged(Status::Loading);
+
+        QFile file(m_path);
+
+        if (!file.open(QFile::ReadOnly | QFile::Text))
+        {
+            qWarning() << "Could not open file for reading.";
+
+            Q_EMIT statusChanged(Status::Error);
+            Q_EMIT errorChanged(tr("Could not open file for reading."));
+            Q_EMIT fileRead(QString(""));
+        }
+
+        QTextStream in(&file);
+        QString text = in.readAll();
+
+        file.flush();
+        file.close();
+
+        Q_EMIT statusChanged(Status::Ready);
+        Q_EMIT errorChanged(QString(""));
+        Q_EMIT fileRead(text);
+    } else {
+        qWarning() << "No file specified.";
+
+        Q_EMIT statusChanged(Status::Error);
+        Q_EMIT errorChanged(tr("No file specified."));
+        Q_EMIT fileRead(QString(""));
+    }
+}

=== added file 'src/plugin/text-qml-plugin/filereader.h'
--- src/plugin/text-qml-plugin/filereader.h	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/filereader.h	2015-04-30 17:36:20 +0000
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013, 2015 Canonical, Ltd.
+ *
+ * This program 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; version 3.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FILEREADER_H
+#define FILEREADER_H
+
+#include <QObject>
+#include <QThread>
+
+class FileReader: public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged)
+    Q_PROPERTY(QString fileString READ getfileString NOTIFY fileStringChanged)
+    Q_PROPERTY(Status status READ getStatus NOTIFY statusChanged)
+    Q_PROPERTY(QString errorString READ getErrorString NOTIFY errorStringChanged)
+    Q_ENUMS(Status)
+
+public:
+    explicit FileReader(QObject *parent = 0);
+    ~FileReader();
+
+    enum Status
+    {
+        Null,
+        Ready,
+        Loading,
+        Error
+    };
+
+    QString getPath() { return m_path; }
+    void setPath(QString path);
+
+    QString getErrorString() { return m_errorString; }
+
+    QString getfileString() { return m_fileString; }
+
+    Status getStatus() { return m_status; }
+
+Q_SIGNALS:
+    void pathChanged();
+    void fileStringChanged();
+    void statusChanged();
+    void errorStringChanged();
+
+private slots:
+    void setFileString(QString string);
+    void setStatus(Status status);
+    void setErrorString(QString errorString);
+
+private:
+    QString m_path;
+    QString m_fileString;
+    Status m_status;
+    QString m_errorString;
+};
+
+class ReaderThread : public QThread
+{
+    Q_OBJECT
+    Q_ENUMS(Status)
+
+public:
+    enum Status
+    {
+        Null,
+        Ready,
+        Loading,
+        Error
+    };
+
+    void run();
+    void setFilePath(QString path) { m_path = path; }
+
+Q_SIGNALS:
+    void errorChanged(QString error);
+    void fileRead(QString fileString);
+    void statusChanged(Status status);
+
+private:
+    QString m_path;
+};
+
+#endif // FILEREADER_H

=== added file 'src/plugin/text-qml-plugin/qmldir'
--- src/plugin/text-qml-plugin/qmldir	1970-01-01 00:00:00 +0000
+++ src/plugin/text-qml-plugin/qmldir	2015-04-30 17:36:20 +0000
@@ -0,0 +1,7 @@
+module DocumentViewer.Text
+plugin textqmlplugin
+
+TextEditor 1.0 TextEditor.qml
+TextEditorStyle 1.0 TextEditorStyle.qml
+
+internal LineNumberColumn LineNumberColumn.qml


References