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

Proposed by Stefano Verzegnassi on 2015-04-30
Status: Rejected
Rejected by: Stefano Verzegnassi on 2015-09-11
Proposed branch: lp:~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor
Merge into: lp:ubuntu-docviewer-app/trunk
Diff against target: 1599 lines (+1309/-51)
22 files modified
debian/changelog (+6/-0)
debian/control (+1/-0)
manifest.json.in (+1/-1)
po/com.ubuntu.docviewer.pot (+82/-14)
src/app/qml/textView/TextView.qml (+37/-35)
src/app/qml/textView/TextViewDefaultHeader.qml (+21/-0)
src/app/qml/textView/TextViewSelectionHeader.qml (+81/-0)
src/app/qml/textView/TextViewSettingsPage.qml (+127/-0)
src/app/qml/ubuntu-docviewer-app.qml (+2/-0)
src/plugin/CMakeLists.txt (+1/-0)
src/plugin/text-qml-plugin/CMakeLists.txt (+38/-0)
src/plugin/text-qml-plugin/LineNumberColumn.qml (+66/-0)
src/plugin/text-qml-plugin/TextEditor.qml (+332/-0)
src/plugin/text-qml-plugin/TextEditorStyle.qml (+35/-0)
src/plugin/text-qml-plugin/backend.cpp (+37/-0)
src/plugin/text-qml-plugin/backend.h (+34/-0)
src/plugin/text-qml-plugin/documenthandler.cpp (+108/-0)
src/plugin/text-qml-plugin/documenthandler.h (+85/-0)
src/plugin/text-qml-plugin/filereader.cpp (+110/-0)
src/plugin/text-qml-plugin/filereader.h (+97/-0)
src/plugin/text-qml-plugin/qmldir (+7/-0)
tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py (+1/-1)
To merge this branch: bzr merge lp:~verzegnassi-stefano/ubuntu-docviewer-app/advanced-text-editor
Reviewer Review Type Date Requested Status
Alan Pope 🍺🐧🐱 πŸ¦„ 2015-04-30 Needs Fixing on 2015-05-18
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve on 2015-04-30
Review via email: mp+257943@code.launchpad.net

Commit message

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

Description of the change

- 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.

To post a comment you must log in.
144. By Stefano Verzegnassi on 2015-04-30

Added a category for TextView settings entries

145. By Stefano Verzegnassi on 2015-04-30

Fixed TextView test

This is looking great for viewing small text files.

A few issues:-

* "Large" text files can lock up / crash the app. By "large" I mean 910kB such as https://www.gutenberg.org/cache/epub/48984/pg48984.txt

* First line number gets messed up with large files http://people.canonical.com/~alan/screenshots/device-2015-05-18-142938.png

* Edits don't get saved. If I create a small file (using vim/nano) then edit it in docviewer, and go back, the changes don't get saved to disk.

review: Needs Fixing

I'll answer point by point:

1) Yes, that happens also with the current code.
The issue is not about the reading of the file (which happens asynchronously although it's not really a time-waster process), but the issue is due to the filling of the text in the TextEdit component, which happens in the main threads (I don't know if the in GUI one, or the other).

I will have a look if it's possible to work around it, by using a Loader component.
Since the issue lives in an upstream component (TextEdit is provided by the Qt library), it may become very hard to fix.

2) I had hard times in order to reproduce it. Sure, it needs some fix!

