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
1=== modified file 'debian/changelog'
2--- debian/changelog 2015-02-23 12:49:52 +0000
3+++ debian/changelog 2015-04-30 18:50:23 +0000
4@@ -1,3 +1,9 @@
5+ubuntu-docviewer-app (0.4.0) utopic; urgency=medium
6+
7+ * Improved text editor
8+
9+ -- Stefano Verzegnassi <verzegnassi.stefano@gmail.com> Thu, 30 Apr 2015 19:12:24 +0200
10+
11 ubuntu-docviewer-app (0.3.0) utopic; urgency=medium
12
13 * Improved content-hub support
14
15=== modified file 'debian/control'
16--- debian/control 2015-02-23 12:49:52 +0000
17+++ debian/control 2015-04-30 18:50:23 +0000
18@@ -28,6 +28,7 @@
19 Architecture: any
20 Depends: qtdeclarative5-qtquick2-plugin,
21 qtdeclarative5-ubuntu-ui-toolkit-plugin,
22+ qml-module-qt-labs-settings,
23 ${misc:Depends}
24 Description: Document Viewer application
25 Core Document Viewer application
26
27=== modified file 'manifest.json.in'
28--- manifest.json.in 2015-02-13 15:30:01 +0000
29+++ manifest.json.in 2015-04-30 18:50:23 +0000
30@@ -13,7 +13,7 @@
31 "urls": "@URLS_FILE@"
32 }
33 },
34- "version": "0.3.@BZR_REVNO@",
35+ "version": "0.4.@BZR_REVNO@",
36 "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@lists.launchpad.net>",
37 "x-source": {
38 "vcs-bzr": "@BZR_SOURCE@",
39
40=== modified file 'po/com.ubuntu.docviewer.pot'
41--- po/com.ubuntu.docviewer.pot 2015-04-27 16:02:40 +0000
42+++ po/com.ubuntu.docviewer.pot 2015-04-30 18:50:23 +0000
43@@ -8,7 +8,7 @@
44 msgstr ""
45 "Project-Id-Version: \n"
46 "Report-Msgid-Bugs-To: \n"
47-"POT-Creation-Date: 2015-04-27 18:02+0200\n"
48+"POT-Creation-Date: 2015-04-30 19:09+0200\n"
49 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
50 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
51 "Language-Team: LANGUAGE <LL@li.org>\n"
52@@ -34,13 +34,13 @@
53
54 #: ../src/app/docviewer-application.cpp:164
55 #: ../src/app/qml/documentPage/DocumentPage.qml:25
56-#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
57+#: /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
58 msgid "Document Viewer"
59 msgstr ""
60
61 #: ../src/app/qml/common/DetailsPage.qml:27
62 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:97
63-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:83
64+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:104
65 msgid "Details"
66 msgstr ""
67
68@@ -77,7 +77,7 @@
69 #: ../src/app/qml/common/RejectedImportDialog.qml:38
70 #: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:32
71 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
72-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
73+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
74 msgid "Close"
75 msgstr ""
76
77@@ -274,7 +274,7 @@
78 msgstr ""
79
80 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
81-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
82+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:63
83 msgid "Back"
84 msgstr ""
85
86@@ -283,12 +283,12 @@
87 msgstr ""
88
89 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
90-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77
91+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
92 msgid "Disable night mode"
93 msgstr ""
94
95 #: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
96-#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77
97+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:92
98 msgid "Enable night mode"
99 msgstr ""
100
101@@ -305,20 +305,88 @@
102 msgid "GO!"
103 msgstr ""
104
105-#: ../src/app/qml/textView/TextView.qml:42
106-msgid "Loading..."
107-msgstr ""
108-
109-#: ../src/app/qml/ubuntu-docviewer-app.qml:183
110+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:86
111+msgid "Enable selection mode"
112+msgstr ""
113+
114+#: ../src/app/qml/textView/TextViewDefaultHeader.qml:98
115+msgid "Settings"
116+msgstr ""
117+
118+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:36
119+msgid "Select all"
120+msgstr ""
121+
122+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:42
123+msgid "Paste"
124+msgstr ""
125+
126+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:48
127+msgid "Cut"
128+msgstr ""
129+
130+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:54
131+msgid "Copy"
132+msgstr ""
133+
134+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:60
135+msgid "Redo"
136+msgstr ""
137+
138+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:67
139+msgid "Undo"
140+msgstr ""
141+
142+#: ../src/app/qml/textView/TextViewSelectionHeader.qml:75
143+msgid "Exit selection mode"
144+msgstr ""
145+
146+#: ../src/app/qml/textView/TextViewSettingsPage.qml:24
147+msgid "Text settings"
148+msgstr ""
149+
150+#: ../src/app/qml/textView/TextViewSettingsPage.qml:45
151+msgid "Show line numbers"
152+msgstr ""
153+
154+#: ../src/app/qml/textView/TextViewSettingsPage.qml:61
155+msgid "Enable text wrapping"
156+msgstr ""
157+
158+#: ../src/app/qml/textView/TextViewSettingsPage.qml:77
159+msgid "Highlight current line"
160+msgstr ""
161+
162+#: ../src/app/qml/textView/TextViewSettingsPage.qml:88
163+msgid "Right margin settings"
164+msgstr ""
165+
166+#: ../src/app/qml/textView/TextViewSettingsPage.qml:95
167+msgid "Show right margin"
168+msgstr ""
169+
170+#: ../src/app/qml/textView/TextViewSettingsPage.qml:111
171+msgid "Show at column (default = 80):"
172+msgstr ""
173+
174+#: ../src/app/qml/ubuntu-docviewer-app.qml:185
175 msgid "Document successfully imported!"
176 msgid_plural "Documents successfully imported!"
177 msgstr[0] ""
178 msgstr[1] ""
179
180-#: ../src/app/qml/ubuntu-docviewer-app.qml:186
181+#: ../src/app/qml/ubuntu-docviewer-app.qml:188
182 msgid "Open"
183 msgstr ""
184
185-#: /home/stefano/tmp/build-ch-imported-documents-name-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
186+#: ../src/plugin/text-qml-plugin/filereader.cpp:90
187+msgid "Could not open file for reading."
188+msgstr ""
189+
190+#: ../src/plugin/text-qml-plugin/filereader.cpp:107
191+msgid "No file specified."
192+msgstr ""
193+
194+#: /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
195 msgid "documents;viewer;pdf;reader;"
196 msgstr ""
197
198=== modified file 'src/app/qml/textView/TextView.qml'
199--- src/app/qml/textView/TextView.qml 2015-03-26 13:58:31 +0000
200+++ src/app/qml/textView/TextView.qml 2015-04-30 18:50:23 +0000
201@@ -16,7 +16,8 @@
202
203 import QtQuick 2.3
204 import Ubuntu.Components 1.1
205-import Ubuntu.Components.Themes.Ambiance 0.1
206+import DocumentViewer.Text 1.0
207+import Qt.labs.settings 1.0
208
209 import "../common/utils.js" as Utils
210
211@@ -24,50 +25,51 @@
212 id: textPage
213 title: Utils.getNameOfFile(file.path);
214
215+ property bool selectionModeEnabled: false
216+ property alias editor: editor
217+
218 // Reset night mode shader settings when closing the page
219 // Component.onDestruction: mainView.nightModeEnabled = false
220
221- TextArea {
222- id: textAreaMain
223- objectName: "textAreaMain"
224-
225- property bool isLoading: true
226-
227+ Settings {
228+ category: "TextViewer"
229+
230+ property alias displayLineNumbers: editor.displayLineNumbers
231+ property alias textWrap: editor.textWrap
232+ property alias rightMarginAtColumn: editor.rightMarginAtColumn
233+ property alias showRightMargin: editor.showRightMargin
234+ property alias highlightCurrentLine: editor.highlightCurrentLine
235+ }
236+
237+ TextReader {
238+ id: reader
239+ path: file.path
240+
241+ onErrorStringChanged: console.log(errorString)
242+ }
243+
244+ TextEditor {
245+ id: editor
246+ objectName: "editor"
247 anchors.fill: parent
248
249- // FIXME: If set to true, some of the keyboard hooks are disabled
250- // And it's not possible to move the cursor with arrow keys.
251- readOnly: true
252-
253- text: i18n.tr("Loading...")
254- font.family: "UbuntuMono"
255-
256- Component.onCompleted: {
257- var xhr = new XMLHttpRequest;
258-
259- xhr.open("GET", file.path);
260- xhr.onreadystatechange = function() {
261- if (xhr.readyState === XMLHttpRequest.DONE) {
262- textAreaMain.text = xhr.responseText;
263- textAreaMain.isLoading = false
264- }
265- };
266-
267- xhr.send();
268- }
269-
270- style: TextAreaStyle {
271- background: Rectangle { color: "white" }
272- }
273+ selectionMode: textPage.selectionModeEnabled
274+ text: reader.fileString
275 }
276
277 // *** HEADER ***
278- state: "default"
279 states: [
280 TextViewDefaultHeader {
281- name: "default"
282- targetPage: textPage
283- activityRunning: textAreaMain.isLoading
284+ when: !textPage.selectionModeEnabled
285+
286+ targetPage: textPage
287+ activityRunning: reader.status == TextReader.Loading
288+ },
289+
290+ TextViewSelectionHeader {
291+ when: textPage.selectionModeEnabled
292+
293+ targetPage: textPage
294 }
295 ]
296 }
297
298=== modified file 'src/app/qml/textView/TextViewDefaultHeader.qml'
299--- src/app/qml/textView/TextViewDefaultHeader.qml 2015-03-26 13:58:31 +0000
300+++ src/app/qml/textView/TextViewDefaultHeader.qml 2015-04-30 18:50:23 +0000
301@@ -31,6 +31,8 @@
302 anchors.fill: parent
303 spacing: units.gu(1)
304
305+ visible: !targetPage.selectionModeEnabled
306+
307 ActivityIndicator { id: activity }
308
309 Column {
310@@ -73,12 +75,31 @@
311
312 actions: [
313 Action {
314+ iconName: "search"
315+ // onTriggered: target.state = "search"
316+ //Disable it until we provide search in Text plugin.
317+ enabled: false
318+ },
319+
320+ Action {
321+ iconName: "edit-select-all"
322+ text: i18n.tr("Enable selection mode")
323+ onTriggered: targetPage.selectionModeEnabled = true
324+ },
325+
326+ Action {
327 iconName: "night-mode"
328 text: mainView.nightModeEnabled ? i18n.tr("Disable night mode") : i18n.tr("Enable night mode")
329 onTriggered: mainView.nightModeEnabled = !mainView.nightModeEnabled
330 },
331
332 Action {
333+ iconName: "settings"
334+ text: i18n.tr("Settings")
335+ onTriggered: pageStack.push(Qt.resolvedUrl("TextViewSettingsPage.qml"), { textPage: targetPage })
336+ },
337+
338+ Action {
339 objectName: "detailsAction"
340 text: i18n.tr("Details")
341 iconName: "info"
342
343=== added file 'src/app/qml/textView/TextViewSelectionHeader.qml'
344--- src/app/qml/textView/TextViewSelectionHeader.qml 1970-01-01 00:00:00 +0000
345+++ src/app/qml/textView/TextViewSelectionHeader.qml 2015-04-30 18:50:23 +0000
346@@ -0,0 +1,81 @@
347+/*
348+ * Copyright (C) 2014-2015 Canonical, Ltd.
349+ *
350+ * This program is free software; you can redistribute it and/or modify
351+ * it under the terms of the GNU General Public License as published by
352+ * the Free Software Foundation; version 3.
353+ *
354+ * This program is distributed in the hope that it will be useful,
355+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
356+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
357+ * GNU General Public License for more details.
358+ *
359+ * You should have received a copy of the GNU General Public License
360+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
361+ */
362+
363+import QtQuick 2.3
364+import Ubuntu.Components 1.1
365+import "../upstreamComponents"
366+
367+PageHeadState {
368+ id: rootItem
369+
370+ property Page targetPage
371+ head: targetPage.head
372+
373+ contents: Row {
374+ width: parent ? parent.width : 0
375+ height: parent ? parent.height : 0
376+
377+ layoutDirection: Qt.RightToLeft
378+ visible: targetPage.selectionModeEnabled
379+
380+ HeaderButton {
381+ iconName: "edit-select-all"
382+ text: i18n.tr("Select all")
383+ onTriggered: targetPage.editor.selectAll()
384+ }
385+
386+ HeaderButton {
387+ iconName: "edit-paste"
388+ text: i18n.tr("Paste")
389+ onTriggered: targetPage.editor.paste()
390+ }
391+
392+ HeaderButton {
393+ iconName: "edit-cut"
394+ text: i18n.tr("Cut")
395+ onTriggered: targetPage.editor.cut()
396+ }
397+
398+ HeaderButton {
399+ iconName: "edit-copy"
400+ text: i18n.tr("Copy")
401+ onTriggered: targetPage.editor.copy()
402+ }
403+
404+ HeaderButton {
405+ iconName: "edit-redo"
406+ text: i18n.tr("Redo")
407+ onTriggered: targetPage.editor.redo()
408+ enabled: targetPage.editor.canRedo
409+ }
410+
411+ HeaderButton {
412+ iconName: "edit-undo"
413+ text: i18n.tr("Undo")
414+ onTriggered: targetPage.editor.undo()
415+ enabled: targetPage.editor.canUndo
416+ }
417+ }
418+
419+ backAction: Action {
420+ iconName: "back"
421+ text: i18n.tr("Exit selection mode")
422+ onTriggered: {
423+ targetPage.editor.deselect()
424+ targetPage.selectionModeEnabled = false;
425+ }
426+ }
427+}
428
429=== added file 'src/app/qml/textView/TextViewSettingsPage.qml'
430--- src/app/qml/textView/TextViewSettingsPage.qml 1970-01-01 00:00:00 +0000
431+++ src/app/qml/textView/TextViewSettingsPage.qml 2015-04-30 18:50:23 +0000
432@@ -0,0 +1,127 @@
433+/*
434+ * Copyright (C) 2015 Canonical, Ltd.
435+ *
436+ * This program is free software; you can redistribute it and/or modify
437+ * it under the terms of the GNU General Public License as published by
438+ * the Free Software Foundation; version 3.
439+ *
440+ * This program is distributed in the hope that it will be useful,
441+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
442+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
443+ * GNU General Public License for more details.
444+ *
445+ * You should have received a copy of the GNU General Public License
446+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
447+ */
448+
449+import QtQuick 2.3
450+import Ubuntu.Components 1.1
451+import QtQuick.Layouts 1.1
452+import Ubuntu.Components.ListItems 1.0 as ListItem
453+
454+Page {
455+ id: textSettingsPage
456+ title: i18n.tr("Text settings")
457+
458+ property var textPage
459+
460+ Component.onCompleted: {
461+ // Get settings
462+ lineNumbersCheckBox.checked = textPage.editor.displayLineNumbers
463+ textWrapCheckbox.checked = textPage.editor.textWrap
464+ highlightCurrentLineCheckbox.checked = textPage.editor.highlightCurrentLine
465+ rightMarginCheckbox.checked = textPage.editor.showRightMargin
466+ rightMarginTextField.text = textPage.editor.rightMarginAtColumn
467+ }
468+
469+ Column {
470+ anchors.fill: parent
471+
472+ ListItem.Base {
473+ RowLayout {
474+ anchors.fill: parent
475+
476+ Label {
477+ text: i18n.tr("Show line numbers")
478+ Layout.fillWidth: true
479+ }
480+
481+ CheckBox {
482+ id: lineNumbersCheckBox
483+ onCheckedChanged: textPage.editor.displayLineNumbers = checked;
484+ }
485+ }
486+ }
487+
488+ ListItem.Base {
489+ RowLayout {
490+ anchors.fill: parent
491+
492+ Label {
493+ text: i18n.tr("Enable text wrapping")
494+ Layout.fillWidth: true
495+ }
496+
497+ CheckBox {
498+ id: textWrapCheckbox
499+ onCheckedChanged: textPage.editor.textWrap = checked
500+ }
501+ }
502+ }
503+
504+ ListItem.Base {
505+ RowLayout {
506+ anchors.fill: parent
507+
508+ Label {
509+ text: i18n.tr("Highlight current line")
510+ Layout.fillWidth: true
511+ }
512+
513+ CheckBox {
514+ id: highlightCurrentLineCheckbox
515+ onCheckedChanged: textPage.editor.highlightCurrentLine = checked
516+ }
517+ }
518+ }
519+
520+ ListItem.Header { text: i18n.tr("Right margin settings") }
521+
522+ ListItem.Base {
523+ RowLayout {
524+ anchors.fill: parent
525+
526+ Label {
527+ text: i18n.tr("Show right margin")
528+ Layout.fillWidth: true
529+ }
530+
531+ CheckBox {
532+ id: rightMarginCheckbox
533+ onCheckedChanged: textPage.editor.showRightMargin = checked
534+ }
535+ }
536+ }
537+
538+ ListItem.Base {
539+ RowLayout {
540+ anchors.fill: parent
541+
542+ Label {
543+ text: i18n.tr("Show at column (default = 80):")
544+
545+ Layout.fillWidth: true
546+ }
547+
548+ TextField {
549+ id: rightMarginTextField
550+ implicitWidth: units.gu(12)
551+ inputMethodHints: Qt.ImhDigitsOnly
552+ validator: IntValidator { bottom: 1; top: 150 }
553+
554+ onTextChanged: textPage.editor.rightMarginAtColumn = parseInt(text)
555+ }
556+ }
557+ }
558+ }
559+}
560
561=== modified file 'src/app/qml/ubuntu-docviewer-app.qml'
562--- src/app/qml/ubuntu-docviewer-app.qml 2015-04-14 14:16:16 +0000
563+++ src/app/qml/ubuntu-docviewer-app.qml 2015-04-30 18:50:23 +0000
564@@ -34,7 +34,9 @@
565
566 applicationName: "com.ubuntu.docviewer"
567 useDeprecatedToolbar: false
568+
569 automaticOrientation: true
570+ anchorToKeyboard: true
571
572 width: units.gu(50)
573 height: units.gu(75)
574
575=== modified file 'src/plugin/CMakeLists.txt'
576--- src/plugin/CMakeLists.txt 2014-10-20 21:38:36 +0000
577+++ src/plugin/CMakeLists.txt 2015-04-30 18:50:23 +0000
578@@ -1,2 +1,3 @@
579 add_subdirectory(file-qml-plugin)
580 add_subdirectory(poppler-qml-plugin)
581+add_subdirectory(text-qml-plugin)
582
583=== added directory 'src/plugin/text-qml-plugin'
584=== added file 'src/plugin/text-qml-plugin/CMakeLists.txt'
585--- src/plugin/text-qml-plugin/CMakeLists.txt 1970-01-01 00:00:00 +0000
586+++ src/plugin/text-qml-plugin/CMakeLists.txt 2015-04-30 18:50:23 +0000
587@@ -0,0 +1,38 @@
588+set(PLUGIN_DIR DocumentViewer/Text)
589+include_directories(
590+ ${CMAKE_CURRENT_SOURCE_DIR}
591+ ${CMAKE_CURRENT_BINARY_DIR}
592+)
593+
594+#add QML resources
595+file(GLOB_RECURSE QML_SRCS
596+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
597+ *.qml *.js qmldir
598+)
599+
600+#add the sources to compile
601+set(textqmlplugin_SRCS
602+ backend.cpp
603+ documenthandler.cpp
604+ filereader.cpp
605+ ${QML_SRCS}
606+)
607+
608+add_library(textqmlplugin MODULE ${textqmlplugin_SRCS})
609+qt5_use_modules(textqmlplugin Qml Quick)
610+
611+# Copy the plugin, the qmldir file and other assets to the build dir for running in QtCreator
612+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
613+ add_custom_command(TARGET textqmlplugin POST_BUILD
614+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
615+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
616+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/LineNumberColumn.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
617+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditor.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
618+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TextEditorStyle.qml ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
619+ COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:textqmlplugin> ${CMAKE_CURRENT_BINARY_DIR}/../${PLUGIN_DIR}
620+ )
621+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
622+
623+# Install plugin file
624+install(TARGETS textqmlplugin DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})
625+install(FILES ${QML_SRCS} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN_DIR})
626
627=== added file 'src/plugin/text-qml-plugin/LineNumberColumn.qml'
628--- src/plugin/text-qml-plugin/LineNumberColumn.qml 1970-01-01 00:00:00 +0000
629+++ src/plugin/text-qml-plugin/LineNumberColumn.qml 2015-04-30 18:50:23 +0000
630@@ -0,0 +1,66 @@
631+/*
632+ * Copyright (C) 2015 Canonical, Ltd.
633+ *
634+ * This program is free software; you can redistribute it and/or modify
635+ * it under the terms of the GNU General Public License as published by
636+ * the Free Software Foundation; version 3.
637+ *
638+ * This program is distributed in the hope that it will be useful,
639+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
640+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
641+ * GNU General Public License for more details.
642+ *
643+ * You should have received a copy of the GNU General Public License
644+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
645+ */
646+
647+import QtQuick 2.3
648+import Ubuntu.Components 1.1
649+
650+Rectangle {
651+ id: lineColumn
652+
653+ width: visible ? units.gu(3.5) + lineNumberRepeater.itemAt(lineNumberRepeater.count - 1).paintedWidth : 0
654+
655+ property int topMargin
656+ property alias model: lineNumberRepeater.model
657+ property var font
658+
659+ property alias dividerColor: divider.color
660+ property alias dividerWidth: divider.width
661+ property color fontColor
662+
663+ Rectangle {
664+ id: divider
665+ height: parent.height
666+ anchors.right: parent.right
667+ color: lineColumn.dividerColor
668+ }
669+
670+ Column {
671+ id: layout
672+ anchors {
673+ top: parent.top
674+ left: parent.left
675+ right: parent.right
676+ topMargin: lineColumn.topMargin
677+ rightMargin: units.gu(2)
678+ leftMargin: units.gu(1.5)
679+ }
680+
681+ Repeater {
682+ id: lineNumberRepeater
683+
684+ delegate: Text {
685+ width: layout.width
686+ height: modelData.lineHeight
687+ text: modelData.lineNumber + 1
688+
689+ horizontalAlignment: Text.AlignRight
690+
691+ font: lineColumn.font
692+ color: lineColumn.fontColor
693+ }
694+ }
695+ }
696+}
697
698=== added file 'src/plugin/text-qml-plugin/TextEditor.qml'
699--- src/plugin/text-qml-plugin/TextEditor.qml 1970-01-01 00:00:00 +0000
700+++ src/plugin/text-qml-plugin/TextEditor.qml 2015-04-30 18:50:23 +0000
701@@ -0,0 +1,332 @@
702+/*
703+ * Copyright (C) 2015 Canonical, Ltd.
704+ *
705+ * This program is free software; you can redistribute it and/or modify
706+ * it under the terms of the GNU General Public License as published by
707+ * the Free Software Foundation; version 3.
708+ *
709+ * This program is distributed in the hope that it will be useful,
710+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
711+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
712+ * GNU General Public License for more details.
713+ *
714+ * You should have received a copy of the GNU General Public License
715+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
716+ */
717+
718+import QtQuick 2.3
719+import Ubuntu.Components 1.1
720+import DocumentViewer.Text 1.0
721+
722+StyledItem {
723+ id: rootItem
724+
725+ property alias text: textArea.text
726+ property alias readOnly: textArea.readOnly
727+
728+ property alias mouseSelectionMode: textArea.mouseSelectionMode
729+ property alias selectionMode: textArea.selectByMouse
730+
731+ property bool displayLineNumbers: true
732+ property bool textWrap: false
733+ property int rightMarginAtColumn: 80
734+ property bool showRightMargin: true
735+ property bool highlightCurrentLine: false
736+
737+ property alias canPaste: textArea.canPaste
738+ property alias canRedo: textArea.canRedo
739+ property alias canUndo: textArea.canUndo
740+
741+ // functions
742+ /*!
743+ Copies the currently selected text to the system clipboard.
744+ */
745+ function copy()
746+ {
747+ textArea.copy();
748+ }
749+
750+ /*!
751+ Moves the currently selected text to the system clipboard.
752+ */
753+ function cut()
754+ {
755+ textArea.cut();
756+ }
757+
758+ /*!
759+ Removes active text selection.
760+ */
761+ function deselect()
762+ {
763+ textArea.deselect();
764+ }
765+
766+ /*!
767+ Inserts text into the TextArea at position.
768+ */
769+ function insert(position, text)
770+ {
771+ textArea.insert(position, text);
772+ }
773+
774+ /*!
775+ Returns the text position closest to pixel position (x, y).
776+
777+ Position 0 is before the first character, position 1 is after the first
778+ character but before the second, and so on until position text.length,
779+ which is after all characters.
780+ */
781+ function positionAt(x, y)
782+ {
783+ return textArea.positionAt(x, y);
784+ }
785+
786+ /*!
787+ Returns true if the natural reading direction of the textArea text found
788+ between positions start and end is right to left.
789+ */
790+ function isRightToLeft(start, end)
791+ {
792+ return textArea.isRightToLeft(start, end)
793+ }
794+
795+ /*!
796+ Moves the cursor to position and updates the selection according to the
797+ optional mode parameter. (To only move the cursor, set the cursorPosition
798+ property.)
799+
800+ When this method is called it additionally sets either the selectionStart
801+ or the selectionEnd (whichever was at the previous cursor position) to the
802+ specified position. This allows you to easily extend and contract the selected
803+ text range.
804+
805+ The selection mode specifies whether the selection is updated on a per character
806+ or a per word basis. If not specified the selection mode will default to whatever
807+ is given in the mouseSelectionMode property.
808+ */
809+ function moveCursorSelection(position, mode)
810+ {
811+ if (mode === undefined)
812+ textArea.moveCursorSelection(position, mouseSelectionMode);
813+ else
814+ textArea.moveCursorSelection(position, mode);
815+ }
816+
817+ /*!
818+ Places the clipboard or the data given as parameter into the text input.
819+ The selected text will be replaces with the data.
820+ */
821+ function paste(data) {
822+ if ((data !== undefined) && (typeof data === "string") && !textArea.readOnly) {
823+ var selTxt = textArea.selectedText;
824+ var txt = textArea.text;
825+ var pos = (selTxt !== "") ? txt.indexOf(selTxt) : textArea.cursorPosition
826+ if (selTxt !== "") {
827+ textArea.text = txt.substring(0, pos) + data + txt.substr(pos + selTxt.length);
828+ } else {
829+ textArea.text = txt.substring(0, pos) + data + txt.substr(pos);
830+ }
831+ textArea.cursorPosition = pos + data.length;
832+ } else
833+ textArea.paste();
834+ }
835+
836+ /*!
837+ Returns the rectangle at the given position in the text. The x, y, and height
838+ properties correspond to the cursor that would describe that position.
839+ */
840+ function positionToRectangle(position)
841+ {
842+ return textArea.positionToRectangle(position);
843+ }
844+
845+ /*!
846+ Redoes the last operation if redo is \l{canRedo}{available}.
847+ */
848+ function redo()
849+ {
850+ textArea.redo();
851+ }
852+
853+ /*!
854+ Causes the text from start to end to be selected.
855+
856+ If either start or end is out of range, the selection is not changed.
857+
858+ After calling this, selectionStart will become the lesser and selectionEnd
859+ will become the greater (regardless of the order passed to this method).
860+
861+ See also selectionStart and selectionEnd.
862+ */
863+ function select(start, end)
864+ {
865+ textArea.select(start, end);
866+ }
867+
868+ /*!
869+ Causes all text to be selected.
870+ */
871+ function selectAll()
872+ {
873+ textArea.selectAll();
874+ }
875+
876+ /*!
877+ Causes the word closest to the current cursor position to be selected.
878+ */
879+ function selectWord()
880+ {
881+ textArea.selectWord();
882+ }
883+
884+ /*!
885+ Returns the section of text that is between the start and end positions.
886+
887+ The returned text does not include any rich text formatting. A getText(0, length)
888+ will result in the same value as displayText.
889+ */
890+ function getText(start, end)
891+ {
892+ return textArea.getText(start, end);
893+ }
894+
895+ /*!
896+ Removes the section of text that is between the start and end positions
897+ from the TextEditor.
898+ */
899+ function remove(start, end)
900+ {
901+ return textArea.remove(start, end);
902+ }
903+
904+ /*!
905+ Undoes the last operation if undo is \l{canUndo}{available}. Deselects
906+ any current selection, and updates the selection start to the current
907+ cursor position.
908+ */
909+ function undo()
910+ {
911+ textArea.undo();
912+ }
913+
914+ clip: true
915+ style: Qt.createComponent("TextEditorStyle.qml", rootItem)
916+
917+ LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
918+ LayoutMirroring.childrenInherit: true
919+
920+ Rectangle {
921+ color: rootItem.__styleInstance.backgroundColor
922+ anchors.fill: parent
923+
924+ Rectangle {
925+ visible: rootItem.showRightMargin && (rootItem.rightMarginAtColumn > 0)
926+ color: rootItem.__styleInstance.secondaryBackgroundColor
927+
928+ anchors {
929+ fill: parent
930+ leftMargin: dummyFont.paintedWidth * rootItem.rightMarginAtColumn + lineColumn.width + flicker.margin - flicker.contentX
931+ }
932+
933+ Rectangle {
934+ height: parent.height
935+ anchors.left: parent.left
936+ width: rootItem.__styleInstance.dividerWidth
937+ color: rootItem.__styleInstance.dividerColor
938+ }
939+ }
940+
941+ Rectangle {
942+ width: parent.width
943+ height: textArea.cursorRectangle.height
944+
945+ y: textArea.cursorRectangle.y + flicker.margin - flicker.contentY
946+ color: rootItem.__styleInstance.currentLineHighlightColor
947+ opacity: 0.3
948+
949+ visible: rootItem.highlightCurrentLine && textArea.cursorVisible
950+ }
951+ }
952+
953+ Flickable {
954+ id: flicker
955+ anchors.fill: parent
956+
957+ property int margin: rootItem.__styleInstance.frameSpacing
958+
959+ contentWidth: textArea.paintedWidth + lineColumn.width
960+ contentHeight: textArea.paintedHeight + flicker.margin * 2
961+
962+ boundsBehavior: Flickable.StopAtBounds
963+
964+ LineNumberColumn {
965+ id: lineColumn
966+
967+ color: rootItem.__styleInstance.secondaryBackgroundColor
968+ dividerColor: rootItem.__styleInstance.dividerColor
969+ fontColor: rootItem.__styleInstance.selectedTextColor
970+ dividerWidth: rootItem.__styleInstance.dividerWidth
971+
972+ visible: rootItem.displayLineNumbers
973+ model: documentHandler.lineBlocks
974+ font: textArea.font
975+
976+ height: Math.max(flicker.contentHeight, flicker.height)
977+ topMargin: flicker.margin
978+ }
979+
980+ TextEdit {
981+ id: textArea
982+
983+ focus: true
984+ width: rootItem.width - lineColumn.width
985+ height: Math.max(textArea.contentHeight, textArea.contentHeight)
986+ anchors {
987+ left: lineColumn.right
988+ top: parent.top
989+ margins: flicker.margin
990+ }
991+
992+ mouseSelectionMode: TextEdit.SelectWords
993+ selectByMouse: false
994+ activeFocusOnPress: true
995+
996+ color: rootItem.__styleInstance.color
997+ selectedTextColor: rootItem.__styleInstance.selectedTextColor
998+ selectionColor: rootItem.__styleInstance.selectionColor
999+
1000+ // Work as a code editor
1001+ textFormat: TextEdit.PlainText
1002+ font.family: "UbuntuMono"
1003+ font.pixelSize: FontUtils.sizeToPixels("medium")
1004+
1005+ wrapMode: rootItem.textWrap ? TextEdit.Wrap
1006+ : TextEdit.NoWrap
1007+ }
1008+ }
1009+
1010+ Label {
1011+ id: dummyFont
1012+ text: "a"
1013+ font: textArea.font
1014+ visible: false
1015+ }
1016+
1017+ DocumentHandler {
1018+ id: documentHandler
1019+ textEditItem: textArea
1020+ }
1021+
1022+ Scrollbar {
1023+ id: rightScrollbar
1024+ flickableItem: flicker
1025+ }
1026+
1027+ Scrollbar {
1028+ id: bottomScrollbar
1029+ flickableItem: flicker
1030+ align: Qt.AlignBottom
1031+ }
1032+}
1033+
1034
1035=== added file 'src/plugin/text-qml-plugin/TextEditorStyle.qml'
1036--- src/plugin/text-qml-plugin/TextEditorStyle.qml 1970-01-01 00:00:00 +0000
1037+++ src/plugin/text-qml-plugin/TextEditorStyle.qml 2015-04-30 18:50:23 +0000
1038@@ -0,0 +1,35 @@
1039+/*
1040+ * Copyright (C) 2015 Canonical, Ltd.
1041+ *
1042+ * This program is free software; you can redistribute it and/or modify
1043+ * it under the terms of the GNU General Public License as published by
1044+ * the Free Software Foundation; version 3.
1045+ *
1046+ * This program is distributed in the hope that it will be useful,
1047+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1048+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1049+ * GNU General Public License for more details.
1050+ *
1051+ * You should have received a copy of the GNU General Public License
1052+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1053+ */
1054+
1055+import QtQuick 2.3
1056+import Ubuntu.Components 1.1
1057+
1058+Item {
1059+ id: visuals
1060+
1061+ property color backgroundColor: "white"
1062+ property color secondaryBackgroundColor: "#f2f2f2"
1063+ property color dividerColor: "#ddd"
1064+ property color currentLineHighlightColor: UbuntuColors.blue
1065+
1066+ property color color: styledItem.activeFocus ? Theme.palette.selected.fieldText : Theme.palette.normal.fieldText
1067+ property color selectedTextColor: Theme.palette.selected.foregroundText
1068+ property color selectionColor: Theme.palette.selected.foreground
1069+
1070+ property real frameSpacing: units.gu(1)
1071+ property real dividerWidth: units.gu(0.1)
1072+}
1073+
1074
1075=== added file 'src/plugin/text-qml-plugin/backend.cpp'
1076--- src/plugin/text-qml-plugin/backend.cpp 1970-01-01 00:00:00 +0000
1077+++ src/plugin/text-qml-plugin/backend.cpp 2015-04-30 18:50:23 +0000
1078@@ -0,0 +1,37 @@
1079+/*
1080+ * Copyright (C) 2013-2015 Canonical, Ltd.
1081+ *
1082+ * This program is free software: you can redistribute it and/or modify it
1083+ * under the terms of the GNU General Public License version 3, as published
1084+ * by the Free Software Foundation.
1085+ *
1086+ * This program is distributed in the hope that it will be useful, but
1087+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1088+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1089+ * PURPOSE. See the GNU General Public License for more details.
1090+ *
1091+ * You should have received a copy of the GNU General Public License along
1092+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1093+ *
1094+ */
1095+
1096+#include <QtQml>
1097+#include <QtQml/QQmlContext>
1098+
1099+#include "backend.h"
1100+#include "documenthandler.h"
1101+#include "filereader.h"
1102+
1103+void BackendPlugin::registerTypes(const char *uri)
1104+{
1105+ Q_ASSERT(uri == QLatin1String("DocumentViewer.Text"));
1106+
1107+ //@uri DocumentViewer.Text
1108+ qmlRegisterType<FileReader>(uri, 1, 0, "TextReader");
1109+ qmlRegisterType<DocumentHandler>(uri, 1, 0, "DocumentHandler");
1110+}
1111+
1112+void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1113+{
1114+ QQmlExtensionPlugin::initializeEngine(engine, uri);
1115+}
1116
1117=== added file 'src/plugin/text-qml-plugin/backend.h'
1118--- src/plugin/text-qml-plugin/backend.h 1970-01-01 00:00:00 +0000
1119+++ src/plugin/text-qml-plugin/backend.h 2015-04-30 18:50:23 +0000
1120@@ -0,0 +1,34 @@
1121+/*
1122+ * Copyright (C) 2013-2015 Canonical, Ltd.
1123+ *
1124+ * This program is free software: you can redistribute it and/or modify it
1125+ * under the terms of the GNU General Public License version 3, as published
1126+ * by the Free Software Foundation.
1127+ *
1128+ * This program is distributed in the hope that it will be useful, but
1129+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1130+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1131+ * PURPOSE. See the GNU General Public License for more details.
1132+ *
1133+ * You should have received a copy of the GNU General Public License along
1134+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1135+ *
1136+ */
1137+
1138+#ifndef BACKEND_PLUGIN_H
1139+#define BACKEND_PLUGIN_H
1140+
1141+#include <QtQml/QQmlEngine>
1142+#include <QtQml/QQmlExtensionPlugin>
1143+
1144+class BackendPlugin : public QQmlExtensionPlugin
1145+{
1146+ Q_OBJECT
1147+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1148+
1149+public:
1150+ void registerTypes(const char *uri);
1151+ void initializeEngine(QQmlEngine *engine, const char *uri);
1152+};
1153+#endif // BACKEND_PLUGIN_H
1154+
1155
1156=== added file 'src/plugin/text-qml-plugin/documenthandler.cpp'
1157--- src/plugin/text-qml-plugin/documenthandler.cpp 1970-01-01 00:00:00 +0000
1158+++ src/plugin/text-qml-plugin/documenthandler.cpp 2015-04-30 18:50:23 +0000
1159@@ -0,0 +1,108 @@
1160+/****************************************************************************
1161+**
1162+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
1163+** Contact: http://www.qt-project.org/legal
1164+**
1165+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
1166+**
1167+** $QT_BEGIN_LICENSE:BSD$
1168+** You may use this file under the terms of the BSD license as follows:
1169+**
1170+** "Redistribution and use in source and binary forms, with or without
1171+** modification, are permitted provided that the following conditions are
1172+** met:
1173+** * Redistributions of source code must retain the above copyright
1174+** notice, this list of conditions and the following disclaimer.
1175+** * Redistributions in binary form must reproduce the above copyright
1176+** notice, this list of conditions and the following disclaimer in
1177+** the documentation and/or other materials provided with the
1178+** distribution.
1179+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
1180+** of its contributors may be used to endorse or promote products derived
1181+** from this software without specific prior written permission.
1182+**
1183+**
1184+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1185+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1186+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1187+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1188+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1189+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1190+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1191+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1192+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1193+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1194+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1195+**
1196+** $QT_END_LICENSE$
1197+**
1198+****************************************************************************/
1199+
1200+#include "documenthandler.h"
1201+
1202+#include <QtGui/QTextDocument>
1203+#include <QtGui/QTextBlock>
1204+#include <QtGui/QTextLayout>
1205+
1206+DocumentHandler::DocumentHandler() :
1207+ m_textEditItem(nullptr),
1208+ m_doc(nullptr)
1209+{
1210+ //
1211+}
1212+
1213+void DocumentHandler::setTextEditItem(QQuickItem *textEditItem)
1214+{
1215+ if (textEditItem == m_textEditItem)
1216+ return;
1217+
1218+ QString className(textEditItem->metaObject()->className());
1219+ if (className != "QQuickTextEdit") {
1220+ qWarning() << "Item passed as argument is not a QQuickTextEdit. Aborting...";
1221+ return;
1222+ }
1223+
1224+ m_doc = nullptr;
1225+ m_textEditItem = textEditItem;
1226+
1227+ QVariant doc = m_textEditItem->property("textDocument");
1228+ if (doc.canConvert<QQuickTextDocument*>()) {
1229+ QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument*>();
1230+
1231+ if (qqdoc) {
1232+ m_doc = qqdoc->textDocument();
1233+
1234+ // TODO: We can also use lengthChanged() from TextEdit?
1235+ connect(m_doc, SIGNAL(contentsChanged()), this, SLOT(updateLineBlocks()));
1236+ connect(m_textEditItem, SIGNAL(contentSizeChanged()), this, SLOT(updateLineBlocks()));
1237+ connect(m_textEditItem, SIGNAL(wrapModeChanged()), this, SLOT(updateLineBlocks()));
1238+ }
1239+ }
1240+
1241+ Q_EMIT textEditItemChanged();
1242+}
1243+
1244+void DocumentHandler::updateLineBlocks()
1245+{
1246+ m_lineBlocks.clear();
1247+
1248+ QTextBlock block = m_doc->firstBlock();
1249+ int blockNumber = block.blockNumber();
1250+ qreal blockHeight;
1251+
1252+ while (blockNumber < m_doc->blockCount()) {
1253+ blockHeight = block.layout()->boundingRect().height();
1254+
1255+ QVariantMap map;
1256+ map["lineNumber"] = blockNumber;
1257+ map["lineHeight"] = blockHeight;
1258+
1259+ m_lineBlocks.append(map);
1260+
1261+ block = block.next();
1262+ blockNumber++;
1263+ }
1264+
1265+ Q_EMIT lineBlocksChanged();
1266+}
1267+
1268
1269=== added file 'src/plugin/text-qml-plugin/documenthandler.h'
1270--- src/plugin/text-qml-plugin/documenthandler.h 1970-01-01 00:00:00 +0000
1271+++ src/plugin/text-qml-plugin/documenthandler.h 2015-04-30 18:50:23 +0000
1272@@ -0,0 +1,85 @@
1273+/****************************************************************************
1274+**
1275+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
1276+** Contact: http://www.qt-project.org/legal
1277+**
1278+** This file is part of the Qt Quick Controls module of the Qt Toolkit.
1279+**
1280+** $QT_BEGIN_LICENSE:BSD$
1281+** You may use this file under the terms of the BSD license as follows:
1282+**
1283+** "Redistribution and use in source and binary forms, with or without
1284+** modification, are permitted provided that the following conditions are
1285+** met:
1286+** * Redistributions of source code must retain the above copyright
1287+** notice, this list of conditions and the following disclaimer.
1288+** * Redistributions in binary form must reproduce the above copyright
1289+** notice, this list of conditions and the following disclaimer in
1290+** the documentation and/or other materials provided with the
1291+** distribution.
1292+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
1293+** of its contributors may be used to endorse or promote products derived
1294+** from this software without specific prior written permission.
1295+**
1296+**
1297+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1298+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1299+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1300+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1301+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1302+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1303+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1304+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1305+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1306+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1307+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1308+**
1309+** $QT_END_LICENSE$
1310+**
1311+****************************************************************************/
1312+
1313+#ifndef DOCUMENTHANDLER_H
1314+#define DOCUMENTHANDLER_H
1315+
1316+#include <QQuickTextDocument>
1317+
1318+#include <QtGui/QTextCharFormat>
1319+#include <QtCore/QTextCodec>
1320+
1321+#include <qqmlfile.h>
1322+
1323+QT_BEGIN_NAMESPACE
1324+class QTextDocument;
1325+QT_END_NAMESPACE
1326+
1327+class DocumentHandler : public QObject
1328+{
1329+ Q_OBJECT
1330+
1331+ Q_PROPERTY(QQuickItem *textEditItem READ textEditItem WRITE setTextEditItem NOTIFY textEditItemChanged)
1332+ Q_PROPERTY(QVariantList lineBlocks READ lineBlocks NOTIFY lineBlocksChanged)
1333+
1334+public:
1335+ DocumentHandler();
1336+
1337+ QQuickItem *textEditItem() { return m_textEditItem; }
1338+ void setTextEditItem(QQuickItem *textEditItem);
1339+
1340+ QVariantList lineBlocks() { return m_lineBlocks; }
1341+
1342+Q_SIGNALS:
1343+ void textEditItemChanged();
1344+ void lineBlocksChanged();
1345+ void highlighterChanged();
1346+
1347+private slots:
1348+ void updateLineBlocks();
1349+
1350+private:
1351+ QQuickItem *m_textEditItem;
1352+ QTextDocument *m_doc;
1353+
1354+ QVariantList m_lineBlocks;
1355+};
1356+
1357+#endif
1358
1359=== added file 'src/plugin/text-qml-plugin/filereader.cpp'
1360--- src/plugin/text-qml-plugin/filereader.cpp 1970-01-01 00:00:00 +0000
1361+++ src/plugin/text-qml-plugin/filereader.cpp 2015-04-30 18:50:23 +0000
1362@@ -0,0 +1,110 @@
1363+/*
1364+ * Copyright (C) 2013, 2015 Canonical, Ltd.
1365+ *
1366+ * This program is free software; you can redistribute it and/or modify
1367+ * it under the terms of the GNU General Public License as published by
1368+ * the Free Software Foundation; version 3.
1369+ *
1370+ * This program is distributed in the hope that it will be useful,
1371+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1372+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1373+ * GNU General Public License for more details.
1374+ *
1375+ * You should have received a copy of the GNU General Public License
1376+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1377+ */
1378+
1379+#include <QFile>
1380+#include <QObject>
1381+#include <QDebug>
1382+#include <QTextStream>
1383+
1384+#include "filereader.h"
1385+
1386+FileReader::FileReader(QObject *parent) :
1387+ QObject(parent),
1388+ m_path(""),
1389+ m_status(Status::Null)
1390+{
1391+ qRegisterMetaType<Status>("Status");
1392+}
1393+
1394+FileReader::~FileReader() {
1395+
1396+}
1397+
1398+void FileReader::setPath(QString path) {
1399+
1400+ if (path == m_path && path.isEmpty())
1401+ return;
1402+
1403+ m_path = path;
1404+ Q_EMIT pathChanged();
1405+
1406+ ReaderThread *p = new ReaderThread();
1407+ p->setFilePath(path);
1408+
1409+ connect(p, SIGNAL(fileRead(QString)), this, SLOT(setFileString(QString)));
1410+ connect(p, SIGNAL(errorChanged(QString)), this, SLOT(setErrorString(QString)));
1411+ connect(p, SIGNAL(statusChanged(Status)), this, SLOT(setStatus(Status)));
1412+ connect(p, SIGNAL(finished()), p, SLOT(deleteLater()));
1413+
1414+ p->start();
1415+}
1416+
1417+void FileReader::setFileString(QString string)
1418+{
1419+ if (m_fileString != string) {
1420+ m_fileString = string;
1421+ Q_EMIT fileStringChanged();
1422+ }
1423+}
1424+
1425+void FileReader::setStatus(Status status)
1426+{
1427+ if (m_status != status) {
1428+ m_status = status;
1429+ Q_EMIT statusChanged();
1430+ }
1431+}
1432+
1433+void FileReader::setErrorString(QString errorString)
1434+{
1435+ if (m_errorString != errorString) {
1436+ m_errorString = errorString;
1437+ Q_EMIT errorStringChanged();
1438+ }
1439+}
1440+
1441+void ReaderThread::run() {
1442+ if (!m_path.isEmpty()) {
1443+ Q_EMIT statusChanged(Status::Loading);
1444+
1445+ QFile file(m_path);
1446+
1447+ if (!file.open(QFile::ReadOnly | QFile::Text))
1448+ {
1449+ qWarning() << "Could not open file for reading.";
1450+
1451+ Q_EMIT statusChanged(Status::Error);
1452+ Q_EMIT errorChanged(tr("Could not open file for reading."));
1453+ Q_EMIT fileRead(QString(""));
1454+ }
1455+
1456+ QTextStream in(&file);
1457+ QString text = in.readAll();
1458+
1459+ file.flush();
1460+ file.close();
1461+
1462+ Q_EMIT statusChanged(Status::Ready);
1463+ Q_EMIT errorChanged(QString(""));
1464+ Q_EMIT fileRead(text);
1465+ } else {
1466+ qWarning() << "No file specified.";
1467+
1468+ Q_EMIT statusChanged(Status::Error);
1469+ Q_EMIT errorChanged(tr("No file specified."));
1470+ Q_EMIT fileRead(QString(""));
1471+ }
1472+}
1473
1474=== added file 'src/plugin/text-qml-plugin/filereader.h'
1475--- src/plugin/text-qml-plugin/filereader.h 1970-01-01 00:00:00 +0000
1476+++ src/plugin/text-qml-plugin/filereader.h 2015-04-30 18:50:23 +0000
1477@@ -0,0 +1,97 @@
1478+/*
1479+ * Copyright (C) 2013, 2015 Canonical, Ltd.
1480+ *
1481+ * This program is free software; you can redistribute it and/or modify
1482+ * it under the terms of the GNU General Public License as published by
1483+ * the Free Software Foundation; version 3.
1484+ *
1485+ * This program is distributed in the hope that it will be useful,
1486+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1487+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1488+ * GNU General Public License for more details.
1489+ *
1490+ * You should have received a copy of the GNU General Public License
1491+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1492+ */
1493+
1494+#ifndef FILEREADER_H
1495+#define FILEREADER_H
1496+
1497+#include <QObject>
1498+#include <QThread>
1499+
1500+class FileReader: public QObject
1501+{
1502+ Q_OBJECT
1503+ Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged)
1504+ Q_PROPERTY(QString fileString READ getfileString NOTIFY fileStringChanged)
1505+ Q_PROPERTY(Status status READ getStatus NOTIFY statusChanged)
1506+ Q_PROPERTY(QString errorString READ getErrorString NOTIFY errorStringChanged)
1507+ Q_ENUMS(Status)
1508+
1509+public:
1510+ explicit FileReader(QObject *parent = 0);
1511+ ~FileReader();
1512+
1513+ enum Status
1514+ {
1515+ Null,
1516+ Ready,
1517+ Loading,
1518+ Error
1519+ };
1520+
1521+ QString getPath() { return m_path; }
1522+ void setPath(QString path);
1523+
1524+ QString getErrorString() { return m_errorString; }
1525+
1526+ QString getfileString() { return m_fileString; }
1527+
1528+ Status getStatus() { return m_status; }
1529+
1530+Q_SIGNALS:
1531+ void pathChanged();
1532+ void fileStringChanged();
1533+ void statusChanged();
1534+ void errorStringChanged();
1535+
1536+private slots:
1537+ void setFileString(QString string);
1538+ void setStatus(Status status);
1539+ void setErrorString(QString errorString);
1540+
1541+private:
1542+ QString m_path;
1543+ QString m_fileString;
1544+ Status m_status;
1545+ QString m_errorString;
1546+};
1547+
1548+class ReaderThread : public QThread
1549+{
1550+ Q_OBJECT
1551+ Q_ENUMS(Status)
1552+
1553+public:
1554+ enum Status
1555+ {
1556+ Null,
1557+ Ready,
1558+ Loading,
1559+ Error
1560+ };
1561+
1562+ void run();
1563+ void setFilePath(QString path) { m_path = path; }
1564+
1565+Q_SIGNALS:
1566+ void errorChanged(QString error);
1567+ void fileRead(QString fileString);
1568+ void statusChanged(Status status);
1569+
1570+private:
1571+ QString m_path;
1572+};
1573+
1574+#endif // FILEREADER_H
1575
1576=== added file 'src/plugin/text-qml-plugin/qmldir'
1577--- src/plugin/text-qml-plugin/qmldir 1970-01-01 00:00:00 +0000
1578+++ src/plugin/text-qml-plugin/qmldir 2015-04-30 18:50:23 +0000
1579@@ -0,0 +1,7 @@
1580+module DocumentViewer.Text
1581+plugin textqmlplugin
1582+
1583+TextEditor 1.0 TextEditor.qml
1584+TextEditorStyle 1.0 TextEditorStyle.qml
1585+
1586+internal LineNumberColumn LineNumberColumn.qml
1587
1588=== modified file 'tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py'
1589--- tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py 2015-04-14 15:37:06 +0000
1590+++ tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py 2015-04-30 18:50:23 +0000
1591@@ -29,7 +29,7 @@
1592 self.launch_app()
1593
1594 text_area = self.app.main_view.select_single(
1595- "TextArea", objectName="textAreaMain")
1596+ "TextEditor", objectName="editor")
1597
1598 self.assertThat(
1599 text_area.text, Eventually(Equals('TEST\n')))

Subscribers

People subscribed via source and target branches