3) Do we want to save them? As far as I remember, we discussed about it during some meeting and we decided to provide just a viewer.
I think we should provide the documents in a read-only mode, but eventually still allow to copy/paste text (which we said it's broken in the platform).

I agree that it should "only" view and not edit. So we should remove the buttons that make it editable, right?

Ideally, we may want to keep the selection mode/buttons: although the content is read-only, it should anyway be possible to copy a part of the text.

We should see if it's better to make some of the buttons (paste, copy, undo, redo) not visible, rather than simply disable them (but keep them still visible).

About the read-only mode, we should find out a way to provide some feedback to the user.
Since the new component looks like a text editor (and in future it will probably be), it may be useful to inform the user that the document is not editable.

A simple way to archieve this may be: https://imgur.com/aEY4a8T
Anyway I feel it's better to ask to the UX team, in order to have an organic, well-designed thing.

About the performance of the new component with huge text files, I saw that there are a few bottleneck that should be fixed before to have this branch merged.
The most relevant are:
    - Binding from the FileReader output to the textProperty of the TextEdit
    - Generation of line numbers (expecially when resizing the application window)

Unmerged revisions

145. By Stefano Verzegnassi on 2015-04-30

Fixed TextView test

144. By Stefano Verzegnassi on 2015-04-30

Added a category for TextView settings entries

143. By Stefano Verzegnassi on 2015-04-30

Bump Document Viewer version to 0.4

142. By Stefano Verzegnassi on 2015-04-30

Make CMake working for building DocumentViewer.Text module

141. By Stefano Verzegnassi on 2015-04-30

Updated theming stuff in DocumentViewer.Text plugin

140. By Stefano Verzegnassi on 2015-04-30

Updated translations

139. By Stefano Verzegnassi on 2015-04-30

Added settings for TextEditor component

138. By Stefano Verzegnassi on 2015-04-30

[textView] Added selection mode

137. By Stefano Verzegnassi on 2015-04-30

Added DocumentViewer.Text plugin

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2015-02-23 12:49:52 +0000
+++ debian/changelog 2015-04-30 18:50:23 +0000
@@ -1,3 +1,9 @@
1ubuntu-docviewer-app (0.4.0) utopic; urgency=medium
2
3 * Improved text editor
4
5 -- Stefano Verzegnassi <verzegnassi.stefano@gmail.com> Thu, 30 Apr 2015 19:12:24 +0200
6
1ubuntu-docviewer-app (0.3.0) utopic; urgency=medium7ubuntu-docviewer-app (0.3.0) utopic; urgency=medium
28
3 * Improved content-hub support9 * Improved content-hub support
410
=== modified file 'debian/control'
--- debian/control 2015-02-23 12:49:52 +0000
+++ debian/control 2015-04-30 18:50:23 +0000
@@ -28,6 +28,7 @@
28Architecture: any28Architecture: any
29Depends: qtdeclarative5-qtquick2-plugin,29Depends: qtdeclarative5-qtquick2-plugin,
30 qtdeclarative5-ubuntu-ui-toolkit-plugin,30 qtdeclarative5-ubuntu-ui-toolkit-plugin,
31 qml-module-qt-labs-settings,
31 ${misc:Depends}32 ${misc:Depends}
32Description: Document Viewer application33Description: Document Viewer application
33 Core Document Viewer application34 Core Document Viewer application
3435
=== modified file 'manifest.json.in'
--- manifest.json.in 2015-02-13 15:30:01 +0000
+++ manifest.json.in 2015-04-30 18:50:23 +0000
@@ -13,7 +13,7 @@
13 "urls": "@URLS_FILE@"13 "urls": "@URLS_FILE@"
14 }14 }
15 },15 },
16 "version": "0.3.@BZR_REVNO@",16 "version": "0.4.@BZR_REVNO@",
17 "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@lists.launchpad.net>",17 "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@lists.launchpad.net>",
18 "x-source": {18 "x-source": {
19 "vcs-bzr": "@BZR_SOURCE@",19 "vcs-bzr": "@BZR_SOURCE@",
2020
=== 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 18:50:23 +0000
@@ -8,7 +8,7 @@
8msgstr ""8msgstr ""
9"Project-Id-Version: \n"9"Project-Id-Version: \n"
10"Report-Msgid-Bugs-To: \n"10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2015-04-27 18:02+0200\n"11"POT-Creation-Date: 2015-04-30 19:09+0200\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -34,13 +34,13 @@
3434
35#: ../src/app/docviewer-application.cpp:16435#: ../src/app/docviewer-application.cpp:164
36#: ../src/app/qml/documentPage/DocumentPage.qml:2536#: ../src/app/qml/documentPage/DocumentPage.qml:25
37#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:137#: /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
38msgid "Document Viewer"38msgid "Document Viewer"
39msgstr ""39msgstr ""
4040
41#: ../src/app/qml/common/DetailsPage.qml:2741#: ../src/app/qml/common/DetailsPage.qml:27
42#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:9742#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:97
43#: ../src/app/qml/textView/TextViewDefaultHeader.qml:8343#: ../src/app/qml/textView/TextViewDefaultHeader.qml:104
44msgid "Details"44msgid "Details"
45msgstr ""45msgstr ""
4646
@@ -77,7 +77,7 @@
77#: ../src/app/qml/common/RejectedImportDialog.qml:3877#: ../src/app/qml/common/RejectedImportDialog.qml:38
78#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:3278#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:32
79#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:6179#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
80#: ../src/app/qml/textView/TextViewDefaultHeader.qml:6180#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
81msgid "Close"81msgid "Close"
82msgstr ""82msgstr ""
8383
@@ -274,7 +274,7 @@
274msgstr ""274msgstr ""
275275
276#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61276#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
277#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61277#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
278msgid "Back"278msgid "Back"
279msgstr ""279msgstr ""
280280
@@ -283,12 +283,12 @@
283msgstr ""283msgstr ""
284284
285#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91285#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
286#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77286#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
287msgid "Disable night mode"287msgid "Disable night mode"
288msgstr ""288msgstr ""
289289
290#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91290#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
291#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77291#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
292msgid "Enable night mode"292msgid "Enable night mode"
293msgstr ""293msgstr ""
294294
@@ -305,20 +305,88 @@
305msgid "GO!"305msgid "GO!"
306msgstr ""306msgstr ""
307307
308#: ../src/app/qml/textView/TextView.qml:42308#: ../src/app/qml/textView/TextViewDefaultHeader.qml:86
309msgid "Loading..."309msgid "Enable selection mode"
310msgstr ""310msgstr ""
311311
312#: ../src/app/qml/ubuntu-docviewer-app.qml:183312#: ../src/app/qml/textView/TextViewDefaultHeader.qml:98
313msgid "Settings"
314msgstr ""
315
316#: ../src/app/qml/textView/TextViewSelectionHeader.qml:36
317msgid "Select all"
318msgstr ""
319
320#: ../src/app/qml/textView/TextViewSelectionHeader.qml:42
321msgid "Paste"
322msgstr ""
323
324#: ../src/app/qml/textView/TextViewSelectionHeader.qml:48
325msgid "Cut"
326msgstr ""
327
328#: ../src/app/qml/textView/TextViewSelectionHeader.qml:54
329msgid "Copy"
330msgstr ""
331
332#: ../src/app/qml/textView/TextViewSelectionHeader.qml:60
333msgid "Redo"
334msgstr ""
335
336#: ../src/app/qml/textView/TextViewSelectionHeader.qml:67
337msgid "Undo"
338msgstr ""
339
340#: ../src/app/qml/textView/TextViewSelectionHeader.qml:75
341msgid "Exit selection mode"
342msgstr ""
343
344#: ../src/app/qml/textView/TextViewSettingsPage.qml:24
345msgid "Text settings"
346msgstr ""
347
348#: ../src/app/qml/textView/TextViewSettingsPage.qml:45
349msgid "Show line numbers"
350msgstr ""
351
352#: ../src/app/qml/textView/TextViewSettingsPage.qml:61
353msgid "Enable text wrapping"
354msgstr ""
355
356#: ../src/app/qml/textView/TextViewSettingsPage.qml:77
357msgid "Highlight current line"
358msgstr ""
359
360#: ../src/app/qml/textView/TextViewSettingsPage.qml:88
361msgid "Right margin settings"
362msgstr ""
363
364#: ../src/app/qml/textView/TextViewSettingsPage.qml:95
365msgid "Show right margin"
366msgstr ""
367
368#: ../src/app/qml/textView/TextViewSettingsPage.qml:111
369msgid "Show at column (default = 80):"
370msgstr ""
371
372#: ../src/app/qml/ubuntu-docviewer-app.qml:185
313msgid "Document successfully imported!"373msgid "Document successfully imported!"
314msgid_plural "Documents successfully imported!"374msgid_plural "Documents successfully imported!"
315msgstr[0] ""375msgstr[0] ""
316msgstr[1] ""376msgstr[1] ""
317377
318#: ../src/app/qml/ubuntu-docviewer-app.qml:186378#: ../src/app/qml/ubuntu-docviewer-app.qml:188
319msgid "Open"379msgid "Open"
320msgstr ""380msgstr ""
321381
322#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2382#: ../src/plugin/text-qml-plugin/filereader.cpp:90
383msgid "Could not open file for reading."
384msgstr ""
385
386#: ../src/plugin/text-qml-plugin/filereader.cpp:107
387msgid "No file specified."
388msgstr ""
389
390#: /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
323msgid "documents;viewer;pdf;reader;"391msgid "documents;viewer;pdf;reader;"
324msgstr ""392msgstr ""
325393
=== 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 18:50:23 +0000
@@ -16,7 +16,8 @@
1616
17import QtQuick 2.317import QtQuick 2.3
18import Ubuntu.Components 1.118import Ubuntu.Components 1.1
19import Ubuntu.Components.Themes.Ambiance 0.119import DocumentViewer.Text 1.0
20import Qt.labs.settings 1.0
2021
21import "../common/utils.js" as Utils22import "../common/utils.js" as Utils
2223
@@ -24,50 +25,51 @@
24 id: textPage25 id: textPage
25 title: Utils.getNameOfFile(file.path);26 title: Utils.getNameOfFile(file.path);
2627
28 property bool selectionModeEnabled: false
29 property alias editor: editor
30
27 // Reset night mode shader settings when closing the page31 // Reset night mode shader settings when closing the page
28 // Component.onDestruction: mainView.nightModeEnabled = false32 // Component.onDestruction: mainView.nightModeEnabled = false
2933
30 TextArea {34 Settings {
31 id: textAreaMain35 category: "TextViewer"
32 objectName: "textAreaMain"36
3337 property alias displayLineNumbers: editor.displayLineNumbers
34 property bool isLoading: true38 property alias textWrap: editor.textWrap
3539 property alias rightMarginAtColumn: editor.rightMarginAtColumn
40 property alias showRightMargin: editor.showRightMargin
41 property alias highlightCurrentLine: editor.highlightCurrentLine
42 }
43
44 TextReader {
45 id: reader
46 path: file.path
47
48 onErrorStringChanged: console.log(errorString)
49 }
50
51 TextEditor {
52 id: editor
53 objectName: "editor"
36 anchors.fill: parent54 anchors.fill: parent
3755
38 // FIXME: If set to true, some of the keyboard hooks are disabled56 selectionMode: textPage.selectionModeEnabled
39 // And it's not possible to move the cursor with arrow keys.57 text: reader.fileString
40 readOnly: true
41
42 text: i18n.tr("Loading...")
43 font.family: "UbuntuMono"
44
45 Component.onCompleted: {
46 var xhr = new XMLHttpRequest;
47
48 xhr.open("GET", file.path);
49 xhr.onreadystatechange = function() {
50 if (xhr.readyState === XMLHttpRequest.DONE) {
51 textAreaMain.text = xhr.responseText;
52 textAreaMain.isLoading = false
53 }
54 };
55
56 xhr.send();
57 }
58
59 style: TextAreaStyle {
60 background: Rectangle { color: "white" }
61 }
62 }58 }
6359
64 // *** HEADER ***60 // *** HEADER ***
65 state: "default"
66 states: [61 states: [
67 TextViewDefaultHeader {62 TextViewDefaultHeader {
68 name: "default"63 when: !textPage.selectionModeEnabled
69 targetPage: textPage64
70 activityRunning: textAreaMain.isLoading65 targetPage: textPage
66 activityRunning: reader.status == TextReader.Loading
67 },
68
69 TextViewSelectionHeader {
70 when: textPage.selectionModeEnabled
71
72 targetPage: textPage
71 }73 }
72 ]74 ]
73}75}
7476
=== 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 18:50:23 +0000
@@ -31,6 +31,8 @@
31 anchors.fill: parent31 anchors.fill: parent
32 spacing: units.gu(1)32 spacing: units.gu(1)
3333
34 visible: !targetPage.selectionModeEnabled
35
34 ActivityIndicator { id: activity }36 ActivityIndicator { id: activity }
3537
36 Column {38 Column {
@@ -73,12 +75,31 @@
7375
74 actions: [76 actions: [
75 Action {77 Action {
78 iconName: "search"
79 // onTriggered: target.state = "search"
80 //Disable it until we provide search in Text plugin.
81 enabled: false
82 },
83
84 Action {
85 iconName: "edit-select-all"
86 text: i18n.tr("Enable selection mode")
87 onTriggered: targetPage.selectionModeEnabled = true
88 },
89
90 Action {
76 iconName: "night-mode"91 iconName: "night-mode"
77 text: mainView.nightModeEnabled ? i18n.tr("Disable night mode") : i18n.tr("Enable night mode")92 text: mainView.nightModeEnabled ? i18n.tr("Disable night mode") : i18n.tr("Enable night mode")
78 onTriggered: mainView.nightModeEnabled = !mainView.nightModeEnabled93 onTriggered: mainView.nightModeEnabled = !mainView.nightModeEnabled
79 },94 },
8095
81 Action {96 Action {
97 iconName: "settings"
98 text: i18n.tr("Settings")
99 onTriggered: pageStack.push(Qt.resolvedUrl("TextViewSettingsPage.qml"), { textPage: targetPage })
100 },
101
102 Action {
82 objectName: "detailsAction"103 objectName: "detailsAction"
83 text: i18n.tr("Details")104 text: i18n.tr("Details")
84 iconName: "info"105 iconName: "info"
85106
=== 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 18:50:23 +0000
@@ -0,0 +1,81 @@
1/*
2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components 1.1
19import "../upstreamComponents"
20
21PageHeadState {
22 id: rootItem
23
24 property Page targetPage
25 head: targetPage.head
26
27 contents: Row {
28 width: parent ? parent.width : 0
29 height: parent ? parent.height : 0
30
31 layoutDirection: Qt.RightToLeft
32 visible: targetPage.selectionModeEnabled
33
34 HeaderButton {
35 iconName: "edit-select-all"
36 text: i18n.tr("Select all")
37 onTriggered: targetPage.editor.selectAll()
38 }
39
40 HeaderButton {
41 iconName: "edit-paste"
42 text: i18n.tr("Paste")
43 onTriggered: targetPage.editor.paste()
44 }
45
46 HeaderButton {
47 iconName: "edit-cut"
48 text: i18n.tr("Cut")
49 onTriggered: targetPage.editor.cut()
50 }
51
52 HeaderButton {
53 iconName: "edit-copy"
54 text: i18n.tr("Copy")
55 onTriggered: targetPage.editor.copy()
56 }
57
58 HeaderButton {
59 iconName: "edit-redo"
60 text: i18n.tr("Redo")
61 onTriggered: targetPage.editor.redo()
62 enabled: targetPage.editor.canRedo
63 }
64
65 HeaderButton {
66 iconName: "edit-undo"
67 text: i18n.tr("Undo")
68 onTriggered: targetPage.editor.undo()
69 enabled: targetPage.editor.canUndo
70 }
71 }
72
73 backAction: Action {
74 iconName: "back"
75 text: i18n.tr("Exit selection mode")
76 onTriggered: {
77 targetPage.editor.deselect()
78 targetPage.selectionModeEnabled = false;
79 }
80 }
81}
082
=== 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 18:50:23 +0000
@@ -0,0 +1,127 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components 1.1
19import QtQuick.Layouts 1.1
20import Ubuntu.Components.ListItems 1.0 as ListItem
21
22Page {
23 id: textSettingsPage
24 title: i18n.tr("Text settings")
25
26 property var textPage
27
28 Component.onCompleted: {
29 // Get settings
30 lineNumbersCheckBox.checked = textPage.editor.displayLineNumbers
31 textWrapCheckbox.checked = textPage.editor.textWrap
32 highlightCurrentLineCheckbox.checked = textPage.editor.highlightCurrentLine
33 rightMarginCheckbox.checked = textPage.editor.showRightMargin
34 rightMarginTextField.text = textPage.editor.rightMarginAtColumn
35 }
36
37 Column {
38 anchors.fill: parent
39
40 ListItem.Base {
41 RowLayout {
42 anchors.fill: parent
43
44 Label {
45 text: i18n.tr("Show line numbers")
46 Layout.fillWidth: true
47 }
48
49 CheckBox {
50 id: lineNumbersCheckBox
51 onCheckedChanged: textPage.editor.displayLineNumbers = checked;
52 }
53 }
54 }
55
56 ListItem.Base {
57 RowLayout {
58 anchors.fill: parent
59
60 Label {
61 text: i18n.tr("Enable text wrapping")
62 Layout.fillWidth: true
63 }
64
65 CheckBox {
66 id: textWrapCheckbox
67 onCheckedChanged: textPage.editor.textWrap = checked
68 }
69 }
70 }
71
72 ListItem.Base {
73 RowLayout {
74 anchors.fill: parent
75
76 Label {
77 text: i18n.tr("Highlight current line")
78 Layout.fillWidth: true
79 }
80
81 CheckBox {
82 id: highlightCurrentLineCheckbox
83 onCheckedChanged: textPage.editor.highlightCurrentLine = checked
84 }
85 }
86 }
87
88 ListItem.Header { text: i18n.tr("Right margin settings") }
89
90 ListItem.Base {
91 RowLayout {
92 anchors.fill: parent
93
94 Label {
95 text: i18n.tr("Show right margin")
96 Layout.fillWidth: true
97 }
98
99 CheckBox {
100 id: rightMarginCheckbox
101 onCheckedChanged: textPage.editor.showRightMargin = checked
102 }
103 }
104 }
105
106 ListItem.Base {
107 RowLayout {
108 anchors.fill: parent
109
110 Label {
111 text: i18n.tr("Show at column (default = 80):")
112
113 Layout.fillWidth: true
114 }
115
116 TextField {
117 id: rightMarginTextField
118 implicitWidth: units.gu(12)
119 inputMethodHints: Qt.ImhDigitsOnly
120 validator: IntValidator { bottom: 1; top: 150 }
121
122 onTextChanged: textPage.editor.rightMarginAtColumn = parseInt(text)
123 }
124 }
125 }
126 }
127}
0128
=== 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 18:50:23 +0000
@@ -34,7 +34,9 @@
3434
35 applicationName: "com.ubuntu.docviewer"35 applicationName: "com.ubuntu.docviewer"
36 useDeprecatedToolbar: false 36 useDeprecatedToolbar: false
37
37 automaticOrientation: true38 automaticOrientation: true
39 anchorToKeyboard: true
3840
39 width: units.gu(50)41 width: units.gu(50)
40 height: units.gu(75)42 height: units.gu(75)
4143
=== modified file 'src/plugin/CMakeLists.txt'
--- src/plugin/CMakeLists.txt 2014-10-20 21:38:36 +0000
+++ src/plugin/CMakeLists.txt 2015-04-30 18:50:23 +0000
@@ -1,2 +1,3 @@
1add_subdirectory(file-qml-plugin)1add_subdirectory(file-qml-plugin)
2add_subdirectory(poppler-qml-plugin)2add_subdirectory(poppler-qml-plugin)
3add_subdirectory(text-qml-plugin)
34
=== 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 18:50:23 +0000
@@ -0,0 +1,38 @@
1set(PLUGIN_DIR DocumentViewer/Text)
2include_directories(
3 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_BINARY_DIR}
5)
6
7#add QML resources
8file(GLOB_RECURSE QML_SRCS
9 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
10 *.qml *.js qmldir
11)
12
13#add the sources to compile
14set(textqmlplugin_SRCS
15 backend.cpp
16 documenthandler.cpp
17 filereader.cpp
18 ${QML_SRCS}
19)
20
21add_library(textqmlplugin MODULE ${textqmlplugin_SRCS})
22qt5_use_modules(textqmlplugin Qml Quick)
23
24# Copy the plugin, the qmldir file and other assets to the build dir for running in QtCreator
25if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
26 add_custom_command(TARGET textqmlplugin POST_BUILD
27 COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
28 COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
29 COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/LineNumberColumn.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
30 COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditor.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
31 COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditorStyle.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
32 COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:textqmlplugin> ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
33 )
34endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
35
36# Install plugin file
37install(TARGETS textqmlplugin DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})
38install(FILES ${QML_SRCS} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})
039
=== 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 18:50:23 +0000
@@ -0,0 +1,66 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components 1.1
19
20Rectangle {
21 id: lineColumn
22
23 width: visible ? units.gu(3.5) + lineNumberRepeater.itemAt(lineNumberRepeater.count - 1).paintedWidth : 0
24
25 property int topMargin
26 property alias model: lineNumberRepeater.model
27 property var font
28
29 property alias dividerColor: divider.color
30 property alias dividerWidth: divider.width
31 property color fontColor
32
33 Rectangle {
34 id: divider
35 height: parent.height
36 anchors.right: parent.right
37 color: lineColumn.dividerColor
38 }
39
40 Column {
41 id: layout
42 anchors {
43 top: parent.top
44 left: parent.left
45 right: parent.right
46 topMargin: lineColumn.topMargin
47 rightMargin: units.gu(2)
48 leftMargin: units.gu(1.5)
49 }
50
51 Repeater {
52 id: lineNumberRepeater
53
54 delegate: Text {
55 width: layout.width
56 height: modelData.lineHeight
57 text: modelData.lineNumber + 1
58
59 horizontalAlignment: Text.AlignRight
60
61 font: lineColumn.font
62 color: lineColumn.fontColor
63 }
64 }
65 }
66}
067
=== 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 18:50:23 +0000
@@ -0,0 +1,332 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components 1.1
19import DocumentViewer.Text 1.0
20
21StyledItem {
22 id: rootItem
23
24 property alias text: textArea.text
25 property alias readOnly: textArea.readOnly
26
27 property alias mouseSelectionMode: textArea.mouseSelectionMode
28 property alias selectionMode: textArea.selectByMouse
29
30 property bool displayLineNumbers: true
31 property bool textWrap: false
32 property int rightMarginAtColumn: 80
33 property bool showRightMargin: true
34 property bool highlightCurrentLine: false
35
36 property alias canPaste: textArea.canPaste
37 property alias canRedo: textArea.canRedo
38 property alias canUndo: textArea.canUndo
39
40 // functions
41 /*!
42 Copies the currently selected text to the system clipboard.
43 */
44 function copy()
45 {
46 textArea.copy();
47 }
48
49 /*!
50 Moves the currently selected text to the system clipboard.
51 */
52 function cut()
53 {
54 textArea.cut();
55 }
56
57 /*!
58 Removes active text selection.
59 */
60 function deselect()
61 {
62 textArea.deselect();
63 }
64
65 /*!
66 Inserts text into the TextArea at position.
67 */
68 function insert(position, text)
69 {
70 textArea.insert(position, text);
71 }
72
73 /*!
74 Returns the text position closest to pixel position (x, y).
75
76 Position 0 is before the first character, position 1 is after the first
77 character but before the second, and so on until position text.length,
78 which is after all characters.
79 */
80 function positionAt(x, y)
81 {
82 return textArea.positionAt(x, y);
83 }
84
85 /*!
86 Returns true if the natural reading direction of the textArea text found
87 between positions start and end is right to left.
88 */
89 function isRightToLeft(start, end)
90 {
91 return textArea.isRightToLeft(start, end)
92 }
93
94 /*!
95 Moves the cursor to position and updates the selection according to the
96 optional mode parameter. (To only move the cursor, set the cursorPosition
97 property.)
98
99 When this method is called it additionally sets either the selectionStart
100 or the selectionEnd (whichever was at the previous cursor position) to the
101 specified position. This allows you to easily extend and contract the selected
102 text range.
103
104 The selection mode specifies whether the selection is updated on a per character
105 or a per word basis. If not specified the selection mode will default to whatever
106 is given in the mouseSelectionMode property.
107 */
108 function moveCursorSelection(position, mode)
109 {
110 if (mode === undefined)
111 textArea.moveCursorSelection(position, mouseSelectionMode);
112 else
113 textArea.moveCursorSelection(position, mode);
114 }
115
116 /*!
117 Places the clipboard or the data given as parameter into the text input.
118 The selected text will be replaces with the data.
119 */
120 function paste(data) {
121 if ((data !== undefined) && (typeof data === "string") && !textArea.readOnly) {
122 var selTxt = textArea.selectedText;
123 var txt = textArea.text;
124 var pos = (selTxt !== "") ? txt.indexOf(selTxt) : textArea.cursorPosition
125 if (selTxt !== "") {
126 textArea.text = txt.substring(0, pos) + data + txt.substr(pos + selTxt.length);
127 } else {
128 textArea.text = txt.substring(0, pos) + data + txt.substr(pos);
129 }
130 textArea.cursorPosition = pos + data.length;
131 } else
132 textArea.paste();
133 }
134
135 /*!
136 Returns the rectangle at the given position in the text. The x, y, and height
137 properties correspond to the cursor that would describe that position.
138 */
139 function positionToRectangle(position)
140 {
141 return textArea.positionToRectangle(position);
142 }
143
144 /*!
145 Redoes the last operation if redo is \l{canRedo}{available}.
146 */
147 function redo()
148 {
149 textArea.redo();
150 }
151
152 /*!
153 Causes the text from start to end to be selected.
154
155 If either start or end is out of range, the selection is not changed.
156
157 After calling this, selectionStart will become the lesser and selectionEnd
158 will become the greater (regardless of the order passed to this method).
159
160 See also selectionStart and selectionEnd.
161 */
162 function select(start, end)
163 {
164 textArea.select(start, end);
165 }
166
167 /*!
168 Causes all text to be selected.
169 */
170 function selectAll()
171 {
172 textArea.selectAll();
173 }
174
175 /*!
176 Causes the word closest to the current cursor position to be selected.
177 */
178 function selectWord()
179 {
180 textArea.selectWord();
181 }
182
183 /*!
184 Returns the section of text that is between the start and end positions.
185
186 The returned text does not include any rich text formatting. A getText(0, length)
187 will result in the same value as displayText.
188 */
189 function getText(start, end)
190 {
191 return textArea.getText(start, end);
192 }
193
194 /*!
195 Removes the section of text that is between the start and end positions
196 from the TextEditor.
197 */
198 function remove(start, end)
199 {
200 return textArea.remove(start, end);
201 }
202
203 /*!
204 Undoes the last operation if undo is \l{canUndo}{available}. Deselects
205 any current selection, and updates the selection start to the current
206 cursor position.
207 */
208 function undo()
209 {
210 textArea.undo();
211 }
212
213 clip: true
214 style: Qt.createComponent("TextEditorStyle.qml", rootItem)
215
216 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
217 LayoutMirroring.childrenInherit: true
218
219 Rectangle {
220 color: rootItem.__styleInstance.backgroundColor
221 anchors.fill: parent
222
223 Rectangle {
224 visible: rootItem.showRightMargin && (rootItem.rightMarginAtColumn > 0)
225 color: rootItem.__styleInstance.secondaryBackgroundColor
226
227 anchors {
228 fill: parent
229 leftMargin: dummyFont.paintedWidth * rootItem.rightMarginAtColumn + lineColumn.width + flicker.margin - flicker.contentX
230 }
231
232 Rectangle {
233 height: parent.height
234 anchors.left: parent.left
235 width: rootItem.__styleInstance.dividerWidth
236 color: rootItem.__styleInstance.dividerColor
237 }
238 }
239
240 Rectangle {
241 width: parent.width
242 height: textArea.cursorRectangle.height
243
244 y: textArea.cursorRectangle.y + flicker.margin - flicker.contentY
245 color: rootItem.__styleInstance.currentLineHighlightColor
246 opacity: 0.3
247
248 visible: rootItem.highlightCurrentLine && textArea.cursorVisible
249 }
250 }
251
252 Flickable {
253 id: flicker
254 anchors.fill: parent
255
256 property int margin: rootItem.__styleInstance.frameSpacing
257
258 contentWidth: textArea.paintedWidth + lineColumn.width
259 contentHeight: textArea.paintedHeight + flicker.margin * 2
260
261 boundsBehavior: Flickable.StopAtBounds
262
263 LineNumberColumn {
264 id: lineColumn
265
266 color: rootItem.__styleInstance.secondaryBackgroundColor
267 dividerColor: rootItem.__styleInstance.dividerColor
268 fontColor: rootItem.__styleInstance.selectedTextColor
269 dividerWidth: rootItem.__styleInstance.dividerWidth
270
271 visible: rootItem.displayLineNumbers
272 model: documentHandler.lineBlocks
273 font: textArea.font
274
275 height: Math.max(flicker.contentHeight, flicker.height)
276 topMargin: flicker.margin
277 }
278
279 TextEdit {
280 id: textArea
281
282 focus: true
283 width: rootItem.width - lineColumn.width
284 height: Math.max(textArea.contentHeight, textArea.contentHeight)
285 anchors {
286 left: lineColumn.right
287 top: parent.top
288 margins: flicker.margin
289 }
290
291 mouseSelectionMode: TextEdit.SelectWords
292 selectByMouse: false
293 activeFocusOnPress: true
294
295 color: rootItem.__styleInstance.color
296 selectedTextColor: rootItem.__styleInstance.selectedTextColor
297 selectionColor: rootItem.__styleInstance.selectionColor
298
299 // Work as a code editor
300 textFormat: TextEdit.PlainText
301 font.family: "UbuntuMono"
302 font.pixelSize: FontUtils.sizeToPixels("medium")
303
304 wrapMode: rootItem.textWrap ? TextEdit.Wrap
305 : TextEdit.NoWrap
306 }
307 }
308
309 Label {
310 id: dummyFont
311 text: "a"
312 font: textArea.font
313 visible: false
314 }
315
316 DocumentHandler {
317 id: documentHandler
318 textEditItem: textArea
319 }
320
321 Scrollbar {
322 id: rightScrollbar
323 flickableItem: flicker
324 }
325
326 Scrollbar {
327 id: bottomScrollbar
328 flickableItem: flicker
329 align: Qt.AlignBottom
330 }
331}
332
0333
=== 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 18:50:23 +0000
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.3
18import Ubuntu.Components 1.1
19
20Item {
21 id: visuals
22
23 property color backgroundColor: "white"
24 property color secondaryBackgroundColor: "#f2f2f2"
25 property color dividerColor: "#ddd"
26 property color currentLineHighlightColor: UbuntuColors.blue
27
28 property color color: styledItem.activeFocus ? Theme.palette.selected.fieldText : Theme.palette.normal.fieldText
29 property color selectedTextColor: Theme.palette.selected.foregroundText
30 property color selectionColor: Theme.palette.selected.foreground
31
32 property real frameSpacing: units.gu(1)
33 property real dividerWidth: units.gu(0.1)
34}
35
036
=== 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 18:50:23 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include <QtQml>
19#include <QtQml/QQmlContext>
20
21#include "backend.h"
22#include "documenthandler.h"
23#include "filereader.h"
24
25void BackendPlugin::registerTypes(const char *uri)
26{
27 Q_ASSERT(uri == QLatin1String("DocumentViewer.Text"));
28
29 //@uri DocumentViewer.Text
30 qmlRegisterType<FileReader>(uri, 1, 0, "TextReader");
31 qmlRegisterType<DocumentHandler>(uri, 1, 0, "DocumentHandler");
32}
33
34void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
35{
36 QQmlExtensionPlugin::initializeEngine(engine, uri);
37}
038
=== 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 18:50:23 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#ifndef BACKEND_PLUGIN_H
19#define BACKEND_PLUGIN_H
20
21#include <QtQml/QQmlEngine>
22#include <QtQml/QQmlExtensionPlugin>
23
24class BackendPlugin : public QQmlExtensionPlugin
25{
26 Q_OBJECT
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28
29public:
30 void registerTypes(const char *uri);
31 void initializeEngine(QQmlEngine *engine, const char *uri);
32};
33#endif // BACKEND_PLUGIN_H
34
035
=== 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 18:50:23 +0000
@@ -0,0 +1,108 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt Quick Controls module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** You may use this file under the terms of the BSD license as follows:
10**
11** "Redistribution and use in source and binary forms, with or without
12** modification, are permitted provided that the following conditions are
13** met:
14** * Redistributions of source code must retain the above copyright
15** notice, this list of conditions and the following disclaimer.
16** * Redistributions in binary form must reproduce the above copyright
17** notice, this list of conditions and the following disclaimer in
18** the documentation and/or other materials provided with the
19** distribution.
20** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21** of its contributors may be used to endorse or promote products derived
22** from this software without specific prior written permission.
23**
24**
25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "documenthandler.h"
42
43#include <QtGui/QTextDocument>
44#include <QtGui/QTextBlock>
45#include <QtGui/QTextLayout>
46
47DocumentHandler::DocumentHandler() :
48 m_textEditItem(nullptr),
49 m_doc(nullptr)
50{
51 //
52}
53
54void DocumentHandler::setTextEditItem(QQuickItem *textEditItem)
55{
56 if (textEditItem == m_textEditItem)
57 return;
58
59 QString className(textEditItem->metaObject()->className());
60 if (className != "QQuickTextEdit") {
61 qWarning() << "Item passed as argument is not a QQuickTextEdit. Aborting...";
62 return;
63 }
64
65 m_doc = nullptr;
66 m_textEditItem = textEditItem;
67
68 QVariant doc = m_textEditItem->property("textDocument");
69 if (doc.canConvert<QQuickTextDocument*>()) {
70 QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument*>();
71
72 if (qqdoc) {
73 m_doc = qqdoc->textDocument();
74
75 // TODO: We can also use lengthChanged() from TextEdit?
76 connect(m_doc, SIGNAL(contentsChanged()), this, SLOT(updateLineBlocks()));
77 connect(m_textEditItem, SIGNAL(contentSizeChanged()), this, SLOT(updateLineBlocks()));
78 connect(m_textEditItem, SIGNAL(wrapModeChanged()), this, SLOT(updateLineBlocks()));
79 }
80 }
81
82 Q_EMIT textEditItemChanged();
83}
84
85void DocumentHandler::updateLineBlocks()
86{
87 m_lineBlocks.clear();
88
89 QTextBlock block = m_doc->firstBlock();
90 int blockNumber = block.blockNumber();
91 qreal blockHeight;
92
93 while (blockNumber < m_doc->blockCount()) {
94 blockHeight = block.layout()->boundingRect().height();
95
96 QVariantMap map;
97 map["lineNumber"] = blockNumber;
98 map["lineHeight"] = blockHeight;
99
100 m_lineBlocks.append(map);
101
102 block = block.next();
103 blockNumber++;
104 }
105
106 Q_EMIT lineBlocksChanged();
107}
108
0109
=== 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 18:50:23 +0000
@@ -0,0 +1,85 @@
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt Quick Controls module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** You may use this file under the terms of the BSD license as follows:
10**
11** "Redistribution and use in source and binary forms, with or without
12** modification, are permitted provided that the following conditions are
13** met:
14** * Redistributions of source code must retain the above copyright
15** notice, this list of conditions and the following disclaimer.
16** * Redistributions in binary form must reproduce the above copyright
17** notice, this list of conditions and the following disclaimer in
18** the documentation and/or other materials provided with the
19** distribution.
20** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21** of its contributors may be used to endorse or promote products derived
22** from this software without specific prior written permission.
23**
24**
25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#ifndef DOCUMENTHANDLER_H
42#define DOCUMENTHANDLER_H
43
44#include <QQuickTextDocument>
45
46#include <QtGui/QTextCharFormat>
47#include <QtCore/QTextCodec>
48
49#include <qqmlfile.h>
50
51QT_BEGIN_NAMESPACE
52class QTextDocument;
53QT_END_NAMESPACE
54
55class DocumentHandler : public QObject
56{
57 Q_OBJECT
58
59 Q_PROPERTY(QQuickItem *textEditItem READ textEditItem WRITE setTextEditItem NOTIFY textEditItemChanged)
60 Q_PROPERTY(QVariantList lineBlocks READ lineBlocks NOTIFY lineBlocksChanged)
61
62public:
63 DocumentHandler();
64
65 QQuickItem *textEditItem() { return m_textEditItem; }
66 void setTextEditItem(QQuickItem *textEditItem);
67
68 QVariantList lineBlocks() { return m_lineBlocks; }
69
70Q_SIGNALS:
71 void textEditItemChanged();
72 void lineBlocksChanged();
73 void highlighterChanged();
74
75private slots:
76 void updateLineBlocks();
77
78private:
79 QQuickItem *m_textEditItem;
80 QTextDocument *m_doc;
81
82 QVariantList m_lineBlocks;
83};
84
85#endif
086
=== 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 18:50:23 +0000
@@ -0,0 +1,110 @@
1/*
2 * Copyright (C) 2013, 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <QFile>
18#include <QObject>
19#include <QDebug>
20#include <QTextStream>
21
22#include "filereader.h"
23
24FileReader::FileReader(QObject *parent) :
25 QObject(parent),
26 m_path(""),
27 m_status(Status::Null)
28{
29 qRegisterMetaType<Status>("Status");
30}
31
32FileReader::~FileReader() {
33
34}
35
36void FileReader::setPath(QString path) {
37
38 if (path == m_path && path.isEmpty())
39 return;
40
41 m_path = path;
42 Q_EMIT pathChanged();
43
44 ReaderThread *p = new ReaderThread();
45 p->setFilePath(path);
46
47 connect(p, SIGNAL(fileRead(QString)), this, SLOT(setFileString(QString)));
48 connect(p, SIGNAL(errorChanged(QString)), this, SLOT(setErrorString(QString)));
49 connect(p, SIGNAL(statusChanged(Status)), this, SLOT(setStatus(Status)));
50 connect(p, SIGNAL(finished()), p, SLOT(deleteLater()));
51
52 p->start();
53}
54
55void FileReader::setFileString(QString string)
56{
57 if (m_fileString != string) {
58 m_fileString = string;
59 Q_EMIT fileStringChanged();
60 }
61}
62
63void FileReader::setStatus(Status status)
64{
65 if (m_status != status) {
66 m_status = status;
67 Q_EMIT statusChanged();
68 }
69}
70
71void FileReader::setErrorString(QString errorString)
72{
73 if (m_errorString != errorString) {
74 m_errorString = errorString;
75 Q_EMIT errorStringChanged();
76 }
77}
78
79void ReaderThread::run() {
80 if (!m_path.isEmpty()) {
81 Q_EMIT statusChanged(Status::Loading);
82
83 QFile file(m_path);
84
85 if (!file.open(QFile::ReadOnly | QFile::Text))
86 {
87 qWarning() << "Could not open file for reading.";
88
89 Q_EMIT statusChanged(Status::Error);
90 Q_EMIT errorChanged(tr("Could not open file for reading."));
91 Q_EMIT fileRead(QString(""));
92 }
93
94 QTextStream in(&file);
95 QString text = in.readAll();
96
97 file.flush();
98 file.close();
99
100 Q_EMIT statusChanged(Status::Ready);
101 Q_EMIT errorChanged(QString(""));
102 Q_EMIT fileRead(text);
103 } else {
104 qWarning() << "No file specified.";
105
106 Q_EMIT statusChanged(Status::Error);
107 Q_EMIT errorChanged(tr("No file specified."));
108 Q_EMIT fileRead(QString(""));
109 }
110}
0111
=== 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 18:50:23 +0000
@@ -0,0 +1,97 @@
1/*
2 * Copyright (C) 2013, 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef FILEREADER_H
18#define FILEREADER_H
19
20#include <QObject>
21#include <QThread>
22
23class FileReader: public QObject
24{
25 Q_OBJECT
26 Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged)
27 Q_PROPERTY(QString fileString READ getfileString NOTIFY fileStringChanged)
28 Q_PROPERTY(Status status READ getStatus NOTIFY statusChanged)
29 Q_PROPERTY(QString errorString READ getErrorString NOTIFY errorStringChanged)
30 Q_ENUMS(Status)
31
32public:
33 explicit FileReader(QObject *parent = 0);
34 ~FileReader();
35
36 enum Status
37 {
38 Null,
39 Ready,
40 Loading,
41 Error
42 };
43
44 QString getPath() { return m_path; }
45 void setPath(QString path);
46
47 QString getErrorString() { return m_errorString; }
48
49 QString getfileString() { return m_fileString; }
50
51 Status getStatus() { return m_status; }
52
53Q_SIGNALS:
54 void pathChanged();
55 void fileStringChanged();
56 void statusChanged();
57 void errorStringChanged();
58
59private slots:
60 void setFileString(QString string);
61 void setStatus(Status status);
62 void setErrorString(QString errorString);
63
64private:
65 QString m_path;
66 QString m_fileString;
67 Status m_status;
68 QString m_errorString;
69};
70
71class ReaderThread : public QThread
72{
73 Q_OBJECT
74 Q_ENUMS(Status)
75
76public:
77 enum Status
78 {
79 Null,
80 Ready,
81 Loading,
82 Error
83 };
84
85 void run();
86 void setFilePath(QString path) { m_path = path; }
87
88Q_SIGNALS:
89 void errorChanged(QString error);
90 void fileRead(QString fileString);
91 void statusChanged(Status status);
92
93private:
94 QString m_path;
95};
96
97#endif // FILEREADER_H
098
=== 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 18:50:23 +0000
@@ -0,0 +1,7 @@
1module DocumentViewer.Text
2plugin textqmlplugin
3
4TextEditor 1.0 TextEditor.qml
5TextEditorStyle 1.0 TextEditorStyle.qml
6
7internal LineNumberColumn LineNumberColumn.qml
08
=== modified file 'tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py'
--- tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py 2015-04-14 15:37:06 +0000
+++ tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py 2015-04-30 18:50:23 +0000
@@ -29,7 +29,7 @@
29 self.launch_app()29 self.launch_app()
3030
31 text_area = self.app.main_view.select_single(31 text_area = self.app.main_view.select_single(
32 "TextArea", objectName="textAreaMain")32 "TextEditor", objectName="editor")
3333
34 self.assertThat(34 self.assertThat(
35 text_area.text, Eventually(Equals('TEST\n')))35 text_area.text, Eventually(Equals('TEST\n')))

Subscribers

People subscribed via source and target branches