← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

Re: [Merge] lp:~verzegnassi-stefano/ubuntu-docviewer-app/document-hub2 into lp:ubuntu-docviewer-app

 

Review: Needs Fixing code

I read the first 5000 lines of diff, and I left some comments.

Later I'll finish the diff, and then I'll test the app. Maybe I'll do another code review because it's huge

Diff comments:

> === modified file 'CMakeLists.txt'
> --- CMakeLists.txt	2015-01-29 16:35:28 +0000
> +++ CMakeLists.txt	2015-02-27 15:45:56 +0000
> @@ -23,7 +23,7 @@
>  
>  set(APP_NAME ubuntu-docviewer-app)
>  set(DESKTOP_FILE "${PROJECT_NAME}.desktop")
> -#set(URLS_FILE "${PROJECT_NAME}_${APP_NAME}.url-dispatcher")
> +set(URLS_FILE "${PROJECT_NAME}.url-dispatcher")
>  set(LP_PROJECT ubuntu-docviewer-app)
>  set(ICON_FILE docviewer@xxxxxx)
>  set(AUTOPILOT_DIR ubuntu_docviewer_app)
> @@ -67,7 +67,7 @@
>      set(DESKTOP_DIR ${DATA_DIR})
>      set(URLS_DIR ${DATA_DIR})
>      configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
> -    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json docviewer.apparmor docviewer-content.json
> +    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json docviewer.apparmor docviewer-content.json ${URLS_FILE}
>              DESTINATION ${CMAKE_INSTALL_PREFIX})
>      # Make the click files visible in Qt Creator
>      file(GLOB CLICK_FILES
> 
> === modified file 'com.ubuntu.docviewer.desktop.in.in'
> --- com.ubuntu.docviewer.desktop.in.in	2014-09-24 05:06:10 +0000
> +++ com.ubuntu.docviewer.desktop.in.in	2015-02-27 15:45:56 +0000
> @@ -6,6 +6,8 @@
>  Icon=@ICON@
>  _Name=Document Viewer
>  _Keywords=documents;viewer;pdf;reader;
> -MimeType=text/plain;image/png;image/jpeg;image/svg+xml;application/pdf
> +MimeType=text/plain;application/pdf
>  X-Ubuntu-Touch=true
>  X-Ubuntu-StageHint=SideStage
> +X-Ubuntu-Splash-Show-Header=true
> +_X-Ubuntu-Splash-Title=Document Viewer
> 
> === added file 'com.ubuntu.docviewer.url-dispatcher'
> --- com.ubuntu.docviewer.url-dispatcher	1970-01-01 00:00:00 +0000
> +++ com.ubuntu.docviewer.url-dispatcher	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,5 @@
> +[
> +    {
> +        "protocol": "document"
> +    }
> +]
> 
> === modified file 'debian/changelog'
> --- debian/changelog	2015-02-04 19:30:22 +0000
> +++ debian/changelog	2015-02-27 15:45:56 +0000
> @@ -1,14 +1,24 @@
> +ubuntu-docviewer-app (0.3.0) utopic; urgency=medium
> +
> +  * Improved content-hub support
> +  * Added uri handler support
> +  * Use the Unity 8 splash screen
> +  * In-app browser for files stored in XDG Documents folder
> +  * Support for images has been dropped
> +
> + -- Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>  Mon, 23 Feb 2015 13:41:47 +0100
> +
>  ubuntu-docviewer-app (0.2.1) utopic; urgency=medium
>  
>    * Added Table of Content in the PDF viewer
>  
> - -- Stefano <verzegnassi.stefano@xxxxxxxxx>  Wed, 04 Feb 2015 20:26:58 +0100
> + -- Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>  Wed, 04 Feb 2015 20:26:58 +0100
>  
>  ubuntu-docviewer-app (0.2.0) utopic; urgency=medium
>  
>    * Enabled zoom in the PDF viewer
>  
> - -- Stefano <verzegnassi.stefano@xxxxxxxxx>  Wed, 04 Feb 2015 14:37:54 +0100
> + -- Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>  Wed, 04 Feb 2015 14:37:54 +0100
>  
>  ubuntu-docviewer-app (0.1.2) UNRELEASED; urgency=medium
>  
> 
> === modified file 'debian/control'
> --- debian/control	2015-02-04 16:08:33 +0000
> +++ debian/control	2015-02-27 15:45:56 +0000
> @@ -4,6 +4,7 @@
>  Build-Depends: cmake,
>                 debhelper (>= 9),
>                 intltool,
> +               libcontent-hub-dev (>= 0.0+13.10.20130930.1),
>                 libpoppler-qt5-dev,
>                 pep8,
>                 pkg-config,
> @@ -27,7 +28,6 @@
>  Architecture: any
>  Depends: qtdeclarative5-qtquick2-plugin,
>           qtdeclarative5-ubuntu-ui-toolkit-plugin,
> -         qtdeclarative5-ubuntu-content1,
>           ${misc:Depends}
>  Description: Document Viewer application
>   Core Document Viewer application
> 
> === modified file 'docviewer-content.json'
> --- docviewer-content.json	2014-10-31 16:25:17 +0000
> +++ docviewer-content.json	2015-02-27 15:45:56 +0000
> @@ -1,7 +1,8 @@
>  {
>      "destination": [
> -        "pictures",
> -        "documents",
> -        "unknown"
> +        "documents"
> +    ],
> +    "source": [
> +        "documents"
>      ]
>  }
> 
> === modified file 'docviewer.apparmor'
> --- docviewer.apparmor	2014-10-31 16:25:17 +0000
> +++ docviewer.apparmor	2015-02-27 15:45:56 +0000
> @@ -1,6 +1,14 @@
>  {
>      "policy_groups": [
> -        "content_exchange"
> +        "content_exchange_source",
> +        "content_exchange",
> +        "debug"
> +    ],
> +    "read_path": [
> +        "@{HOME}/Documents/"
> +    ],
> +    "write_path": [
> +        "@{HOME}/Documents/"
>      ],
>      "policy_version": 1.2
>  }
> 
> === modified file 'manifest.json.in'
> --- manifest.json.in	2015-02-04 19:30:22 +0000
> +++ manifest.json.in	2015-02-27 15:45:56 +0000
> @@ -9,10 +9,11 @@
>          "docviewer": {
>              "apparmor": "docviewer.apparmor",
>              "desktop": "com.ubuntu.docviewer.desktop",
> -        "content-hub": "docviewer-content.json"
> +            "content-hub": "docviewer-content.json",
> +            "urls": "@URLS_FILE@"
>          }
>      },
> -    "version": "0.2.1.@BZR_REVNO@",
> +    "version": "0.3.@BZR_REVNO@",
>      "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@xxxxxxxxxxxxxxxxxxx>",
>      "x-source": {
>          "vcs-bzr": "@BZR_SOURCE@",
> 
> === modified file 'po/com.ubuntu.docviewer.pot'
> --- po/com.ubuntu.docviewer.pot	2015-02-05 19:30:53 +0000
> +++ po/com.ubuntu.docviewer.pot	2015-02-27 15:45:56 +0000
> @@ -8,7 +8,7 @@
>  msgstr ""
>  "Project-Id-Version: \n"
>  "Report-Msgid-Bugs-To: \n"
> -"POT-Creation-Date: 2015-02-05 20:29+0100\n"
> +"POT-Creation-Date: 2015-02-27 16:44+0100\n"
>  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
>  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
>  "Language-Team: LANGUAGE <LL@xxxxxx>\n"
> @@ -17,136 +17,190 @@
>  "Content-Type: text/plain; charset=CHARSET\n"
>  "Content-Transfer-Encoding: 8bit\n"
>  
> -#: ../src/app/qml/ContentHubPicker.qml:23
> -msgid "Open with..."
> -msgstr ""
> -
> -#: ../src/app/qml/ContentHubPicker.qml:27
> -msgid "Documents"
> -msgstr ""
> -
> -#: ../src/app/qml/ContentHubPicker.qml:27
> -msgid "Pictures"
> -msgstr ""
> -
> -#: ../src/app/qml/ContentHubPicker.qml:27
> -msgid "Other"
> -msgstr ""
> -
> -#: ../src/app/qml/ContentHubPicker.qml:30
> -#: ../src/app/qml/ImageViewDefaultHeader.qml:53
> -#: ../src/app/qml/PdfViewDefaultHeader.qml:61
> -#: ../src/app/qml/TextViewDefaultHeader.qml:61
> -msgid "Back"
> -msgstr ""
> -
> -#: ../src/app/qml/DetailsPage.qml:27
> -#: ../src/app/qml/ImageViewDefaultHeader.qml:69
> -#: ../src/app/qml/PdfViewDefaultHeader.qml:91
> -#: ../src/app/qml/TextViewDefaultHeader.qml:77
> +#: ../src/app/qml/common/DetailsPage.qml:27
> +#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:91
> +#: ../src/app/qml/textView/TextViewDefaultHeader.qml:77
>  msgid "Details"
>  msgstr ""
>  
> -#: ../src/app/qml/DetailsPage.qml:33
> +#: ../src/app/qml/common/DetailsPage.qml:33
>  msgid "Location"
>  msgstr ""
>  
> -#: ../src/app/qml/DetailsPage.qml:37
> +#: ../src/app/qml/common/DetailsPage.qml:37
>  msgid "Size"
>  msgstr ""
>  
> -#: ../src/app/qml/DetailsPage.qml:42
> +#: ../src/app/qml/common/DetailsPage.qml:42
>  msgid "Created"
>  msgstr ""
>  
> -#: ../src/app/qml/DetailsPage.qml:47
> +#: ../src/app/qml/common/DetailsPage.qml:47
>  msgid "Last modified"
>  msgstr ""
>  
> -#: ../src/app/qml/DetailsPage.qml:54
> +#: ../src/app/qml/common/DetailsPage.qml:54
>  msgid "MIME type"
>  msgstr ""
>  
> -#: ../src/app/qml/ErrorDialog.qml:23
> +#: ../src/app/qml/common/ErrorDialog.qml:23
>  msgid "Error"
>  msgstr ""
>  
> -#: ../src/app/qml/ErrorDialog.qml:24
> +#: ../src/app/qml/common/ErrorDialog.qml:24
>  msgid "File does not exist"
>  msgstr ""
>  
> -#: ../src/app/qml/ErrorDialog.qml:27
> -#: ../src/app/qml/ImageViewDefaultHeader.qml:53
> -#: ../src/app/qml/PdfViewDefaultHeader.qml:61
> -#: ../src/app/qml/TextViewDefaultHeader.qml:61
> +#: ../src/app/qml/common/ErrorDialog.qml:27
> +#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:33
> +#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
> +#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
>  msgid "Close"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfContentsPage.qml:24 ../src/app/qml/PdfView.qml:33
> +#: ../src/app/qml/common/UnknownTypeDialog.qml:26
> +msgid "Unknown file type"
> +msgstr ""
> +
> +#: ../src/app/qml/common/UnknownTypeDialog.qml:27
> +msgid ""
> +"Sorry but we can't find a way to display this file. Do you want to open it "
> +"as a plain text?"
> +msgstr ""
> +
> +#: ../src/app/qml/common/UnknownTypeDialog.qml:29
> +msgid "Yes"
> +msgstr ""
> +
> +#: ../src/app/qml/common/UnknownTypeDialog.qml:38
> +msgid "No"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:27
> +msgid "Delete file"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:28
> +#, qt-format
> +msgid "Delete %1 files"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:29
> +msgid "Are you sure you want to permanently delete this file?"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:30
> +msgid "Are you sure you want to permanently delete these files?"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:33
> +#: ../src/app/qml/documentPage/DocumentPagePickModeHeader.qml:29
> +#: ../src/app/qml/pdfView/PdfViewGotoDialog.qml:52
> +msgid "Cancel"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DeleteFileDialog.qml:38
> +#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:83
> +msgid "Delete"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentEmptyState.qml:24
> +msgid "No document found"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentEmptyState.qml:28
> +msgid ""
> +"Connect your device to any computer and simply drag files to the Documents "
> +"folder."
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentGridDelegate.qml:33
> +msgid "Today, "
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentGridDelegate.qml:36
> +msgid "Yesterday, "
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentListView.qml:135
> +msgid "Today"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentListView.qml:138
> +msgid "Yesterday"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentListView.qml:141
> +msgid "Earlier this week"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentListView.qml:144
> +msgid "Earlier this month"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentListView.qml:147
> +msgid "Even more earlier..."
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPage.qml:25
> +#: /home/stefano/Progetti/doc-viewer/build-document-hub2-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
> +msgid "Document Viewer"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPageDefaultHeader.qml:30
> +#: ../src/app/qml/documentPage/DocumentPagePickModeHeader.qml:37
> +msgid "Switch to single column list"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPageDefaultHeader.qml:30
> +#: ../src/app/qml/documentPage/DocumentPagePickModeHeader.qml:37
> +msgid "Switch to grid"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPagePickModeHeader.qml:45
> +msgid "Pick"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:53
> +msgid "Select None"
> +msgstr ""
> +
> +#: ../src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml:55
> +msgid "Select All"
> +msgstr ""
> +
> +#: ../src/app/qml/pdfView/PdfContentsPage.qml:24
> +#: ../src/app/qml/pdfView/PdfView.qml:34
>  msgid "Contents"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfView.qml:31
> +#: ../src/app/qml/pdfView/PdfView.qml:32
>  #, qt-format
>  msgid "Page %1 of %2"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfViewGotoDialog.qml:25
> +#: ../src/app/qml/pdfView/PdfViewDefaultHeader.qml:61
> +#: ../src/app/qml/textView/TextViewDefaultHeader.qml:61
> +msgid "Back"
> +msgstr ""
> +
> +#: ../src/app/qml/pdfView/PdfViewGotoDialog.qml:25
>  msgid "Go to page"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfViewGotoDialog.qml:26
> +#: ../src/app/qml/pdfView/PdfViewGotoDialog.qml:26
>  #, qt-format
>  msgid "Choose a page between 1 and %1"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfViewGotoDialog.qml:44
> +#: ../src/app/qml/pdfView/PdfViewGotoDialog.qml:44
>  msgid "GO!"
>  msgstr ""
>  
> -#: ../src/app/qml/PdfViewGotoDialog.qml:52
> -msgid "Cancel"
> -msgstr ""
> -
> -#: ../src/app/qml/TextView.qml:39
> +#: ../src/app/qml/textView/TextView.qml:39
>  msgid "Loading..."
>  msgstr ""
>  
> -#: ../src/app/qml/UnknownTypeDialog.qml:26
> -msgid "Unknown file type"
> -msgstr ""
> -
> -#: ../src/app/qml/UnknownTypeDialog.qml:27
> -msgid ""
> -"Sorry but we can't find a way to display this file. Do you want to open it "
> -"as a plain text?"
> -msgstr ""
> -
> -#: ../src/app/qml/UnknownTypeDialog.qml:29
> -msgid "Yes"
> -msgstr ""
> -
> -#: ../src/app/qml/UnknownTypeDialog.qml:38
> -msgid "No"
> -msgstr ""
> -
> -#: ../src/app/qml/WelcomePage.qml:23
> -#: /home/stefano/Progetti/doc-viewer/build-ubuntu-docviewer-app-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
> -msgid "Document Viewer"
> -msgstr ""
> -
> -#: ../src/app/qml/WelcomePage.qml:27
> -msgid "No opened documents"
> -msgstr ""
> -
> -#: ../src/app/qml/WelcomePage.qml:28
> -msgid "Tap the + icon to open a document"
> -msgstr ""
> -
> -#: ../src/app/qml/WelcomePage.qml:37
> -msgid "Open a file..."
> -msgstr ""
> -
> -#: /home/stefano/Progetti/doc-viewer/build-ubuntu-docviewer-app-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
> +#: /home/stefano/Progetti/doc-viewer/build-document-hub2-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
>  msgid "documents;viewer;pdf;reader;"
>  msgstr ""
> 
> === modified file 'src/app/CMakeLists.txt'
> --- src/app/CMakeLists.txt	2014-10-21 13:23:11 +0000
> +++ src/app/CMakeLists.txt	2015-02-27 15:45:56 +0000
> @@ -1,13 +1,25 @@
>  file(GLOB_RECURSE QML_SRCS *.qml *.js)
> +file(GLOB_RECURSE IMAGE_FILES *.qml *.js)
> +
> +pkg_check_modules(CONTENTHUB REQUIRED libcontent-hub)
>  
>  set(docviewer_SRCS
>      main.cpp
> +    content-communicator.cpp
> +    command-line-parser.cpp
> +    docviewer-application.cpp
> +    urlhandler.cpp
> +    quick/documentmodel.cpp
>      ${QML_SRCS}
>  )
>  
>  add_executable(ubuntu-docviewer-app ${docviewer_SRCS})
>  
> -qt5_use_modules(ubuntu-docviewer-app Gui Qml Quick)
> +qt5_use_modules(ubuntu-docviewer-app Widgets Gui Qml Quick DBus)
> +
> +target_link_libraries( ubuntu-docviewer-app
> +    ${CONTENTHUB_LIBRARIES}
> +)
>  
>  if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
>  add_custom_target(docviewer-qmlfiles ALL
> @@ -16,7 +28,16 @@
>  )
>  endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
>  
> +if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
> +add_custom_target(docviewer-imagefiles ALL
> +    COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/graphics ${CMAKE_CURRENT_BINARY_DIR}
> +    DEPENDS ${IMAGE_FILES}
> +)
> +endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
> +
>  install(DIRECTORY qml DESTINATION ${DATA_DIR})
> +install(DIRECTORY graphics DESTINATION ${DATA_DIR})
> +
>  if(CLICK_MODE)
>    install(TARGETS ubuntu-docviewer-app DESTINATION ${BIN_DIR})
>  else()
> 
> === added file 'src/app/command-line-parser.cpp'
> --- src/app/command-line-parser.cpp	1970-01-01 00:00:00 +0000
> +++ src/app/command-line-parser.cpp	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,105 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * Authors:
> + *  Nicolas d'Offay <nicolas.doffay@xxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "command-line-parser.h"
> +
> +#include "urlhandler.h"
> +
> +#include <QDebug>
> +#include <QDir>
> +#include <QStandardPaths>
> +#include <QTextStream>
> +#include <QUrl>
> +
> +CommandLineParser::CommandLineParser()
> +    : m_pickMode(false),
> +      m_testability(false),
> +      m_documentFile("")
> +{
> +    m_urlHandler = new UrlHandler();
> +}
> +
> +/*!
> + * @brief CommandLineParser::processArguments parsers our input commandline args and sets attributes accordingly.
> + * @param QStringList of commandline args to parse and set attributes.
> + * @return false if invalid parameter is input or -h/--help is called.
> + */
> +bool CommandLineParser::processArguments(const QStringList& args)
> +{
> +    bool valid_args = true;
> +
> +    for (int i = 1; i < args.count(); ++i)
> +    {
> +        if (args[i] == "--help" || args[i] == "-h") {
> +            usage();
> +            return false;
> +        }
> +        else if (args[i] == "--pick-mode") {
> +            m_pickMode = true;
> +        }
> +        else if (args[i] == "--testability") {
> +            m_testability = true;
> +        }
> +        else {
> +            if (args[i].startsWith("--desktop_file_hint")) {
> +                // ignore this command line switch, hybris uses it to get application info
> +            }
> +            else if (!args.at(i).isEmpty()) {
> +                QFileInfo fi(args.at(i));
> +
> +                if (fi.exists()) {
> +                    m_documentFile = fi.absoluteFilePath();
> +                }
> +                else if (m_urlHandler->processUri(args.at(i))) {
> +                    m_documentFile = m_urlHandler->documentFile();
> +                }
> +            }
> +            else {
> +                valid_args = !invalidArg(args[i]);
> +            }
> +        }
> +    }
> +
> +    return valid_args;
> +}
> +
> +/*!
> + * @brief CommandLineParser::usage() prints out our form factors.
> + */
> +void CommandLineParser::usage()
> +{
> +    QTextStream out(stdout);
> +    out << "Usage: ubuntu-docviewer-app [options] [file_path]" << endl;
> +    out << "Options:" << endl;
> +    out << "  --pick-mode\t\tEnable mode to pick photos" << endl;
> +    out << "  file_path\t\tOpens ubuntu-docviewer-app displaying the selected file" << endl;
> +}
> +
> +/*!
> + * @brief CommandLineParser::invalidArg() if an invalid argument is contained in our QStringList.
> + * @return returns true.
> + */
> +bool CommandLineParser::invalidArg(QString arg)
> +{
> +    QTextStream(stderr) << "Invalid argument '" << arg << "'" << endl;
> +    usage();
> +
> +    return true;
> +}
> 
> === added file 'src/app/command-line-parser.h'
> --- src/app/command-line-parser.h	1970-01-01 00:00:00 +0000
> +++ src/app/command-line-parser.h	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * Authors:
> + *  Nicolas d'Offay <nicolas.doffay@xxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef COMMANDLINEPARSER_H
> +#define COMMANDLINEPARSER_H
> +
> +#include <QHash>
> +#include <QSize>
> +#include <QString>
> +
> +class UrlHandler;
> +
> +/*!
> + * @brief The CommandLineParser is used to parse our commandline inputs and set
> + * parameters accordingly.
> + */
> +class CommandLineParser
> +{
> +public:
> +    CommandLineParser();
> +
> +    bool processArguments(const QStringList& args);
> +
> +    bool pickModeEnabled() const { return m_pickMode; }
> +    bool testability() const { return m_testability; }
> +    const QString &documentFile() const { return m_documentFile; }
> +
> +private:
> +    bool invalidArg(QString arg);
> +    void usage();
> +
> +    UrlHandler *m_urlHandler;
> +
> +    bool m_pickMode;
> +    bool m_testability;
> +    QString m_documentFile;
> +};
> +
> +#endif // COMMANDLINEPARSER_H
> 
> === added file 'src/app/content-communicator.cpp'
> --- src/app/content-communicator.cpp	1970-01-01 00:00:00 +0000
> +++ src/app/content-communicator.cpp	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "content-communicator.h"
> +
> +#include <QApplication>
> +#include <QStandardPaths>
> +#include <QMimeDatabase>
> +#include <QDebug>
> +#include <QFileInfo>
> +
> +#include <com/ubuntu/content/hub.h>
> +#include <com/ubuntu/content/item.h>
> +#include <com/ubuntu/content/transfer.h>
> +
> +
> +using namespace com::ubuntu::content;
> +
> +/*!
> + * \brief ContentCommunicator::ContentCommunicator
> + * \param parent
> + */
> +ContentCommunicator::ContentCommunicator(QObject *parent)
> +    : ImportExportHandler(parent),
> +      m_transfer(nullptr)
> +{
> +}
> +
> +/*!
> + * \brief ContentCommunicator::registerWithHub Register the handlers provided
> + * by ContentCommunicator with the content hub
> + */
> +void ContentCommunicator::registerWithHub()
> +{
> +    Hub *hub = Hub::Client::instance();
> +    hub->register_import_export_handler(this);
> +}
> +
> +/*!
> + * \brief \reimp
> + */
> +void ContentCommunicator::handle_import(content::Transfer *transfer)
> +{
> +    // FIXME: If a file is imported from $HOME/Documents, a new copy of the file is created.
> +    //   Sadly, we can't check for the source of the file, since we just get the path
> +    //   of the cache copy.
> +    // FIXME: If there already a file called "filename.1.ext", the imported file won't be renamed as "filename.2.ext", but "filename.1.1.ext".
> +    //  (This issue is in gallery-app too.)
> +
> +    QVector<Item> transferedItems = transfer->collect();
> +    foreach (const Item &hubItem, transferedItems) {
> +        QFileInfo fi(hubItem.url().toLocalFile());
> +        QString filename = fi.fileName();
> +        QString dir;
> +        QMimeDatabase mdb;
> +        QMimeType mt = mdb.mimeTypeForFile(hubItem.url().toLocalFile());
> +        QString suffix = fi.completeSuffix();
> +        QString filenameWithoutSuffix = filename.left(filename.size() - suffix.size());

Please use QFileInfo::baseName()

http://doc.qt.io/qt-5/qfileinfo.html#baseName

> +        if(suffix.isEmpty()) {
> +            // If the filename doesn't have an extension add one from the
> +            // detected mimetype
> +            if(!mt.preferredSuffix().isEmpty()) {
> +                suffix = mt.preferredSuffix();
> +                filenameWithoutSuffix += ".";

This changes the name of the file without consinstency.

Suppose we already have in our directory a file named file.txt.

If we want to import a file named file.txt we will have file1.txt.
On the other hand, if we want to import a file named file, without suffix, but with txt mimetype, we will have file.1.txt

I suggest to always add the . to filenameWithoutSuffix, so do something like
QString filenameWithoutSuffix = fi.baseName() + '.';

This because we can have file with a date as name, and the dot separate the date from the suffix that comes from the number of the files with the same name

E.g.
20150302.txt becomes 20150302.1.txt

> +            }
> +        }
> +        dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + QDir::separator();
> +
> +        QString destination = QString("%1%2").arg(dir + filenameWithoutSuffix, suffix);
> +        // If we already have a file of this name reformat to "filename.x.png"
> +        // (where x is a number, incremented until we find an available filename)
> +        if(QFile::exists(destination)) {
> +            int append = 1;
> +            do {
> +                destination = QString("%1%2.%3").arg(dir + filenameWithoutSuffix, QString::number(append), suffix);
> +                append++;
> +            } while(QFile::exists(destination));
> +        }
> +        QFile::copy(hubItem.url().toLocalFile(), destination);
> +    }
> +

Please remove a newline

> +
> +    // Allow content-hub to clean up temporary files in .cache/ once we've
> +    // moved them
> +    transfer->finalize();
> +
> +    emit documentImported();
> +}
> +
> +/*!
> + * \brief \reimp
> + */
> +void ContentCommunicator::handle_export(content::Transfer *transfer)
> +{
> +    if (m_transfer != nullptr) {
> +        qWarning() << "docviewer-app does only one content export at a time";
> +        transfer->abort();
> +        return;
> +    }
> +
> +    m_transfer = transfer;
> +    emit documentRequested();
> +    emit selectionTypeChanged();
> +    emit singleContentPickModeChanged();
> +}
> +
> +/*!
> + * \brief \reimp
> + */
> +void ContentCommunicator::handle_share(content::Transfer *)
> +{
> +    qDebug() << Q_FUNC_INFO << "docviewer does not share content";
> +}
> +
> +/*!
> + * \brief ContentCommunicator::cancelTransfer aborts the current transfer
> + */
> +void ContentCommunicator::cancelTransfer()
> +{
> +    if (!m_transfer) {
> +        qWarning() << "No ongoing transfer to cancel";
> +        return;
> +    }
> +
> +    m_transfer->abort();
> +    m_transfer = nullptr;
> +}
> +
> +/*!
> + * \brief ContentCommunicator::returnPhoto returns the given photos

This is returnDocuments, not returnPhoto :-)

> + * via content hub to the requester
> + * \param urls
> + */
> +void ContentCommunicator::returnDocuments(const QVector<QUrl> &urls)
> +{
> +    if (!m_transfer) {
> +        qWarning() << "No ongoing transfer to return a document";
> +        return;
> +    }
> +
> +    QVector<Item> items;
> +    items.reserve(urls.size());
> +    foreach (const QUrl &url, urls) {
> +        items.append(Item(url));
> +    }
> +
> +    m_transfer->charge(items);
> +    m_transfer = nullptr;
> +}
> +
> +/*!
> + * \brief ContentCommunicator::selectionType return if the transfer requests
> + * one single item only, or multiple
> + * \return
> + */
> +ContentCommunicator::SelectionType ContentCommunicator::selectionType() const
> +{
> +    if (!m_transfer)
> +        return SingleSelect;
> +
> +    return static_cast<SelectionType>(m_transfer->selectionType());
> +}
> +
> +/*!
> + * \brief ContentCommunicator::singleContentPickMode
> + * \return
> + */
> +bool ContentCommunicator::singleContentPickMode() const
> +{
> +    if (!m_transfer)
> +        return true;
> +
> +    return m_transfer->selectionType() == Transfer::SelectionType::single;

Shouldn't be Transfer::SelectionType::SingleSelect?

> +}
> 
> === added file 'src/app/content-communicator.h'
> --- src/app/content-communicator.h	1970-01-01 00:00:00 +0000
> +++ src/app/content-communicator.h	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,69 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef CONTENTCOMMUNICATOR_H
> +#define CONTENTCOMMUNICATOR_H
> +
> +#include <com/ubuntu/content/import_export_handler.h>
> +#include <com/ubuntu/content/transfer.h>
> +
> +#include <QUrl>
> +#include <QVector>
> +
> +using namespace com::ubuntu;
> +
> +/*!
> + * Class to handle the communication with the content manager
> + */
> +class ContentCommunicator : public content::ImportExportHandler
> +{
> +    Q_OBJECT
> +    Q_PROPERTY(bool singleContentPickMode READ singleContentPickMode NOTIFY singleContentPickModeChanged)
> +    Q_PROPERTY(SelectionType selectionType READ selectionType NOTIFY selectionTypeChanged)
> +    Q_ENUMS(SelectionType)
> +
> +public:
> +    enum SelectionType {
> +        SingleSelect = content::Transfer::single,
> +        MultiSelect = content::Transfer::multiple
> +    };
> +
> +    ContentCommunicator(QObject *parent = nullptr);
> +
> +    virtual void handle_import(content::Transfer*);
> +    virtual void handle_export(content::Transfer *transfer);
> +    virtual void handle_share(content::Transfer*);
> +
> +    void cancelTransfer();
> +    void returnDocuments(const QVector<QUrl> &urls);
> +
> +    SelectionType selectionType() const;
> +    bool singleContentPickMode() const;
> +
> +    void registerWithHub();
> +
> +signals:
> +    void documentRequested();
> +    void documentImported();
> +    void selectionTypeChanged();
> +    void singleContentPickModeChanged();
> +
> +private:
> +    content::Transfer *m_transfer;
> +};
> +
> +#endif // CONTENTCOMMUNICATOR_H
> 
> === added file 'src/app/docviewer-application.cpp'
> --- src/app/docviewer-application.cpp	1970-01-01 00:00:00 +0000
> +++ src/app/docviewer-application.cpp	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,298 @@
> +/*
> + * Copyright (C) 2012 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 3 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authors:
> + * Charles Lindsay <chaz@xxxxxxxxx>
> + */
> +
> +#include "docviewer-application.h"
> +#include "content-communicator.h"
> +#include "command-line-parser.h"
> +#include "urlhandler.h"
> +#include "quick/documentmodel.h"
> +
> +#include <QQuickItem>
> +#include <QStandardPaths>
> +#include <QQuickView>
> +#include <QtQml/QtQml>
> +#include <QString>
> +#include <QUrl>
> +#include <QtGui/QGuiApplication>
> +
> +/*!
> + * \brief DocViewerApplication::DocViewerApplication
> + * \param argc
> + * \param argv
> + */
> +DocViewerApplication::DocViewerApplication(int& argc, char** argv)
> +    : QApplication(argc, argv),
> +      m_view(new QQuickView()),
> +      m_contentCommunicator(new ContentCommunicator(this)),
> +      m_pickModeEnabled(false),
> +      m_defaultUiMode(BrowseContentMode),
> +      m_documentFile(""),
> +      m_documentLoaded(false)
> +{
> +   //
> +}
> +
> +bool DocViewerApplication::init()
> +{
> +    m_cmdLineParser = new CommandLineParser();
> +    bool ok = m_cmdLineParser->processArguments(arguments());
> +    if (!ok)
> +        return false;

Why do you instantiate a new var?
Just do if (!m_cmdLineParser->processArguments(arguments())) return false;

> +
> +    if (qgetenv("QT_LOAD_TESTABILITY") == "1" || m_cmdLineParser->testability()) {
> +        QLibrary testLib(QLatin1String("qttestability"));
> +        if (testLib.load()) {
> +            typedef void (*TasInitialize)(void);
> +            TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
> +            if (initFunction) {
> +                initFunction();
> +            } else {
> +                qCritical("Library qttestability resolve failed!");
> +            }
> +        } else {
> +            qCritical("Library qttestability load failed!");
> +        }
> +    }
> +
> +    m_urlHandler = new UrlHandler();
> +
> +    registerQML();
> +
> +    if (m_cmdLineParser->pickModeEnabled())
> +        setDefaultUiMode(DocViewerApplication::PickContentMode);
> +
> +    QObject::connect(m_contentCommunicator, SIGNAL(documentRequested()),
> +                     this, SLOT(switchToPickMode()));
> +
> +    QObject::connect(m_contentCommunicator, SIGNAL(documentImported()),
> +                     this, SLOT(switchToBrowseMode()));
> +
> +    return true;
> +}
> +
> +/*!
> + * \brief DocViewerApplication::~DocViewerApplication
> + */
> +DocViewerApplication::~DocViewerApplication()
> +{
> +    delete m_view;
> +    delete m_cmdLineParser;
> +}
> +
> +/*!
> + * \brief DocViewerApplication::exec
> + * \return
> + */
> +int DocViewerApplication::exec()
> +{
> +    createView();
> +
> +    return QApplication::exec();
> +}
> +
> +/*!
> + * \brief DocViewerApplication::registerQML
> + */
> +void DocViewerApplication::registerQML()
> +{
> +    // Set up import paths
> +    QStringList importPathList = m_view->engine()->importPathList();
> +    // Prepend the location of the plugin in the build dir,
> +    // so that Qt Creator finds it there, thus overriding the one installed
> +    // in the sistem if there is one
> +    importPathList.prepend(QCoreApplication::applicationDirPath() + "/../plugin/");
> +    m_view->engine()->setImportPathList(importPathList);
> +
> +    qmlRegisterType<SortFilterDocumentModel>("DocumentViewer", 1, 0, "DocumentsModel");
> +}
> +
> +/*!
> + * \brief DocViewerApplication::getDocumentFile
> + * Returns the document file passed as a parameter
> + */
> +const QString& DocViewerApplication::getDocumentFile() const
> +{
> +    return m_documentFile;
> +}
> +
> +/*!
> + * \brief DocViewerApplication::createView
> + * Create the master QDeclarativeView that all the pages will operate within
> + */
> +void DocViewerApplication::createView()
> +{
> +    m_view->setTitle("Document Viewer");

I think this should be a translable string:

m_view->setTitle(tr("Document Viewer"));

> +
> +    // Set ourselves up to expose functionality to run external commands from QML...
> +    m_view->engine()->rootContext()->setContextProperty("DOC_VIEWER", this);
> +    m_view->engine()->rootContext()->setContextProperty("PICKER_HUB", m_contentCommunicator);
> +
> +    QObject::connect(m_view->engine(), SIGNAL(quit()), this, SLOT(quit()));
> +
> +    QString qmlfile;
> +    const QString filePath = QLatin1String("qml/ubuntu-docviewer-app.qml");
> +    QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
> +    paths.prepend(QDir::currentPath());
> +    paths.prepend(QCoreApplication::applicationDirPath());
> +    Q_FOREACH (const QString &path, paths) {
> +        QString myPath = path + QLatin1Char('/') + filePath;
> +
> +        if (QFile::exists(myPath)) {
> +            qmlfile = myPath;
> +            break;
> +        } else {
> +            myPath.replace(QCoreApplication::applicationName(), "ubuntu-docviewer-app");
> +
> +            if (QFile::exists(myPath)) {
> +                qmlfile = myPath;
> +                break;
> +            }
> +        }
> +    }
> +    // sanity check
> +    if (qmlfile.isEmpty()) {
> +        qFatal("File: %s does not exist at any of the standard paths!", qPrintable(filePath));
> +    }
> +
> +    registerHub();
> +    m_view->setSource(QUrl::fromLocalFile(qmlfile));
> +    setDocumentFile(m_cmdLineParser->documentFile());
> +
> +    m_view->setResizeMode(QQuickView::SizeRootObjectToView);
> +    m_view->show();
> +}
> +
> +/*!
> + * \brief DocViewerApplication::setDefaultUiMode set the default UI mode. This might
> + * get overridden during the lifetime
> + * \param mode
> + */
> +void DocViewerApplication::setDefaultUiMode(DocViewerApplication::UiMode mode)
> +{
> +    m_defaultUiMode = mode;
> +    setUiMode(mode);
> +}
> +
> +/*!
> + * \brief DocViewerApplication::setUiMode set's the current UI mode
> + * \param mode
> + */
> +void DocViewerApplication::setUiMode(DocViewerApplication::UiMode mode)
> +{
> +    bool enablePickMode = (mode == PickContentMode);
> +
> +    if (enablePickMode != m_pickModeEnabled) {
> +        m_pickModeEnabled = enablePickMode;
> +        Q_EMIT pickModeEnabledChanged();
> +    }
> +}
> +
> +/*!
> + * \brief DocViewerApplication::pickModeEnabled returns true if the current UI
> + * mode should be for picking acontent
> + * \return
> + */
> +bool DocViewerApplication::pickModeEnabled() const
> +{
> +    return m_pickModeEnabled;
> +}
> +
> +/*!
> + * \brief DocViewerApplication::switchToPickMode
> + */
> +void DocViewerApplication::switchToPickMode()
> +{
> +    setUiMode(PickContentMode);
> +}
> +
> +/*!
> + * \brief DocViewerApplication::switchToBrowseMode
> + */
> +void DocViewerApplication::switchToBrowseMode()
> +{
> +    Q_EMIT browseModeRequested();
> +}
> +
> +void DocViewerApplication::setDocumentFile(const QString &documentFile)
> +{
> +    if(!documentFile.isEmpty()) {
> +        if (m_documentFile != documentFile) {
> +            m_documentFile = "file://" + documentFile;
> +            Q_EMIT documentFileChanged();;
> +        }
> +    }
> +}
> +
> +/*!
> + * \brief DocViewerApplication::returnPickedContent passes the selcted items to the
> + * content manager
> + * \param variant
> + */
> +void DocViewerApplication::returnPickedContent(QList<QString> paths)
> +{
> +    QVector<QUrl> selectedMedias;
> +    selectedMedias.reserve(paths.size());
> +    foreach (const QString path, paths) {
> +        // We handle paths without "file://" prefix, so we need to add it when exporting to content-hub.
> +        selectedMedias.append(QUrl("file://" + path));
> +    }
> +    m_contentCommunicator->returnDocuments(selectedMedias);
> +
> +    if (m_defaultUiMode == BrowseContentMode) {
> +        setUiMode(BrowseContentMode);
> +    } else {
> +        // give the app and content-hub some time to finish taks (run the event loop)
> +        QTimer::singleShot(10, this, SLOT(quit()));
> +    }
> +}
> +
> +/*!
> + * \brief DocViewerApplication::contentPickingCanceled tell the content manager, that
> + * the picking was canceled
> + */
> +void DocViewerApplication::contentPickingCanceled()
> +{
> +    m_contentCommunicator->cancelTransfer();
> +
> +    if (m_defaultUiMode == BrowseContentMode) {
> +        setUiMode(BrowseContentMode);
> +    } else {
> +        // give the app and content-hub some time to finish taks (run the event loop)
> +        QTimer::singleShot(10, this, SLOT(quit()));
> +    }
> +}
> +
> +void DocViewerApplication::registerHub()
> +{
> +    m_contentCommunicator->registerWithHub();
> +}
> +
> +void DocViewerApplication::parseUri(const QString &arg)
> +{
> +    if (m_urlHandler->processUri(arg)) {
> +        setDocumentFile(m_urlHandler->documentFile());
> +    }
> +}
> +
> +void DocViewerApplication::releaseResources()
> +{
> +    if (m_view) {
> +        m_view->releaseResources();
> +    }
> +}
> 
> === added file 'src/app/docviewer-application.h'
> --- src/app/docviewer-application.h	1970-01-01 00:00:00 +0000
> +++ src/app/docviewer-application.h	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,92 @@
> +/*
> + * Copyright (C) 2012 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 3 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authors:
> + * Charles Lindsay <chaz@xxxxxxxxx>
> + */
> +
> +#ifndef DOCVIEWERAPPLICATION_H
> +#define DOCVIEWERAPPLICATION_H
> +
> +#include <QApplication>
> +#include <QElapsedTimer>
> +#include <QFileInfo>
> +#include <QTimer>
> +
> +class CommandLineParser;
> +class UrlHandler;
> +class ContentCommunicator;
> +
> +class QQuickView;
> +
> +/*!
> + * \brief The DocViewerApplication class
> + */
> +class DocViewerApplication : public QApplication
> +{
> +    Q_OBJECT
> +    Q_PROPERTY(bool pickModeEnabled READ pickModeEnabled NOTIFY pickModeEnabledChanged)
> +    Q_PROPERTY(QString documentFile READ getDocumentFile WRITE setDocumentFile NOTIFY documentFileChanged)
> +
> +public:
> +    enum UiMode{
> +        BrowseContentMode,
> +        PickContentMode
> +    };
> +
> +    explicit DocViewerApplication(int& argc, char** argv);
> +    virtual ~DocViewerApplication();
> +
> +    bool init();
> +    int exec();
> +
> +    void setDefaultUiMode(UiMode mode);
> +    UiMode defaultUiMode() const;
> +    void setUiMode(UiMode mode);
> +    bool pickModeEnabled() const;
> +    const QString &getDocumentFile() const;
> +
> +    Q_INVOKABLE void returnPickedContent(QList<QString> paths);
> +    Q_INVOKABLE void contentPickingCanceled();
> +    Q_INVOKABLE void parseUri(const QString &arg);
> +    Q_INVOKABLE void releaseResources();
> +
> +signals:
> +    void pickModeEnabledChanged();
> +    void documentFileChanged();
> +    void browseModeRequested();
> +
> +private slots:
> +    void switchToPickMode();
> +    void switchToBrowseMode();
> +    void setDocumentFile(const QString &documentFile);
> +
> +private:
> +    void registerHub();
> +    void registerQML();
> +    void createView();
> +
> +    QQuickView *m_view;
> +    CommandLineParser* m_cmdLineParser;
> +    UrlHandler *m_urlHandler;
> +    ContentCommunicator *m_contentCommunicator;
> +
> +    bool m_pickModeEnabled;
> +    UiMode m_defaultUiMode;
> +    QString m_documentFile;
> +    bool m_documentLoaded;
> +};
> +
> +#endif // DOCVIEWERAPPLICATION_H
> 
> === added directory 'src/app/graphics'
> === added file 'src/app/graphics/select-none.svg'
> --- src/app/graphics/select-none.svg	1970-01-01 00:00:00 +0000
> +++ src/app/graphics/select-none.svg	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,153 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!-- Created with Inkscape (http://www.inkscape.org/) -->
> +
> +<svg
> +   xmlns:dc="http://purl.org/dc/elements/1.1/";
> +   xmlns:cc="http://creativecommons.org/ns#";
> +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
> +   xmlns:svg="http://www.w3.org/2000/svg";
> +   xmlns="http://www.w3.org/2000/svg";
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
> +   width="90"
> +   height="90"
> +   id="svg4874"
> +   version="1.1"
> +   inkscape:version="0.48+devel r"
> +   viewBox="0 0 90 90.000001"
> +   sodipodi:docname="select-none.svg">
> +  <defs
> +     id="defs4876" />
> +  <sodipodi:namedview
> +     id="base"
> +     pagecolor="#ffffff"
> +     bordercolor="#666666"
> +     borderopacity="1.0"
> +     inkscape:pageopacity="0.0"
> +     inkscape:pageshadow="2"
> +     inkscape:zoom="12.434498"
> +     inkscape:cx="10.237647"
> +     inkscape:cy="53.078139"
> +     inkscape:document-units="px"
> +     inkscape:current-layer="g1311"
> +     showgrid="true"
> +     showborder="true"
> +     fit-margin-top="0"
> +     fit-margin-left="0"
> +     fit-margin-right="0"
> +     fit-margin-bottom="0"
> +     inkscape:snap-bbox="true"
> +     inkscape:bbox-paths="true"
> +     inkscape:bbox-nodes="true"
> +     inkscape:snap-bbox-edge-midpoints="true"
> +     inkscape:snap-bbox-midpoints="true"
> +     inkscape:object-paths="true"
> +     inkscape:snap-intersection-paths="true"
> +     inkscape:object-nodes="true"
> +     inkscape:snap-smooth-nodes="true"
> +     inkscape:snap-midpoints="true"
> +     inkscape:snap-object-midpoints="true"
> +     inkscape:snap-center="true"
> +     showguides="true"
> +     inkscape:guide-bbox="true">
> +    <inkscape:grid
> +       type="xygrid"
> +       id="grid5451"
> +       empspacing="6" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="6,77"
> +       id="guide4063" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="3,78"
> +       id="guide4065" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="55,84"
> +       id="guide4067" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="53,87"
> +       id="guide4069" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="20,3"
> +       id="guide4071" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="20,6"
> +       id="guide4073" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="87,7"
> +       id="guide4075" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="84,7"
> +       id="guide4077" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="58,81"
> +       id="guide4074" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="9,74"
> +       id="guide4076" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="21,9"
> +       id="guide4078" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="81,4"
> +       id="guide4080" />
> +  </sodipodi:namedview>
> +  <metadata
> +     id="metadata4879">
> +    <rdf:RDF>
> +      <cc:Work
> +         rdf:about="">
> +        <dc:format>image/svg+xml</dc:format>
> +        <dc:type
> +           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
> +        <dc:title></dc:title>
> +      </cc:Work>
> +    </rdf:RDF>
> +  </metadata>
> +  <g
> +     inkscape:label="Layer 1"
> +     inkscape:groupmode="layer"
> +     id="layer1"
> +     transform="translate(67.857146,-84.50504)">
> +    <g
> +       transform="matrix(0,-1,-1,0,373.50506,516.50504)"
> +       id="g4845"
> +       style="display:inline">
> +      <g
> +         transform="matrix(0,-1,-1,0,567.36222,615.36221)"
> +         id="g1311"
> +         inkscape:export-filename="envelope02.png"
> +         inkscape:export-xdpi="90"
> +         inkscape:export-ydpi="90">
> +        <g
> +           id="g1313"
> +           transform="matrix(1.875,0,0,1.875,-366,-1657.8169)">
> +          <rect
> +             transform="translate(0,804.3622)"
> +             y="152"
> +             x="288"
> +             height="48"
> +             width="48"
> +             id="rect1315"
> +             style="opacity:0.21171169;fill:none;stroke:none" />
> +        </g>
> +        <path
> +           style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none;display:inline;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
> +           d="M 21 6 C 11 6 6 5.9998033 6 17.626953 L 6 72.373047 C 6 84.000207 11 84 21 84 L 69 84 C 79 84 84 84.000207 84 72.373047 L 84 17.626953 C 84 5.9998033 79 6 69 6 L 21 6 z M 22.867188 12 L 67.132812 12 C 75.065512 12 78 11.999356 78 20.191406 L 78 69.808594 C 78 78.000644 75.065512 78 67.132812 78 L 22.867188 78 C 14.934488 78 12 78.000644 12 69.808594 L 12 20.191406 C 12 11.999356 14.934488 12 22.867188 12 z "
> +           transform="translate(174,135.36222)"
> +           id="path4098" />
> +      </g>
> +    </g>
> +  </g>
> +</svg>
> 
> === added file 'src/app/graphics/select.svg'
> --- src/app/graphics/select.svg	1970-01-01 00:00:00 +0000
> +++ src/app/graphics/select.svg	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,158 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!-- Created with Inkscape (http://www.inkscape.org/) -->
> +
> +<svg
> +   xmlns:dc="http://purl.org/dc/elements/1.1/";
> +   xmlns:cc="http://creativecommons.org/ns#";
> +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
> +   xmlns:svg="http://www.w3.org/2000/svg";
> +   xmlns="http://www.w3.org/2000/svg";
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
> +   width="90"
> +   height="90"
> +   id="svg4874"
> +   version="1.1"
> +   inkscape:version="0.48+devel r12833"
> +   viewBox="0 0 90 90.000001"
> +   sodipodi:docname="select.svg">
> +  <defs
> +     id="defs4876" />
> +  <sodipodi:namedview
> +     id="base"
> +     pagecolor="#ffffff"
> +     bordercolor="#666666"
> +     borderopacity="1.0"
> +     inkscape:pageopacity="0.0"
> +     inkscape:pageshadow="2"
> +     inkscape:zoom="12.434498"
> +     inkscape:cx="30.343002"
> +     inkscape:cy="53.600878"
> +     inkscape:document-units="px"
> +     inkscape:current-layer="g1311"
> +     showgrid="true"
> +     showborder="true"
> +     fit-margin-top="0"
> +     fit-margin-left="0"
> +     fit-margin-right="0"
> +     fit-margin-bottom="0"
> +     inkscape:snap-bbox="true"
> +     inkscape:bbox-paths="true"
> +     inkscape:bbox-nodes="true"
> +     inkscape:snap-bbox-edge-midpoints="true"
> +     inkscape:snap-bbox-midpoints="true"
> +     inkscape:object-paths="true"
> +     inkscape:snap-intersection-paths="true"
> +     inkscape:object-nodes="true"
> +     inkscape:snap-smooth-nodes="true"
> +     inkscape:snap-midpoints="true"
> +     inkscape:snap-object-midpoints="true"
> +     inkscape:snap-center="true"
> +     showguides="true"
> +     inkscape:guide-bbox="true">
> +    <inkscape:grid
> +       type="xygrid"
> +       id="grid5451"
> +       empspacing="6" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="6,77"
> +       id="guide4063" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="3,78"
> +       id="guide4065" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="55,84"
> +       id="guide4067" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="53,87"
> +       id="guide4069" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="20,3"
> +       id="guide4071" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="20,6"
> +       id="guide4073" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="87,7"
> +       id="guide4075" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="84,7"
> +       id="guide4077" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="58,81"
> +       id="guide4074" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="9,74"
> +       id="guide4076" />
> +    <sodipodi:guide
> +       orientation="0,1"
> +       position="21,9"
> +       id="guide4078" />
> +    <sodipodi:guide
> +       orientation="1,0"
> +       position="81,4"
> +       id="guide4080" />
> +  </sodipodi:namedview>
> +  <metadata
> +     id="metadata4879">
> +    <rdf:RDF>
> +      <cc:Work
> +         rdf:about="">
> +        <dc:format>image/svg+xml</dc:format>
> +        <dc:type
> +           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
> +        <dc:title></dc:title>
> +      </cc:Work>
> +    </rdf:RDF>
> +  </metadata>
> +  <g
> +     inkscape:label="Layer 1"
> +     inkscape:groupmode="layer"
> +     id="layer1"
> +     transform="translate(67.857146,-84.50504)">
> +    <g
> +       transform="matrix(0,-1,-1,0,373.50506,516.50504)"
> +       id="g4845"
> +       style="display:inline">
> +      <g
> +         transform="matrix(0,-1,-1,0,567.36222,615.36221)"
> +         id="g1311"
> +         inkscape:export-filename="envelope02.png"
> +         inkscape:export-xdpi="90"
> +         inkscape:export-ydpi="90">
> +        <g
> +           id="g1313"
> +           transform="matrix(1.875,0,0,1.875,-366,-1657.8169)">
> +          <rect
> +             transform="translate(0,804.3622)"
> +             y="152"
> +             x="288"
> +             height="48"
> +             width="48"
> +             id="rect1315"
> +             style="opacity:0.21171169;fill:none;stroke:none" />
> +        </g>
> +        <path
> +           style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none;display:inline;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
> +           d="M 21 6 C 11 6 6 5.9998033 6 17.626953 L 6 72.373047 C 6 84.000207 11 84 21 84 L 69 84 C 79 84 84 84.000207 84 72.373047 L 84 17.626953 C 84 5.9998033 79 6 69 6 L 21 6 z M 22.867188 12 L 67.132812 12 C 75.065512 12 78 11.999356 78 20.191406 L 78 69.808594 C 78 78.000644 75.065512 78 67.132812 78 L 22.867188 78 C 14.934488 78 12 78.000644 12 69.808594 L 12 20.191406 C 12 11.999356 14.934488 12 22.867188 12 z "
> +           transform="translate(174,135.36222)"
> +           id="path4098" />
> +        <path
> +           style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
> +           d="m 242.00422,161.6591 -0.375,0.32812 -26.94727,23.60352 -15.79687,-13.55079 -4.77539,5.4004 20.57617,21.64843 31.30078,-32.9375 -3.98242,-4.49218 z"
> +           id="path4041-9"
> +           inkscape:connector-curvature="0" />
> +      </g>
> +    </g>
> +  </g>
> +</svg>
> 
> === modified file 'src/app/main.cpp'
> --- src/app/main.cpp	2015-01-30 18:52:42 +0000
> +++ src/app/main.cpp	2015-02-27 15:45:56 +0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright: 2013 - 2015 Canonical, Ltd
> + * Copyright: 2015 Canonical Ltd.
>   *
>   * This file is part of docviewer
>   *
> @@ -14,111 +14,23 @@
>   * GNU General Public License for more details.
>   *
>   * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - *
> - * Authors: Michael Zanetti <michael.zanetti@xxxxxxxxxxxxx>
> - *          Riccardo Padovani <rpadovani@xxxxxxxxxx>
> - *          David Planella <david.planella@xxxxxxxxxx>
> - *          Stefano Verzegnassi <stefano92.100@xxxxxxxxx>
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
>   */
>  
>  // Uncomment if you need to use QML analyzer
>  // #define QT_QML_DEBUG
>  
> -#include <QtGui/QGuiApplication>
> -#include <QtQuick/QQuickView>
> -#include <QtQml/QtQml>
> -#include <QLibrary>
> -#include <QDir>
> -
> +#include "docviewer-application.h"
>  #include <QDebug>
>  
>  int main(int argc, char *argv[])
>  {
> -    QGuiApplication a(argc, argv);
> -    QQuickView view;
> -    view.setResizeMode(QQuickView::SizeRootObjectToView);
> -
> -    view.setPersistentOpenGLContext(false);
> -    view.setPersistentSceneGraph(false);
> -    view.engine()->rootContext()->setContextProperty("QQuickView", &view);
> -
> -    // Set up import paths
> -    QStringList importPathList = view.engine()->importPathList();
> -    // Prepend the location of the plugin in the build dir,
> -    // so that Qt Creator finds it there, thus overriding the one installed
> -    // in the sistem if there is one
> -    importPathList.prepend(QCoreApplication::applicationDirPath() + "/../plugin/");
> -
> -    QStringList args = a.arguments();
> -    if (args.contains("-h") || args.contains("--help")) {
> -        qDebug() << "usage: " + args.at(0) + " [-h|--help] <path>";
> -        qDebug() << "    -h|--help     Print this help.";
> -        qDebug() << "    <path>        Path of the document to load.";
> +    QCoreApplication::setApplicationName("com.ubuntu.docviewer");
> +    QCoreApplication::setOrganizationDomain("com.ubuntu.docviewer");

More than setOrganizationDomain, you have to use setOrganizationName, that is used to name the folder in .local/share

> +
> +    DocViewerApplication app(argc, argv);
> +    if (!app.init())
>          return 0;
> -    }
> -
> -    // Check if the path of the document has been specified.
> -    QString docPath;
> -    for (int i = 1; i < args.count(); i++) {
> -        if (args.at(i) != "-h" && args.at(i) != "--h") {
> -            docPath = args.at(i);
> -        }
> -    }
> -    view.engine()->rootContext()->setContextProperty("documentPath", docPath);
> -
> -    if (args.contains(QLatin1String("-testability")) || getenv("QT_LOAD_TESTABILITY")) {
> -        QLibrary testLib(QLatin1String("qttestability"));
> -        if (testLib.load()) {
> -            typedef void (*TasInitialize)(void);
> -            TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
> -            if (initFunction) {
> -                initFunction();
> -            } else {
> -                qCritical("Library qttestability resolve failed!");
> -            }
> -        } else {
> -            qCritical("Library qttestability load failed!");
> -        }
> -    }
> -
> -    view.engine()->setImportPathList(importPathList);
> -
> -    QString qmlfile;
> -    const QString filePath = QLatin1String("qml/ubuntu-docviewer-app.qml");
> -    QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
> -    paths.prepend(QDir::currentPath());
> -    paths.prepend(QCoreApplication::applicationDirPath());
> -    Q_FOREACH (const QString &path, paths) {
> -        QString myPath = path + QLatin1Char('/') + filePath;
> -        if (QFile::exists(myPath)) {
> -            qmlfile = myPath;
> -            break;
> -        }
> -    }
> -    // sanity check
> -    if (qmlfile.isEmpty()) {
> -        qFatal("File: %s does not exist at any of the standard paths!", qPrintable(filePath));
> -    }
> -
> -    // Make sure our cache dir exists. It'll be used all over in this app.
> -    // We need to set the applicationName for that.
> -    // It'll be overwritten again when qml loads but we need it already now.
> -    // So if you want to change it, make sure to find all the places where it is set, not just here :D
> -    QCoreApplication::setApplicationName("com.ubuntu.docviewer");
> -
> -    QDir cacheDir(QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first());
> -    if (!cacheDir.exists()) {
> -        qDebug() << "creating cacheDir:" << cacheDir.absolutePath();
> -        cacheDir.mkpath(cacheDir.absolutePath());
> -    }
> -
> -    // Expose quit() signal to QML
> -    QObject::connect(view.engine(), SIGNAL(quit()), &a, SLOT(quit()));
> -
> -    qDebug() << "using main qml file from:" << qmlfile;
> -    view.setSource(QUrl::fromLocalFile(qmlfile));
> -    view.show();
> -
> -    return a.exec();
> +
> +    app.exec();
>  }
> 
> === removed file 'src/app/qml/ContentHubPicker.qml'
> --- src/app/qml/ContentHubPicker.qml	2015-01-29 16:24:50 +0000
> +++ src/app/qml/ContentHubPicker.qml	1970-01-01 00:00:00 +0000
> @@ -1,71 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.0
> -import Ubuntu.Components 1.1
> -import Ubuntu.Content 1.1
> -
> -Page {
> -    id: picker
> -    title: i18n.tr("Open with...")
> -
> -    property var activeTransfer
> -
> -    head.sections.model: [i18n.tr("Documents"), i18n.tr("Pictures"), i18n.tr("Other")]
> -    head.backAction: Action {
> -        iconName: "back"
> -        text: i18n.tr("Back")
> -        onTriggered: pageStack.pop()
> -    }
> -
> -    ContentPeerPicker {
> -        // Do not show ContentPeerPicker header, since we need head.sections.
> -        showTitle: false
> -
> -        contentType: {
> -            switch (picker.head.sections.selectedIndex) {
> -            case 0:
> -                return ContentType.Documents
> -            case 1:
> -                return ContentType.Pictures
> -            case 2:
> -                return ContentType.Unknown
> -            }
> -        }
> -        handler: ContentHandler.Source
> -
> -        onPeerSelected: picker.activeTransfer = peer.request();
> -    }
> -
> -    ContentTransferHint {
> -        id: transferHint
> -        anchors.fill: parent
> -        activeTransfer: picker.activeTransfer
> -    }
> -
> -    Connections {
> -        target: picker.activeTransfer ? picker.activeTransfer : null
> -        onStateChanged: {
> -            if (picker.activeTransfer.state === ContentTransfer.Charged) {
> -                // Close ContentHubPicker page.
> -                pageStack.pop();
> -
> -                file.path = picker.activeTransfer.items[0].url.toString().replace("file://", "")
> -                console.log("[CONTENT-HUB] Content imported!")
> -            }
> -        }
> -    }
> -}
> 
> === removed file 'src/app/qml/ContentHubProxy.qml'
> --- src/app/qml/ContentHubProxy.qml	2014-11-04 18:54:38 +0000
> +++ src/app/qml/ContentHubProxy.qml	1970-01-01 00:00:00 +0000
> @@ -1,38 +0,0 @@
> -/*
> - * Copyright (C) 2012-2014 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Content 1.1 as ContentHub
> -
> -QtObject {
> -    property QtObject pageStack: null
> -    property list<QtObject> objects: [
> -        Connections {
> -            target: ContentHub.ContentHub
> -
> -            onImportRequested: {
> -                if (transfer.state === ContentHub.ContentTransfer.Charged) {
> -                    // We have no signals to know if an import was requested before Component.completed signal
> -                    //  is emitted. So clear the stack when this occurs.
> -                    pageStack.clear()
> -
> -                    console.log("[CONTENT-HUB] Incoming Import Request")
> -                    file.path = transfer.items[0].url.toString().replace("file://", "");
> -                }
> -            }
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/DetailsPage.qml'
> --- src/app/qml/DetailsPage.qml	2015-01-29 16:24:50 +0000
> +++ src/app/qml/DetailsPage.qml	1970-01-01 00:00:00 +0000
> @@ -1,58 +0,0 @@
> -/*
> - * Copyright (C) 2013-2014 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import Ubuntu.Components.ListItems 1.0 as ListItem
> -
> -import "utils.js" as Utils
> -
> -Page {
> -    id: detailsPage
> -    objectName: "detailsPage"
> -
> -    title: i18n.tr("Details")
> -
> -    Column {
> -        width: parent.width
> -
> -        ListItem.Subtitled {
> -            text: i18n.tr("Location")
> -            subText: file.path
> -        }
> -        ListItem.Subtitled {
> -            text: i18n.tr("Size")
> -            subText: Utils.printSize(file.size)
> -        }
> -
> -        ListItem.Subtitled {
> -            text: i18n.tr("Created")
> -            subText: file.creationTime.toLocaleString(Qt.locale())
> -        }
> -
> -        ListItem.Subtitled {
> -            text: i18n.tr("Last modified")
> -            subText: file.lastModified.toLocaleString(Qt.locale())
> -        }
> -
> -        ListItem.Subtitled {
> -            id: mimetypeItem
> -            objectName: "mimetypeItem"
> -            text: i18n.tr("MIME type")
> -            subText: file.mimetype
> -        }
> -    }
> -}
> 
> === removed file 'src/app/qml/EmptyState.qml'
> --- src/app/qml/EmptyState.qml	2015-01-16 16:46:46 +0000
> +++ src/app/qml/EmptyState.qml	1970-01-01 00:00:00 +0000
> @@ -1,59 +0,0 @@
> -/*
> - * Copyright (C) 2014 Canonical Ltd
> - *
> - * This file is part of Ubuntu Clock App
> - *
> - * Ubuntu Clock App is free software: you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 3 as
> - * published by the Free Software Foundation.
> - *
> - * Ubuntu Clock App is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -
> -/*
> - Component which displays an empty state (approved by design). It offers an
> - icon, title and subtitle to describe the empty state.
> -*/
> -
> -Item {
> -    id: emptyState
> -
> -    // Public APIs
> -    property alias iconName: emptyIcon.name
> -    property alias title: emptyLabel.text
> -    property alias subTitle: emptySublabel.text
> -
> -    height: childrenRect.height
> -
> -    Icon {
> -        id: emptyIcon
> -        anchors.horizontalCenter: parent.horizontalCenter
> -        height: units.gu(10)
> -        width: height
> -        color: "#BBBBBB"
> -    }
> -
> -    Label {
> -        id: emptyLabel
> -        anchors.top: emptyIcon.bottom
> -        anchors.topMargin: units.gu(5)
> -        anchors.horizontalCenter: parent.horizontalCenter
> -        fontSize: "large"
> -        font.bold: true
> -    }
> -
> -    Label {
> -        id: emptySublabel
> -        anchors.top: emptyLabel.bottom
> -        anchors.horizontalCenter: parent.horizontalCenter
> -    }
> -}
> 
> === removed file 'src/app/qml/ErrorDialog.qml'
> --- src/app/qml/ErrorDialog.qml	2015-01-29 19:14:08 +0000
> +++ src/app/qml/ErrorDialog.qml	1970-01-01 00:00:00 +0000
> @@ -1,32 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -Dialog {
> -    id: errorDialog
> -    title: i18n.tr("Error")
> -    text: i18n.tr("File does not exist")
> -
> -    Button {
> -        text: i18n.tr("Close")
> -        color: UbuntuColors.red
> -
> -        onClicked: Qt.quit();
> -    }
> -}
> 
> === removed file 'src/app/qml/ImageView.qml'
> --- src/app/qml/ImageView.qml	2015-02-03 21:11:52 +0000
> +++ src/app/qml/ImageView.qml	1970-01-01 00:00:00 +0000
> @@ -1,48 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -
> -import "utils.js" as Utils
> -
> -Page {
> -    id: imagePage
> -    title: Utils.getNameOfFile(file.path);
> -
> -    Rectangle {
> -        anchors.fill: parent
> -        color: "#221E1C"    // SuruDark.Palette.Normal.Background
> -    }
> -
> -    ZoomableImage {
> -        id: zoomableImage
> -        anchors.fill: parent
> -
> -        zoomable: true
> -        source: file.path
> -    }
> -
> -    // *** HEADER ***
> -    state: "default"
> -    states: [
> -        ImageViewDefaultHeader {
> -            name: "default"
> -            targetPage: imagePage
> -            activityRunning: zoomableImage.imageStatus == Image.Loading
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/ImageViewDefaultHeader.qml'
> --- src/app/qml/ImageViewDefaultHeader.qml	2015-02-03 21:11:52 +0000
> +++ src/app/qml/ImageViewDefaultHeader.qml	1970-01-01 00:00:00 +0000
> @@ -1,74 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import QtQuick.Layouts 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -PageHeadState {
> -    id: rootItem
> -
> -    property Page targetPage
> -    property alias activityRunning: activity.running
> -
> -    head: targetPage.head
> -
> -    contents: RowLayout {
> -        anchors.fill: parent
> -        spacing: units.gu(1)
> -
> -        ActivityIndicator { id: activity }
> -
> -        Column {
> -            id: layout
> -            Layout.fillWidth: true
> -
> -            Label {
> -                width: parent.width
> -                //horizontalAlignment: Text.AlignHCenter
> -                elide: Text.ElideMiddle
> -
> -                font.weight: Font.DemiBold
> -                text: targetPage.title
> -            }
> -        }
> -    }
> -
> -    backAction: Action {
> -        iconName: "back"
> -        text: (pageStack.depth > 1) ? i18n.tr("Back") : i18n.tr("Close")
> -        onTriggered: {
> -            if (pageStack.depth > 1) {
> -                // Go back to Welcome page
> -                pageStack.pop();
> -            } else {
> -                // File has been imported through Content Hub (or was not chosen through WelcomePage)
> -                // Close the application and show our source app (e.g. ubuntu-filemanager-app, if used to open a document)
> -                Qt.quit()
> -            }
> -        }
> -    }
> -
> -    actions: [
> -        Action {
> -            objectName: "detailsAction"
> -            text: i18n.tr("Details")
> -            iconName: "info"
> -            onTriggered: pageStack.push(Qt.resolvedUrl("DetailsPage.qml"))
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/PageWithBottomEdge.qml'
> --- src/app/qml/PageWithBottomEdge.qml	2015-02-04 19:19:21 +0000
> +++ src/app/qml/PageWithBottomEdge.qml	1970-01-01 00:00:00 +0000
> @@ -1,407 +0,0 @@
> -/*
> - * Copyright (C) 2014 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -/*
> -    Example:
> -
> -    MainView {
> -        objectName: "mainView"
> -
> -        applicationName: "com.ubuntu.developer.boiko.bottomedge"
> -
> -        width: units.gu(100)
> -        height: units.gu(75)
> -
> -        Component {
> -            id: pageComponent
> -
> -            PageWithBottomEdge {
> -                id: mainPage
> -                title: i18n.tr("Main Page")
> -
> -                Rectangle {
> -                    anchors.fill: parent
> -                    color: "white"
> -                }
> -
> -                bottomEdgePageComponent: Page {
> -                    title: "Contents"
> -                    anchors.fill: parent
> -                    //anchors.topMargin: contentsPage.flickable.contentY
> -
> -                    ListView {
> -                        anchors.fill: parent
> -                        model: 50
> -                        delegate: ListItems.Standard {
> -                            text: "One Content Item: " + index
> -                        }
> -                    }
> -                }
> -                bottomEdgeTitle: i18n.tr("Bottom edge action")
> -            }
> -        }
> -
> -        PageStack {
> -            id: stack
> -            Component.onCompleted: stack.push(pageComponent)
> -        }
> -    }
> -
> -*/
> -
> -import QtQuick 2.2
> -import Ubuntu.Components 1.1
> -
> -Page {
> -    id: page
> -
> -    property alias bottomEdgePageComponent: edgeLoader.sourceComponent
> -    property alias bottomEdgePageSource: edgeLoader.source
> -    property alias bottomEdgeTitle: tipLabel.text
> -    property bool bottomEdgeEnabled: true
> -    property int bottomEdgeExpandThreshold: page.height * 0.2
> -    property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
> -    property bool reloadBottomEdgePage: true
> -
> -    readonly property alias bottomEdgePage: edgeLoader.item
> -    readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active)
> -    readonly property bool isCollapsed: (bottomEdge.y === page.height)
> -    readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready)
> -
> -    property bool _showEdgePageWhenReady: false
> -    property int _areaWhenExpanded: 0
> -
> -    signal bottomEdgeReleased()
> -    signal bottomEdgeDismissed()
> -
> -
> -    function showBottomEdgePage(source, properties)
> -    {
> -        edgeLoader.setSource(source, properties)
> -        _showEdgePageWhenReady = true
> -    }
> -
> -    function setBottomEdgePage(source, properties)
> -    {
> -        edgeLoader.setSource(source, properties)
> -    }
> -
> -    function _pushPage()
> -    {
> -        if (edgeLoader.status === Loader.Ready) {
> -            edgeLoader.item.active = true
> -            page.pageStack.push(edgeLoader.item)
> -            if (edgeLoader.item.flickable) {
> -                edgeLoader.item.flickable.contentY = -page.header.height
> -                edgeLoader.item.flickable.returnToBounds()
> -            }
> -            if (edgeLoader.item.ready)
> -                edgeLoader.item.ready()
> -        }
> -    }
> -
> -
> -    Component.onCompleted: {
> -        // avoid a binding on the expanded height value
> -        var expandedHeight = height;
> -        _areaWhenExpanded = expandedHeight;
> -    }
> -
> -    onActiveChanged: {
> -        if (active) {
> -            bottomEdge.state = "collapsed"
> -        }
> -    }
> -
> -    onBottomEdgePageLoadedChanged: {
> -        if (_showEdgePageWhenReady && bottomEdgePageLoaded) {
> -            bottomEdge.state = "expanded"
> -            _showEdgePageWhenReady = false
> -        }
> -    }
> -
> -    Rectangle {
> -        id: bgVisual
> -
> -        color: "black"
> -        anchors.fill: page
> -        opacity: 0.7 * ((page.height - bottomEdge.y) / page.height)
> -        z: 1
> -    }
> -
> -    UbuntuShape {
> -        id: tip
> -        objectName: "bottomEdgeTip"
> -
> -        property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y)
> -
> -        enabled: mouseArea.enabled
> -        visible: page.bottomEdgeEnabled
> -        anchors {
> -            bottom: parent.bottom
> -            horizontalCenter: bottomEdge.horizontalCenter
> -            bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1)
> -            Behavior on bottomMargin {
> -                SequentialAnimation {
> -                    // wait some msecs in case of the focus change again, to avoid flickering
> -                    PauseAnimation {
> -                        duration: 300
> -                    }
> -                    UbuntuNumberAnimation {
> -                        duration: UbuntuAnimation.SnapDuration
> -                    }
> -                }
> -            }
> -        }
> -
> -        z: 1
> -        width: tipLabel.paintedWidth + units.gu(6)
> -        height: bottomEdge.tipHeight + units.gu(1)
> -        color: Theme.palette.normal.overlay
> -        Label {
> -            id: tipLabel
> -
> -            anchors {
> -                top: parent.top
> -                left: parent.left
> -                right: parent.right
> -            }
> -            height: bottomEdge.tipHeight
> -            verticalAlignment: Text.AlignVCenter
> -            horizontalAlignment: Text.AlignHCenter
> -            opacity: tip.hidden ? 0.0 : 1.0
> -            Behavior on opacity {
> -                UbuntuNumberAnimation {
> -                    duration: UbuntuAnimation.SnapDuration
> -                }
> -            }
> -        }
> -    }
> -
> -    Rectangle {
> -        id: shadow
> -
> -        anchors {
> -            left: parent.left
> -            right: parent.right
> -            bottom: parent.bottom
> -        }
> -        height: units.gu(1)
> -        z: 1
> -        opacity: 0.0
> -        gradient: Gradient {
> -            GradientStop { position: 0.0; color: "transparent" }
> -            GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
> -        }
> -    }
> -
> -    MouseArea {
> -        id: mouseArea
> -
> -        property real previousY: -1
> -        property string dragDirection: "None"
> -
> -        preventStealing: true
> -        drag {
> -            axis: Drag.YAxis
> -            target: bottomEdge
> -            minimumY: bottomEdge.pageStartY
> -            maximumY: page.height
> -        }
> -        enabled: edgeLoader.status == Loader.Ready
> -        visible: page.bottomEdgeEnabled
> -
> -        anchors {
> -            left: parent.left
> -            right: parent.right
> -            bottom: parent.bottom
> -
> -        }
> -        height: bottomEdge.tipHeight
> -        z: 1
> -
> -        onReleased: {
> -            page.bottomEdgeReleased()
> -            if ((dragDirection === "BottomToTop") &&
> -                bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
> -                bottomEdge.state = "expanded"
> -            } else {
> -                bottomEdge.state = "collapsed"
> -            }
> -            previousY = -1
> -            dragDirection = "None"
> -        }
> -
> -        onPressed: {
> -            previousY = mouse.y
> -            tip.forceActiveFocus()
> -        }
> -
> -        onMouseYChanged: {
> -            var yOffset = previousY - mouseY
> -            // skip if was a small move
> -            if (Math.abs(yOffset) <= units.gu(2)) {
> -                return
> -            }
> -            previousY = mouseY
> -            dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
> -        }
> -    }
> -
> -    Rectangle {
> -        id: bottomEdge
> -        objectName: "bottomEdge"
> -
> -        readonly property int tipHeight: units.gu(3)
> -        readonly property int pageStartY: 0
> -
> -        z: 1
> -        color: Theme.palette.normal.background
> -        clip: true
> -        anchors {
> -            left: parent.left
> -            right: parent.right
> -        }
> -        height: page.height
> -        y: height
> -        visible: !page.isCollapsed
> -        state: "collapsed"
> -        states: [
> -            State {
> -                name: "collapsed"
> -                PropertyChanges {
> -                    target: bottomEdge
> -                    y: bottomEdge.height
> -                }
> -            },
> -            State {
> -                name: "expanded"
> -                PropertyChanges {
> -                    target: bottomEdge
> -                    y: bottomEdge.pageStartY
> -                }
> -            },
> -            State {
> -                name: "floating"
> -                when: mouseArea.drag.active
> -                PropertyChanges {
> -                    target: shadow
> -                    opacity: 1.0
> -                }
> -            }
> -        ]
> -
> -        transitions: [
> -            Transition {
> -                to: "expanded"
> -                SequentialAnimation {
> -                    alwaysRunToEnd: true
> -
> -                    SmoothedAnimation {
> -                        target: bottomEdge
> -                        property: "y"
> -                        duration: UbuntuAnimation.FastDuration
> -                        easing.type: Easing.Linear
> -                    }
> -                    SmoothedAnimation {
> -                        target: edgeLoader
> -                        property: "anchors.topMargin"
> -                        to: - units.gu(4)
> -                        duration: UbuntuAnimation.FastDuration
> -                        easing.type: Easing.Linear
> -                    }
> -                    SmoothedAnimation {
> -                        target: edgeLoader
> -                        property: "anchors.topMargin"
> -                        to: 0
> -                        duration: UbuntuAnimation.FastDuration
> -                        easing: UbuntuAnimation.StandardEasing
> -                    }
> -                    ScriptAction {
> -                        script: page._pushPage()
> -                    }
> -                }
> -            },
> -            Transition {
> -                from: "expanded"
> -                to: "collapsed"
> -                SequentialAnimation {
> -                    alwaysRunToEnd: true
> -
> -                    ScriptAction {
> -                        script: {
> -                            Qt.inputMethod.hide()
> -                            edgeLoader.item.parent = edgeLoader
> -                            edgeLoader.item.anchors.fill = edgeLoader
> -                            edgeLoader.item.active = false
> -                        }
> -                    }
> -                    SmoothedAnimation {
> -                        target: bottomEdge
> -                        property: "y"
> -                        duration: UbuntuAnimation.SlowDuration
> -                    }
> -                    ScriptAction {
> -                        script: {
> -                            // destroy current bottom page
> -                            if (page.reloadBottomEdgePage) {
> -                                edgeLoader.active = false
> -                                // tip will receive focus on page active true
> -                            } else {
> -                                tip.forceActiveFocus()
> -                            }
> -
> -                            // notify
> -                            page.bottomEdgeDismissed()
> -
> -                            edgeLoader.active = true
> -                        }
> -                    }
> -                }
> -            },
> -            Transition {
> -                from: "floating"
> -                to: "collapsed"
> -                SmoothedAnimation {
> -                    target: bottomEdge
> -                    property: "y"
> -                    duration: UbuntuAnimation.FastDuration
> -                }
> -            }
> -        ]
> -
> -        Loader {
> -            id: edgeLoader
> -
> -            asynchronous: true
> -            anchors.fill: parent
> -            //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
> -            Binding {
> -                target: edgeLoader.status === Loader.Ready ? edgeLoader : null
> -                property: "anchors.topMargin"
> -                value:  edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
> -                when: !page.isReady
> -            }
> -
> -            onLoaded: {
> -                tip.forceActiveFocus()
> -                if (page.isReady && edgeLoader.item.active !== true) {
> -                    page._pushPage()
> -                }
> -            }
> -        }
> -    }
> -}
> 
> === removed file 'src/app/qml/PdfContentsPage.qml'
> --- src/app/qml/PdfContentsPage.qml	2015-02-05 16:40:16 +0000
> +++ src/app/qml/PdfContentsPage.qml	1970-01-01 00:00:00 +0000
> @@ -1,60 +0,0 @@
> -/*
> - * Copyright (C) 2014, 2015
> - *                  Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.0
> -import Ubuntu.Components 1.1
> -import QtQuick.Layouts 1.1
> -import Ubuntu.Components.ListItems 1.0 as ListItem
> -
> -Page {
> -    title: i18n.tr("Contents")
> -
> -    ListView {
> -        anchors.fill: parent
> -
> -        model: poppler.tocModel
> -
> -        delegate: ListItem.Base {
> -            showDivider: model.level == 0
> -
> -            onClicked: {
> -                pdfView.positionAtIndex(model.pageIndex);
> -                pageStack.pop();
> -            }
> -
> -            RowLayout {
> -                anchors.fill: parent
> -                anchors.leftMargin: units.gu(2) * model.level
> -
> -                spacing: units.gu(1)
> -
> -                Label {
> -                    Layout.fillWidth: true
> -
> -                    text: (typeof model.title === "undefined") ? "" : model.title;
> -                    elide: Text.ElideRight
> -                    Component.onCompleted: { if (model.level === 0); font.weight = Font.DemiBold; }
> -                }
> -
> -                Label {
> -                    text: (typeof model.pageIndex === "undefined") ? "" : model.pageIndex + 1;
> -                    Component.onCompleted: { if (model.level === 0); font.weight = Font.DemiBold; }
> -                }
> -            }
> -        }
> -    }
> -}
> 
> === removed file 'src/app/qml/PdfView.qml'
> --- src/app/qml/PdfView.qml	2015-02-09 12:00:58 +0000
> +++ src/app/qml/PdfView.qml	1970-01-01 00:00:00 +0000
> @@ -1,107 +0,0 @@
> -/*
> - * Copyright (C) 2013-2014 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import com.ubuntu.popplerqmlplugin 1.0 as PDF
> -
> -import "utils.js" as Utils
> -
> -PageWithBottomEdge {
> -    id: pdfPage
> -    title: Utils.getNameOfFile(file.path);
> -
> -    // Disable header auto-hide.
> -    // TODO: Show/hide header if a user taps the page
> -    flickable: null
> -
> -    property string currentPage: i18n.tr("Page %1 of %2").arg(pdfView.currentPageIndex + 1).arg(pdfView.count)
> -
> -    bottomEdgeTitle: i18n.tr("Contents")
> -    bottomEdgePageComponent: PdfContentsPage {}
> -    bottomEdgeEnabled: poppler.tocModel.count > 0
> -
> -    PDF.VerticalView {
> -        id: pdfView
> -        objectName: "pdfView"
> -        anchors.fill: parent
> -        spacing: units.gu(2)
> -
> -        clip: true
> -        boundsBehavior: Flickable.StopAtBounds
> -        flickDeceleration: 1500 * units.gridUnit / 8
> -        maximumFlickVelocity: 2500 * units.gridUnit / 8
> -
> -        contentWidth: parent.width * _zoomHelper.scale
> -        cacheBuffer: height * poppler.providersNumber * _zoomHelper.scale * 0.5
> -        interactive: !pinchy.pinch.active
> -
> -        model: poppler
> -        delegate: PdfViewDelegate {
> -            Component.onDestruction: QQuickView.releaseResources()
> -        }
> -
> -        // FIXME: On zooming, keep the same content position.
> -        PinchArea {
> -            id: pinchy
> -            anchors.fill: parent
> -
> -            pinch {
> -                target: _zoomHelper
> -                minimumScale: 1.0
> -                maximumScale: 2.5
> -            }
> -
> -            onPinchFinished: {
> -                pdfView.returnToBounds();
> -
> -                // This is a bit expensive, so it's safer to put it here.
> -                // It won't be called on desktop (where PinchArea is not used),
> -                // but it's not a problem at the moment (our target is phone).
> -                QQuickView.releaseResources();
> -            }
> -        }
> -
> -        Item { id: _zoomHelper }
> -    }
> -
> -    Scrollbar { flickableItem: pdfView }
> -    Scrollbar { flickableItem: pdfView; align: Qt.AlignBottom }
> -
> -    PDF.Document {
> -        id: poppler
> -
> -        property bool isLoading: true
> -
> -        Component.onCompleted: path = file.path
> -        onPagesLoaded: {
> -            isLoading = false;
> -
> -            var title = getDocumentInfo("Title")
> -            if (title !== "")
> -                pdfPage.title = title
> -        }
> -    }
> -
> -
> -    // *** HEADER ***
> -    state: "default"
> -    states: PdfViewDefaultHeader {
> -        name: "default"
> -        targetPage: pdfPage
> -        activityRunning: pdfView.currentPageItem.status == Image.Loading || poppler.isLoading
> -    }
> -}
> 
> === removed file 'src/app/qml/PdfViewDefaultHeader.qml'
> --- src/app/qml/PdfViewDefaultHeader.qml	2015-02-03 21:11:52 +0000
> +++ src/app/qml/PdfViewDefaultHeader.qml	1970-01-01 00:00:00 +0000
> @@ -1,96 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import QtQuick.Layouts 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -PageHeadState {
> -    id: rootItem
> -
> -    property Page targetPage
> -    property alias activityRunning: activity.running
> -
> -    head: targetPage.head
> -
> -    contents: RowLayout {
> -        anchors.fill: parent
> -        spacing: units.gu(1)
> -
> -        ActivityIndicator { id: activity }
> -
> -        Column {
> -            id: layout
> -            Layout.fillWidth: true
> -
> -            Label {
> -                width: parent.width
> -                //horizontalAlignment: Text.AlignHCenter
> -                elide: Text.ElideMiddle
> -
> -                font.weight: Font.DemiBold
> -                text: targetPage.title
> -            }
> -            Label {
> -                width: parent.width
> -                //horizontalAlignment: Text.AlignHCenter
> -                elide: Text.ElideMiddle
> -
> -                fontSize: "small"
> -                text: targetPage.currentPage
> -            }
> -        }
> -    }
> -
> -    backAction: Action {
> -        iconName: "back"
> -        text: (pageStack.depth > 1) ? i18n.tr("Back") : i18n.tr("Close")
> -        onTriggered: {
> -            if (pageStack.depth > 1) {
> -                // Go back to Welcome page
> -                pageStack.pop();
> -            } else {
> -                // File has been imported through Content Hub (or was not chosen through WelcomePage)
> -                // Close the application and show our source app (e.g. ubuntu-filemanager-app, if used to open a document)
> -                Qt.quit()
> -            }
> -        }
> -    }
> -
> -    actions: [
> -        Action {
> -            iconName: "search"
> -            // onTriggered: pageMain.state = "search"
> -            //Disable it until we provide search in Poppler plugin.
> -            enabled: false
> -        },
> -
> -        Action {
> -            objectName:"gotopage"
> -            iconName: "browser-tabs"
> -            text: "Go to page..."
> -            onTriggered: PopupUtils.open(Qt.resolvedUrl("PdfViewGotoDialog.qml"), targetPage)
> -        },
> -
> -        Action {
> -            objectName: "detailsAction"
> -            text: i18n.tr("Details")
> -            iconName: "info"
> -            onTriggered: pageStack.push(Qt.resolvedUrl("DetailsPage.qml"))
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/PdfViewDelegate.qml'
> --- src/app/qml/PdfViewDelegate.qml	2015-02-03 21:05:26 +0000
> +++ src/app/qml/PdfViewDelegate.qml	1970-01-01 00:00:00 +0000
> @@ -1,95 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -
> -Rectangle {
> -    id: pdfPage
> -
> -    property int index: model.index
> -    property bool _previewFetched: false
> -
> -    property alias status: pageImg.status
> -
> -    width: parent.width
> -    height: width * (model.height / model.width)
> -    color: "#E6E6E6"
> -
> -    // Preview page rendering. Used as placeholder while zooming the page.
> -    // We generate the low resolution preview from the texture of the PDF page,
> -    // so that we can keep page rendering as fast as possible.
> -    ShaderEffectSource {
> -        id: previewImg
> -        anchors.fill: parent
> -
> -        // We cannot change its opacity or visibility, otherwise the texture will be refreshed,
> -        // even if live is false.
> -        live: false
> -        textureSize: Qt.size(256, 256 * (model.height / model.width))
> -    }
> -
> -    Image {
> -        id: pageImg
> -        anchors.fill: parent
> -
> -        source: "image://poppler" + (index % poppler.providersNumber) + "/page/" + index;
> -        sourceSize.width: pdfPage.width
> -
> -        onStatusChanged: {
> -            // This is supposed to run the first time PdfViewDelegate gets the page rendering.
> -            if (!_previewFetched) {
> -                if (status == Image.Ready) {
> -                    previewImg.sourceItem = pageImg
> -                    // Re-assign sourceItem property, so the texture is not updated when Image status changes.
> -                    previewImg.sourceItem = pdfPage
> -                }
> -            }
> -        }
> -
> -        // Request a new page rendering. The order, which pages are requested with, depends on the distance from the currentPage
> -        Timer {
> -            id: _zoomTimer
> -            interval: {
> -                var diff = Math.abs(pdfView.currentPageIndex - model.index)
> -                var prov = poppler.providersNumber * 0.5
> -
> -                if (diff < prov)
> -                    return 0
> -                else
> -                    return (diff - prov) * 10
> -            }
> -
> -            onTriggered: {
> -                pageImg.sourceSize.width = pdfPage.width;
> -            }
> -        }
> -    }
> -
> -    // Page rendering depends on the width of PdfViewDelegate.
> -    // Because of this, we have multiple callings to ImageProvider while zooming.
> -    // Just avoid it.
> -    Connections {
> -        target: pinchy
> -
> -        onPinchStarted: _zoomTimer.stop();
> -        onPinchUpdated: {
> -            // This ensures that page image is not reloaded when the maximumScale or minimumScale has already been reached.
> -            if ( !(_zoomHelper.scale >= 2.5 && pinch.scale > 1.0) && !(_zoomHelper.scale <= 1.0 && pinch.scale < 1.0) )
> -                pageImg.sourceSize.width = 0;
> -        }
> -        onPinchFinished: _zoomTimer.restart();
> -    }
> -}
> 
> === removed file 'src/app/qml/PdfViewGotoDialog.qml'
> --- src/app/qml/PdfViewGotoDialog.qml	2015-01-30 19:37:00 +0000
> +++ src/app/qml/PdfViewGotoDialog.qml	1970-01-01 00:00:00 +0000
> @@ -1,60 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -Dialog {
> -    id: goToPageDialog
> -    objectName:"PdfViewGotoDialog"
> -
> -    title: i18n.tr("Go to page")
> -    text: i18n.tr("Choose a page between 1 and %1").arg(pdfView.count)
> -
> -    TextField {
> -        id: goToPageTextField
> -        objectName:"goToPageTextField"
> -
> -        width: parent.width
> -
> -        hasClearButton: true
> -        inputMethodHints: Qt.ImhFormattedNumbersOnly
> -        validator: IntValidator{ bottom: 1; top: pdfView.count }
> -
> -        Keys.onReturnPressed: goToPage()
> -        Component.onCompleted: forceActiveFocus()
> -    }
> -
> -    Button {
> -        objectName:"GOButton"
> -        text: i18n.tr("GO!")
> -        color: UbuntuColors.orange
> -
> -        enabled: goToPageTextField.acceptableInput
> -        onClicked: goToPage()
> -    }
> -
> -    Button {
> -        text: i18n.tr("Cancel")
> -        onClicked: PopupUtils.close(goToPageDialog)
> -    }
> -
> -    function goToPage() {
> -        pdfView.positionAtIndex((goToPageTextField.text - 1))
> -        PopupUtils.close(goToPageDialog)
> -    }
> -}
> 
> === removed file 'src/app/qml/TextView.qml'
> --- src/app/qml/TextView.qml	2015-01-30 19:44:52 +0000
> +++ src/app/qml/TextView.qml	1970-01-01 00:00:00 +0000
> @@ -1,70 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import Ubuntu.Components.Themes.Ambiance 0.1
> -
> -import "utils.js" as Utils
> -
> -Page {
> -    id: textPage
> -    title: Utils.getNameOfFile(file.path);
> -
> -    TextArea {
> -        id: textAreaMain
> -        objectName: "textAreaMain"
> -
> -        property bool isLoading: true
> -
> -        anchors.fill: parent
> -
> -        // FIXME: If set to true, some of the keyboard hooks are disabled
> -        // And it's not possible to move the cursor with arrow keys.
> -        readOnly: true
> -
> -        text: i18n.tr("Loading...")
> -        font.family: "UbuntuMono"
> -
> -        Component.onCompleted: {
> -            var xhr = new XMLHttpRequest;
> -
> -            xhr.open("GET", file.path);
> -            xhr.onreadystatechange = function() {
> -                if (xhr.readyState === XMLHttpRequest.DONE) {
> -                    textAreaMain.text = xhr.responseText;
> -                    textAreaMain.isLoading = false
> -                }
> -            };
> -
> -            xhr.send();
> -        }
> -
> -        style: TextAreaStyle {
> -            background: Rectangle { color: "white" }
> -        }
> -    }
> -
> -    // *** HEADER ***
> -    state: "default"
> -    states: [
> -        TextViewDefaultHeader {
> -            name: "default"
> -            targetPage: textPage
> -            activityRunning: textAreaMain.isLoading
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/TextViewDefaultHeader.qml'
> --- src/app/qml/TextViewDefaultHeader.qml	2015-02-03 21:11:52 +0000
> +++ src/app/qml/TextViewDefaultHeader.qml	1970-01-01 00:00:00 +0000
> @@ -1,82 +0,0 @@
> -/*
> - * Copyright (C) 2014-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import QtQuick.Layouts 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -PageHeadState {
> -    id: rootItem
> -
> -    property Page targetPage
> -    property alias activityRunning: activity.running
> -
> -    head: targetPage.head
> -
> -    contents: RowLayout {
> -        anchors.fill: parent
> -        spacing: units.gu(1)
> -
> -        ActivityIndicator { id: activity }
> -
> -        Column {
> -            id: layout
> -            Layout.fillWidth: true
> -
> -            Label {
> -                width: parent.width
> -                //horizontalAlignment: Text.AlignHCenter
> -                elide: Text.ElideMiddle
> -
> -                font.weight: Font.DemiBold
> -                text: targetPage.title
> -            }
> -            Label {
> -                width: parent.width
> -                //horizontalAlignment: Text.AlignHCenter
> -                elide: Text.ElideMiddle
> -
> -                fontSize: "small"
> -                text: file.description
> -            }
> -        }
> -    }
> -
> -    backAction: Action {
> -        iconName: "back"
> -        text: (pageStack.depth > 1) ? i18n.tr("Back") : i18n.tr("Close")
> -        onTriggered: {
> -            if (pageStack.depth > 1) {
> -                // Go back to Welcome page
> -                pageStack.pop();
> -            } else {
> -                // File has been imported through Content Hub (or was not chosen through WelcomePage)
> -                // Close the application and show our source app (e.g. ubuntu-filemanager-app, if used to open a document)
> -                Qt.quit()
> -            }
> -        }
> -    }
> -
> -    actions: [
> -        Action {
> -            objectName: "detailsAction"
> -            text: i18n.tr("Details")
> -            iconName: "info"
> -            onTriggered: pageStack.push(Qt.resolvedUrl("DetailsPage.qml"))
> -        }
> -    ]
> -}
> 
> === removed file 'src/app/qml/UnknownTypeDialog.qml'
> --- src/app/qml/UnknownTypeDialog.qml	2015-01-29 18:09:26 +0000
> +++ src/app/qml/UnknownTypeDialog.qml	1970-01-01 00:00:00 +0000
> @@ -1,43 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.3
> -import Ubuntu.Components 1.1
> -import Ubuntu.Components.Popups 1.0
> -
> -import "loadComponent.js" as LoadComponent
> -
> -Dialog {
> -    id: unknownDialog
> -    objectName: "unknownDialog"
> -    title: i18n.tr("Unknown file type")
> -    text: i18n.tr("Sorry but we can't find a way to display this file. Do you want to open it as a plain text?")
> -    Button {
> -        text: i18n.tr("Yes")
> -        color: UbuntuColors.green
> -
> -        onClicked: {
> -            LoadComponent.load("text/plain");
> -            PopupUtils.close(unknownDialog)
> -        }
> -    }
> -    Button {
> -        text: i18n.tr("No")
> -        color: UbuntuColors.red
> -        onClicked: PopupUtils.close(unknownDialog)
> -    }
> -}
> -
> 
> === removed file 'src/app/qml/WelcomePage.qml'
> --- src/app/qml/WelcomePage.qml	2015-01-29 16:24:50 +0000
> +++ src/app/qml/WelcomePage.qml	1970-01-01 00:00:00 +0000
> @@ -1,42 +0,0 @@
> -/*
> - * Copyright (C) 2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.0
> -import Ubuntu.Components 1.1
> -
> -Page {
> -    id: welcomePage
> -
> -    title: i18n.tr("Document Viewer")
> -    head.actions: [ openAction ]
> -
> -    EmptyState {
> -        title: i18n.tr("No opened documents")
> -        subTitle: i18n.tr("Tap the + icon to open a document")
> -
> -        iconName: "edit-copy"
> -
> -        anchors.centerIn: parent
> -    }
> -
> -    Action {
> -        id: openAction
> -        text: i18n.tr("Open a file...")
> -        iconName: "add"
> -
> -        onTriggered: pageStack.push(Qt.resolvedUrl("ContentHubPicker.qml"))
> -    }
> -}
> 
> === removed file 'src/app/qml/ZoomableImage.qml'
> --- src/app/qml/ZoomableImage.qml	2014-11-08 10:59:51 +0000
> +++ src/app/qml/ZoomableImage.qml	1970-01-01 00:00:00 +0000
> @@ -1,155 +0,0 @@
> -/*
> - * Copyright (C) 2014 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -import QtQuick 2.0
> -import Ubuntu.Components 0.1
> -
> -/*! \brief Zoomable for image.
> -
> -    This widget shows image contained in source,
> -    can be zoomable accordingly with zoomable.
> - */
> -
> -Item {
> -    id: root
> -    property alias source: imageRenderer.source
> -    property var zoomable: false
> -    property alias imageStatus: imageRenderer.status
> -    property alias asynchronous: imageRenderer.asynchronous
> -
> -    Flickable {
> -        id: flickable
> -        objectName: "flickable"
> -        clip: true // FIXME maybe we can remove this, or just not clip in few cases
> -        contentHeight: imageContainer.height
> -        contentWidth: imageContainer.width
> -
> -        onHeightChanged: image.resetScale()
> -        onWidthChanged: image.resetScale()
> -        anchors.fill: parent
> -
> -        Item {
> -            id: imageContainer
> -            objectName: "imageContainer"
> -            width: Math.max(image.width * image.scale, flickable.width)
> -            height: Math.max(image.height * image.scale, flickable.height)
> -
> -            Item {
> -                id: image
> -                objectName: "image"
> -                property alias imageStatus: imageRenderer.status
> -                property var prevScale
> -                anchors.centerIn: parent
> -
> -                signal imageReloaded
> -
> -                Image {
> -                    id: imageRenderer
> -                    objectName: "imageRenderer"
> -                    smooth: !flickable.movingVertically
> -                    anchors.fill: parent
> -                    fillMode: Image.PreserveAspectFit
> -
> -                    readonly property int sourceSizeMultiplier: 3
> -
> -                    sourceSize.width: root.width * sourceSizeMultiplier <= root.height * sourceSizeMultiplier ? root.width * sourceSizeMultiplier : 0
> -                    sourceSize.height: root.height * sourceSizeMultiplier <= root.width * sourceSizeMultiplier ? root.height * sourceSizeMultiplier : 0
> -
> -                    onStatusChanged: {
> -                        if (status === Image.Ready) {
> -                            image.imageReloaded();
> -                        }
> -                    }
> -                }
> -
> -                onImageReloaded: {
> -                    image.height = imageRenderer.implicitHeight
> -                    image.width = imageRenderer.implicitWidth
> -                    image.resetScale();
> -                }
> -
> -                function resetScale() {
> -                    image.scale = Math.min(flickable.width / image.width, flickable.height / image.height);
> -                    pinchArea.minScale = image.scale;
> -                    prevScale = Math.min(image.scale, 1);
> -                }
> -
> -                onScaleChanged: {
> -                    var currentWidth = width * scale
> -                    var currentHeight = height * scale
> -                    var scaleRatio = scale / prevScale
> -                    if (currentWidth > flickable.width) {
> -                        var xpos = flickable.width / 2 + flickable.contentX;
> -                        var xoff = xpos * scaleRatio;
> -                        flickable.contentX = xoff - flickable.width / 2;
> -                    }
> -                    if (currentHeight > flickable.height) {
> -                        var ypos = flickable.height / 2 + flickable.contentY;
> -                        var yoff = ypos * scaleRatio;
> -                        flickable.contentY = yoff - flickable.height / 2;
> -                    }
> -                    prevScale = scale;
> -                }
> -            }
> -        }
> -
> -        PinchArea {
> -            id: pinchArea
> -            objectName: "pinchArea"
> -            property real minScale: 1.0
> -            anchors.fill: parent
> -            enabled: zoomable ? zoomable : false
> -
> -            pinch.target: image
> -            pinch.minimumScale: minScale
> -            pinch.maximumScale: 10
> -
> -            onPinchFinished: flickable.returnToBounds()
> -        }
> -
> -        MouseArea {
> -            id: mouseArea
> -            objectName: "mouseArea"
> -
> -            anchors.fill: parent
> -            enabled: zoomable ? zoomable : false
> -
> -            onWheel: {
> -                var startScale = image.scale;
> -                if (wheel.angleDelta.y > 0) {
> -                    image.scale = startScale + 0.1;
> -                } else if (wheel.angleDelta.y < 0) {
> -                    if (image.scale > 0.1 && image.scale > pinchArea.minScale) {
> -                        image.scale = startScale - 0.1;
> -                    }
> -                }
> -                wheel.accepted = true;
> -            }
> -
> -            onPressed: {
> -                mouse.accepted = false;
> -            }
> -
> -            onReleased: {
> -                mouse.accepted = false;
> -            }
> -
> -            onClicked: {
> -                mouse.accepted = false;
> -            }
> -        }
> -    }
> -}
> 
> === added directory 'src/app/qml/common'
> === added file 'src/app/qml/common/DetailsPage.qml'
> --- src/app/qml/common/DetailsPage.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/common/DetailsPage.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,58 @@
> +/*
> + * Copyright (C) 2013-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.ListItems 1.0 as ListItem
> +
> +import "utils.js" as Utils
> +
> +Page {
> +    id: detailsPage
> +    objectName: "detailsPage"
> +
> +    title: i18n.tr("Details")
> +
> +    Column {
> +        width: parent.width
> +
> +        ListItem.Subtitled {
> +            text: i18n.tr("Location")
> +            subText: file.path
> +        }
> +        ListItem.Subtitled {
> +            text: i18n.tr("Size")
> +            subText: Utils.printSize(file.size)
> +        }
> +
> +        ListItem.Subtitled {
> +            text: i18n.tr("Created")
> +            subText: file.creationTime.toLocaleString(Qt.locale())
> +        }
> +
> +        ListItem.Subtitled {
> +            text: i18n.tr("Last modified")
> +            subText: file.lastModified.toLocaleString(Qt.locale())
> +        }
> +
> +        ListItem.Subtitled {
> +            id: mimetypeItem
> +            objectName: "mimetypeItem"
> +            text: i18n.tr("MIME type")
> +            subText: file.mimetype
> +        }
> +    }
> +}
> 
> === added file 'src/app/qml/common/ErrorDialog.qml'
> --- src/app/qml/common/ErrorDialog.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/common/ErrorDialog.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,31 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +Dialog {
> +    id: errorDialog
> +    title: i18n.tr("Error")
> +    text: i18n.tr("File does not exist")

I think you should make this file more generic, adding a text property (maybe with actual text as default value) and a 'type' property, so app could use this dialog for every error, just passing the right error message and the right type, and changing the visible buttons basing on type

> +
> +    Button {
> +        text: i18n.tr("Close")
> +        color: UbuntuColors.red
> +        onClicked: PopupUtils.close(errorDialog)
> +    }
> +}
> 
> === added file 'src/app/qml/common/UnknownTypeDialog.qml'
> --- src/app/qml/common/UnknownTypeDialog.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/common/UnknownTypeDialog.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,43 @@
> +/*
> + * Copyright (C) 2013-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +import "loadComponent.js" as LoadComponent
> +
> +Dialog {
> +    id: unknownDialog
> +    objectName: "unknownDialog"
> +    title: i18n.tr("Unknown file type")
> +    text: i18n.tr("Sorry but we can't find a way to display this file. Do you want to open it as a plain text?")
> +    Button {
> +        text: i18n.tr("Yes")
> +        color: UbuntuColors.green
> +
> +        onClicked: {
> +            LoadComponent.load("text/plain");
> +            PopupUtils.close(unknownDialog)
> +        }
> +    }
> +    Button {
> +        text: i18n.tr("No")
> +        color: UbuntuColors.red
> +        onClicked: PopupUtils.close(unknownDialog)
> +    }
> +}
> +
> 
> === added file 'src/app/qml/common/loadComponent.js'
> --- src/app/qml/common/loadComponent.js	1970-01-01 00:00:00 +0000
> +++ src/app/qml/common/loadComponent.js	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,38 @@
> +/*
> + * Copyright (C) 2013-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +function load(mimetype) {
> +    var qmlToLoad = "";
> +
> +    // Open all text files in text editor
> +    // With that fix it is possible to open LICENSE file
> +    // which was recognised as text/x-pascal
> +    if (mimetype.substring(0, 5) === "text/")
> +        qmlToLoad = Qt.resolvedUrl("../textView/TextView.qml");
> +
> +    // Check if PDF document
> +    if (mimetype === "application/pdf")
> +        qmlToLoad = Qt.resolvedUrl("../pdfView/PdfView.qml");
> +
> +    if (qmlToLoad != "") {
> +       pageStack.push(qmlToLoad);
> +    } else {
> +        console.debug("Unknown MIME type: "+ mimetype);
> +        runUnknownTypeDialog();
> +    }
> +
> +    return mimetype;
> +}
> 
> === added file 'src/app/qml/common/utils.js'
> --- src/app/qml/common/utils.js	1970-01-01 00:00:00 +0000
> +++ src/app/qml/common/utils.js	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright (C) 2013-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +.pragma library
> +
> +function printSize(size) {
> +    if (size >= 1073741824)
> +        return (size / 1073741824).toFixed(2) + " GiB";
> +
> +    if (size >= 1048576)
> +        return (size / 1048576).toFixed(2) + " MiB";
> +
> +    if (size >= 1024)
> +        return parseInt(size / 1024) + " KiB";
> +
> +    return size + " byte";

Shouldn't has to be translable?

We have also Arabic/Chinese support, and I'm pretty sure they have different characters

> +};
> +
> +function getNameOfFile(path) {
> +    return path.toString().substring(path.lastIndexOf('/') + 1);
> +}

Choose if you want to have ';' after a function (as printSize function) or not. 
I suggest to remove it

> 
> === added directory 'src/app/qml/documentPage'
> === added file 'src/app/qml/documentPage/DeleteFileDialog.qml'
> --- src/app/qml/documentPage/DeleteFileDialog.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DeleteFileDialog.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,59 @@
> +/*
> +  Copyright (C) 2013-2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation, either version 3 of the License, or
> +  (at your option) any later version.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +Dialog {
> +    id: deleteFileDialog
> +
> +    property string path
> +
> +    title: path ? i18n.tr("Delete file") :
> +                  i18n.tr("Delete %1 files").arg(documentPage.view.item.selectedItems.count)
> +    text: path ? i18n.tr("Are you sure you want to permanently delete this file?") :
> +                 i18n.tr("Are you sure you want to permanently delete these files?")
> +
> +    Button {
> +        text: i18n.tr("Cancel")
> +        onClicked: PopupUtils.close(deleteFileDialog)
> +    }
> +
> +    Button {
> +        text: i18n.tr("Delete")
> +        color: UbuntuColors.red
> +
> +        onClicked: {
> +            if (deleteFileDialog.path) {
> +                folderModel.rm(path)
> +            } else {
> +                var items = documentPage.view.item.selectedItems;
> +
> +                for (var i=0; i < items.count; i++) {
> +                    console.log("Removing:", items.get(i).model.path);
> +                    folderModel.rm(items.get(i).model.path);
> +                }
> +
> +                viewLoader.item.endSelection();

This shouldn't be out of the if/else?

You have to endSelection also if user selected only 1 file IMO

> +            }
> +
> +            PopupUtils.close(deleteFileDialog)
> +        }
> +    }
> +}
> +
> 
> === added file 'src/app/qml/documentPage/DocumentEmptyState.qml'
> --- src/app/qml/documentPage/DocumentEmptyState.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentEmptyState.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,34 @@
> +/*
> +  Copyright (C) 2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import "../upstreamComponents"

Why?

> +
> +Item {
> +    anchors.fill: parent
> +
> +    EmptyState {
> +        title: i18n.tr("No document found")
> +
> +        // TODO: Add "or insert removable media with documents." to subTitle when
> +        // the support for SD card will be implemented.
> +        subTitle: i18n.tr("Connect your device to any computer and simply drag files to the Documents folder.")
> +        iconName: "edit-copy"
> +
> +        anchors.centerIn: parent
> +        width: parent.width
> +    }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentGridDelegate.qml'
> --- src/app/qml/documentPage/DocumentGridDelegate.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentGridDelegate.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,179 @@
> +/*
> +  Copyright (C) 2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +
> +import "../common/utils.js" as Utils
> +
> +AbstractButton {
> +    id: root
> +    property bool selected: false
> +    property bool selectionMode: false
> +
> +    function formattedDateTime() {
> +        var date = new Date(model.date)
> +        var diff = model.dateDiff
> +
> +        if (diff < 1)
> +            return i18n.tr("Today, ") + Qt.formatDateTime(date, "hh:mm")

You should make translable also "hh:mm", and add a comment for translators to explain what it rapresents, maybe in other languages they use different time format

> +
> +        if (diff < 2)
> +            return i18n.tr("Yesterday, ") + Qt.formatDateTime(date, "hh:mm")

Same as above

> +
> +        if (diff < 7)
> +            return Qt.formatDateTime(date, "dddd, hh:mm")

Same as above

> +
> +        return Qt.formatDateTime(date, "dd-MM-yyyy hh:mm")

Same as above

> +    }
> +
> +    Rectangle {
> +        anchors { fill: parent; margins: units.gu(0.5) }
> +
> +        color: Qt.lighter(UbuntuColors.lightGrey)
> +        clip: true
> +
> +        Loader {
> +            id: selectionIcon
> +
> +            anchors {
> +                right: parent.right
> +                top: parent.top
> +            }
> +
> +            z: 10
> +
> +            width: (status === Loader.Ready) ? item.implicitWidth : 0
> +            visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
> +            Behavior on opacity {
> +                NumberAnimation {
> +                    duration: UbuntuAnimation.SnapDuration
> +                }
> +            }
> +        }
> +
> +        // Document mimetype icon
> +        Icon {
> +            anchors.centerIn: parent
> +            anchors.verticalCenterOffset: - units.gu(2)
> +
> +            width: units.gu(8); height: width
> +
> +            // At the moment the suru icon theme doesn't have much icons.
> +            // Just some note for the future:
> +            // TODO: Add icons for Office/ODF documents
> +            // TODO: Whenever there will be icons for source code files, add them.
> +            name: {
> +                if (model.mimetype.substring(0, 5) === "text/")
> +                    return "text-x-generic-symbolic"
> +
> +                if (model.mimetype.substring(0, 5) === "image")
> +                    return "image-x-generic-symbolic"
> +
> +                if (model.mimetype === "application/pdf")
> +                    return "application-pdf-symbolic"
> +
> +                return "package-x-generic-symbolic"
> +            }
> +        }
> +
> +        // Cover
> +       /* Image {
> +            anchors.fill: parent
> +
> +            source: {
> +                if (model.cover !== "" && typeof model.cover !== "undefined")
> +                    return model.cover
> +
> +                if (model.mimetype.toString().indexOf("image") !== -1)
> +                    return model.path
> +
> +                return ""
> +            }
> +
> +            sourceSize.width: width
> +            fillMode: Image.PreserveAspectCrop
> +        }*/
> +
> +        // Document info overlay
> +        Rectangle {
> +            id: overlay
> +
> +            anchors {
> +                left: parent.left
> +                right: parent.right
> +                bottom: parent.bottom
> +            }
> +
> +            height: units.gu(6)
> +
> +            color: UbuntuColors.darkGrey
> +            opacity: 0.75
> +        }
> +
> +        // Document info
> +        Column {
> +            anchors { fill: overlay; margins: units.gu(0.5) }
> +
> +            RowLayout {

Why you use a Row with only one element inside?

> +                width: parent.width

Please use anchors, if possible, because they have better performances than width/height when the window is resized

> +                spacing: units.gu(1)
> +
> +                Label {
> +                    text: model.name
> +                    color: "white"
> +
> +                    elide: Text.ElideRight
> +                    font.weight: Font.DemiBold
> +                    fontSize: "small"
> +
> +                    Layout.fillWidth: true
> +                }
> +            }
> +
> +            RowLayout {
> +                width: parent.width
> +
> +                Label {
> +                    text: formattedDateTime()
> +                    color: "white"                  
> +                    fontSize: "small"
> +
> +                    Layout.fillWidth: true
> +                }
> +
> +                Label {
> +                    text: Utils.printSize(model.size)
> +                    color: "white"
> +                    fontSize: "small"
> +                }
> +            }
> +        }   // Document info end
> +
> +        states: [
> +            State {
> +                name: "select"
> +                when: selectionMode || selected
> +                PropertyChanges {
> +                    target: selectionIcon
> +                    source: Qt.resolvedUrl("../upstreamComponents/ListItemWithActionsCheckBox.qml")
> +                    anchors.margins: units.gu(1)
> +                }
> +            }
> +        ]
> +    }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentGridView.qml'
> --- src/app/qml/documentPage/DocumentGridView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentGridView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,76 @@
> +/*
> +  Copyright (C) 2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +
> +import "../upstreamComponents"
> +
> +MultipleSelectionGridView {
> +    id: documentGridView
> +
> +    // We use mainView.width to calculate the size and the spacing of elements.
> +    // That's because this GridView always fills (and always will) the whole size of MainView.
> +    // By this way, we can avoid binding loops, keeping the code pretty simple.
> +    anchors {
> +        fill: parent
> +        margins: units.gu(0.5)
> +        leftMargin: (mainView.width % cellWidth) * 0.5
> +        rightMargin: (mainView.width % cellWidth) * 0.5
> +    }
> +    clip: true
> +
> +    cellHeight: cellWidth
> +    cellWidth: (mainView.width > units.gu(50)) ? units.gu(24)
> +                                               : (mainView.width - units.gu(2)) * 0.5
> +
> +    listDelegate: DocumentGridDelegate {
> +        id: delegate
> +        width: cellWidth
> +        height: cellHeight
> +
> +        selectionMode: documentGridView.isInSelectionMode
> +        selected: documentGridView.isSelected(delegate)
> +
> +        onClicked: {
> +            if(documentGridView.isInSelectionMode) {
> +                if(!documentGridView.selectItem(delegate)) {
> +                    documentGridView.deselectItem(delegate)
> +                }
> +                return
> +            }
> +            else {
> +                file.path = model.path
> +            }
> +        }
> +
> +        onPressAndHold: {
> +            if (!documentGridView.isInSelectionMode) {
> +                documentGridView.startSelection()
> +                documentGridView.selectItem(delegate)
> +            }
> +        }
> +    }
> +
> +    listModel: folderModel
> +
> +    Scrollbar {
> +        flickableItem: documentGridView
> +        parent: documentGridView.parent
> +    }
> +
> +    Component.onCompleted: { if (DOC_VIEWER.pickModeEnabled) documentGridView.startSelection(); }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentListDelegate.qml'
> --- src/app/qml/documentPage/DocumentListDelegate.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentListDelegate.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,105 @@
> +/*
> +  Copyright (C) 2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +
> +import "../common/utils.js" as Utils
> +import "../upstreamComponents"
> +
> +ListItemWithActions {
> +    function formattedDateTime() {
> +        var date = new Date(model.date)
> +        var diff = model.dateDiff
> +
> +        if (diff < 2)
> +            return Qt.formatDateTime(date, "hh:mm")
> +
> +        if (diff < 7)
> +            return Qt.formatDateTime(date, "dddd, hh:mm")
> +
> +        return Qt.formatDateTime(date, "dd-MM-yyyy hh:mm")
> +    }
> +
> +    width: parent.width

Use anchors please

> +    height: units.gu(8)
> +
> +

Remove newline

> +    locked: documentPage.state == "pickMode"
> +
> +    // TODO: NEEDS-DESIGN: Enable left action. Still need to find an equivalent for GridDelegate.
> +   /* leftSideAction: Action {
> +        iconName: "delete"
> +        text: i18n.tr("Delete")
> +        onTriggered: {
> +            PopupUtils.open(Qt.resolvedUrl("DeleteFileDialog.qml"),
> +                            documentPage, { path: model.filePath })
> +        }
> +    }*/
> +
> +    contents: RowLayout {
> +        anchors.fill: parent
> +        spacing: units.gu(2)
> +
> +        Icon {
> +            width: height
> +            height: units.gu(5)
> +
> +            // At the moment the suru icon theme doesn't have much icons.
> +            name: {
> +                if (model.mimetype.substring(0, 5) === "text/")
> +                    return "text-x-generic-symbolic"
> +
> +                if (model.mimetype.substring(0, 5) === "image")
> +                    return "image-x-generic-symbolic"
> +
> +                if (model.mimetype === "application/pdf")
> +                    return "application-pdf-symbolic"
> +
> +                return "package-x-generic-symbolic"
> +            }
> +        }
> +
> +        Column {
> +            Layout.fillWidth: true
> +
> +            Label {
> +                text: model.name
> +                wrapMode: Text.Wrap
> +                width: parent.width
> +
> +                color: UbuntuColors.midAubergine
> +            }
> +
> +            RowLayout {
> +                width: parent.width
> +
> +                Label {
> +                    text: formattedDateTime()
> +                    fontSize: "small"
> +
> +                    Layout.fillWidth: true
> +                }
> +
> +                Label {
> +                    text: Utils.printSize(model.size)
> +                    fontSize: "small"
> +                }
> +            }
> +        }
> +    }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentListView.qml'
> --- src/app/qml/documentPage/DocumentListView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentListView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,157 @@
> +/*
> +  Copyright (C) 2015 Stefano Verzegnassi
> +
> +    This program is free software: you can redistribute it and/or modify
> +  it under the terms of the GNU General Public License 3 as published by
> +  the Free Software Foundation.
> +
> +    This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.ListItems 1.0 as ListItem
> +
> +import "../upstreamComponents"
> +
> +MultipleSelectionListView {
> +    id: documentListView
> +
> +    anchors { fill: parent; margins: units.gu(0.5) }
> +    clip: true
> +
> +    property var _currentSwipedItem: null
> +
> +    function _updateSwipeState(item)
> +    {
> +        if (item.swipping) {
> +            return
> +        }
> +
> +        if (item.swipeState !== "Normal") {
> +            if (documentListView._currentSwipedItem !== item) {
> +                if (documentListView._currentSwipedItem) {
> +                    documentListView._currentSwipedItem.resetSwipe()
> +                }
> +                documentListView._currentSwipedItem = item
> +            }
> +        } else if (item.swipeState !== "Normal"
> +                   && documentListView._currentSwipedItem === item) {
> +            documentListView._currentSwipedItem = null
> +        }
> +    }
> +
> +    listDelegate: DocumentListDelegate {
> +        id: delegate
> +
> +        property var removalAnimation
> +
> +        function remove() {
> +            removalAnimation.start()
> +        }
> +
> +        selectionMode: documentListView.isInSelectionMode
> +        selected: documentListView.isSelected(delegate)
> +
> +        onSwippingChanged: {
> +            _updateSwipeState(delegate)
> +        }
> +
> +        onSwipeStateChanged: {
> +            _updateSwipeState(delegate)
> +        }
> +
> +        ListView.onRemove: ScriptAction {
> +            script: {
> +                if (_currentSwipedItem
> +                        === delegate) {
> +                    _currentSwipedItem = null
> +                }
> +            }
> +        }
> +
> +        removalAnimation: SequentialAnimation {
> +            alwaysRunToEnd: true
> +
> +            PropertyAction {
> +                target: delegate
> +                property: "ListView.delayRemove"
> +                value: true
> +            }
> +
> +            UbuntuNumberAnimation {
> +                target: delegate
> +                property: "height"
> +                to: 0
> +            }
> +
> +            PropertyAction {
> +                target: delegate
> +                property: "ListView.delayRemove"
> +                value: false
> +            }
> +
> +            ScriptAction {
> +                script: {
> +                    var filePath = d.folderModel.get(index, "filePath")
> +                    Storage.rm(filePath)
> +                }
> +            }
> +        }
> +
> +        onItemClicked: {
> +            if(documentListView.isInSelectionMode) {
> +                if(!documentListView.selectItem(delegate)) {
> +                    documentListView.deselectItem(delegate)
> +                }
> +                return
> +            }
> +
> +            else {
> +                file.path = model.path
> +            }
> +        }
> +
> +        onItemPressAndHold: {
> +            if (!documentListView.isInSelectionMode) {
> +                documentListView.startSelection()
> +                documentListView.selectItem(delegate)
> +            }
> +        }
> +    }
> +
> +    listModel: folderModel
> +
> +    section.property: "dateDiff"
> +    section.delegate: ListItem.Header {
> +        text: {
> +            if (section == 0)
> +                return i18n.tr("Today")
> +
> +            if (section == 1)
> +                return i18n.tr("Yesterday")
> +
> +            if (section == 2)
> +                return i18n.tr("Earlier this week")
> +
> +            if (section == 3)
> +                return i18n.tr("Earlier this month")
> +
> +            if (section > 3)

You don't need this if, just return

> +                return i18n.tr("Even more earlier...")
> +        }
> +    }
> +
> +    Scrollbar {
> +        flickableItem: documentListView
> +        parent: documentListView.parent
> +    }
> +
> +    Component.onCompleted: { if (DOC_VIEWER.pickModeEnabled) documentListView.startSelection(); }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentPage.qml'
> --- src/app/qml/documentPage/DocumentPage.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentPage.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,76 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0
> +import Qt.labs.settings 1.0
> +
> +Page {
> +    id: documentPage
> +
> +    title: i18n.tr("Document Viewer")
> +    flickable: null
> +
> +    property bool useGridView: false
> +
> +    Settings {
> +        property alias useGridView: documentPage.useGridView
> +    }
> +
> +    property alias view: viewLoader
> +    Loader {
> +        id: viewLoader
> +        anchors.fill: parent
> +
> +        source: (folderModel.count === 0) ? Qt.resolvedUrl("./DocumentEmptyState.qml")
> +                                     : useGridView ? Qt.resolvedUrl("./DocumentGridView.qml")
> +                                                   : Qt.resolvedUrl("./DocumentListView.qml")
> +    }
> +
> +    // *** HEADER ***
> +    states: [
> +        DocumentPageDefaultHeader {
> +            name: "default"
> +            targetPage: documentPage
> +            when: !mainView.pickMode && !viewLoader.item.isInSelectionMode
> +        },
> +
> +        DocumentPagePickModeHeader {
> +            name: "pickMode"
> +            targetPage: documentPage
> +            when: mainView.pickMode
> +        },
> +
> +        DocumentPageSelectionModeHeader {
> +            name: "selection"
> +            targetPage: documentPage
> +            when: !mainView.pickMode && viewLoader.item.isInSelectionMode
> +        }
> +    ]
> +
> +    Connections {
> +        target: DOC_VIEWER
> +
> +        onPickModeEnabledChanged: {
> +            if (DOC_VIEWER.pickModeEnabled) {
> +                viewLoader.item.startSelection()
> +            } else {
> +                viewLoader.item.cancelSelection()
> +            }
> +        }
> +    }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentPageDefaultHeader.qml'
> --- src/app/qml/documentPage/DocumentPageDefaultHeader.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentPageDefaultHeader.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.Popups 1.0

Why?

> +
> +PageHeadState {
> +    id: rootItem
> +
> +    property Page targetPage
> +    head: targetPage.head
> +
> +    actions:  Action {
> +        id: switchView
> +        text: targetPage.useGridView ? i18n.tr("Switch to single column list") : i18n.tr("Switch to grid")
> +        iconName: targetPage.useGridView ? "view-list-symbolic" : "view-grid-symbolic"
> +        onTriggered: targetPage.useGridView = !targetPage.useGridView
> +
> +        visible: folderModel.count !== 0
> +    }
> +}
> 
> === added file 'src/app/qml/documentPage/DocumentPagePickModeHeader.qml'
> --- src/app/qml/documentPage/DocumentPagePickModeHeader.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentPagePickModeHeader.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,65 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +PageHeadState {
> +    id: rootItem
> +
> +    property Page targetPage
> +    head: targetPage.head
> +
> +    backAction: Action {
> +        text: i18n.tr("Cancel")
> +        objectName: "cancelButton"
> +        iconName: "close"
> +        onTriggered: DOC_VIEWER.contentPickingCanceled()
> +    }
> +
> +    actions: [
> +        Action {
> +            text: targetPage.useGridView ? i18n.tr("Switch to single column list") : i18n.tr("Switch to grid")
> +            iconName: targetPage.useGridView ? "view-list-symbolic" : "view-grid-symbolic"
> +            onTriggered: targetPage.useGridView = !targetPage.useGridView
> +
> +            visible: folderModel.count !== 0
> +        },
> +
> +        Action {
> +            text: i18n.tr("Pick")
> +            objectName: "pickButton"
> +            enabled: viewLoader.item.selectedItems.count > 0

I think you should add visible: enabled

> +            iconName: "ok"
> +            onTriggered: {
> +                if (!enabled)
> +                    return;
> +
> +                var urlList = []
> +                var items = documentPage.view.item.selectedItems;
> +
> +                for (var i=0; i < items.count; i++) {
> +                    urlList.push(items.get(i).model.path);
> +                }
> +
> +                DOC_VIEWER.returnPickedContent(urlList);
> +            }
> +        }
> +    ]
> +}
> +
> 
> === added file 'src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml'
> --- src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/documentPage/DocumentPageSelectionModeHeader.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,96 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +import "../upstreamComponents"
> +
> +PageHeadState {
> +    id: rootItem
> +
> +    property Page targetPage
> +
> +    head: targetPage.head
> +
> +    backAction: Action {
> +        iconName: "close"
> +        text: i18n.tr("Close")
> +        onTriggered: {
> +            viewLoader.item.cancelSelection()
> +        }
> +    }
> +
> +    contents: Loader {
> +        id: selectionStateLoader
> +        active: documentPage.state === "selection"
> +        sourceComponent: Item {
> +            HeaderButton {
> +                id: selectButton
> +
> +                anchors {
> +                    right: deleteButton.left
> +                    rightMargin: units.gu(1)
> +                }
> +
> +                text: {
> +                    if(viewLoader.item.selectedItems.count === viewLoader.item.count) {
> +                        return i18n.tr("Select None")
> +                    } else {
> +                        return i18n.tr("Select All")
> +                    }
> +                }
> +
> +                iconSource: {
> +                    if(viewLoader.item.selectedItems.count === viewLoader.item.count) {
> +                        return Qt.resolvedUrl("../../graphics/select-none.svg")
> +                    } else {
> +                        return Qt.resolvedUrl("../../graphics/select.svg")
> +                    }
> +                }
> +
> +                onTriggered: {
> +                    if(viewLoader.item.selectedItems.count === viewLoader.item.count) {
> +                        viewLoader.item.clearSelection()
> +                    } else {
> +                        viewLoader.item.selectAll()
> +                    }
> +                }
> +            }
> +
> +            HeaderButton {
> +                id: deleteButton
> +
> +                anchors.right: parent.right
> +                anchors.rightMargin: units.gu(2)
> +
> +                iconName: "delete"
> +                text: i18n.tr("Delete")
> +                enabled: viewLoader.item.selectedItems.count !== 0
> +
> +                onTriggered: {
> +                    PopupUtils.open(Qt.resolvedUrl("DeleteFileDialog.qml"), documentPage)
> +                }
> +            }
> +        }
> +
> +        height: parent ? parent.height : undefined

Maybe 0 is better than undefined here?

> +        anchors.right: parent ? parent.right: undefined
> +    }
> +
> +}
> 
> === removed file 'src/app/qml/loadComponent.js'
> --- src/app/qml/loadComponent.js	2015-01-29 18:45:21 +0000
> +++ src/app/qml/loadComponent.js	1970-01-01 00:00:00 +0000
> @@ -1,45 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -function load(mimetype) {
> -    var qmlToLoad = "";
> -
> -    // Open all text files in text editor
> -    // With that fix it is possible to open LICENSE file
> -    // which was recognised as text/x-pascal
> -    if (mimetype.substring(0, 5) === "text/")
> -        qmlToLoad = "TextView";
> -
> -    // Check if image
> -    if (mimetype === "image/jpeg" || mimetype === "image/png" ||
> -            mimetype === "image/gif" || mimetype === "image/tiff" ||
> -            mimetype === "image/x-icon" || mimetype === "image/x-ms-bmp" ||
> -            mimetype === "image/svg+xml")
> -        qmlToLoad = "ImageView";
> -
> -    // Check if PDF document
> -    if (mimetype === "application/pdf")
> -        qmlToLoad = "PdfView";
> -
> -    if (qmlToLoad != "") {
> -       pageStack.push(Qt.resolvedUrl(qmlToLoad + ".qml"));
> -    } else {
> -        console.debug("Unknown MIME type: "+ mimetype);
> -        runUnknownTypeDialog();
> -    }
> -
> -    return mimetype;
> -}
> 
> === added directory 'src/app/qml/pdfView'
> === added file 'src/app/qml/pdfView/PdfContentsPage.qml'
> --- src/app/qml/pdfView/PdfContentsPage.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/pdfView/PdfContentsPage.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,60 @@
> +/*
> + * Copyright (C) 2014, 2015
> + *                  Stefano Verzegnassi <verzegnassi.stefano@xxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.0
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.ListItems 1.0 as ListItem
> +
> +Page {
> +    title: i18n.tr("Contents")
> +
> +    ListView {
> +        anchors.fill: parent
> +
> +        model: poppler.tocModel
> +
> +        delegate: ListItem.Base {
> +            showDivider: model.level == 0
> +
> +            onClicked: {
> +                pdfView.positionAtIndex(model.pageIndex);
> +                pageStack.pop();
> +            }
> +
> +            RowLayout {
> +                anchors.fill: parent
> +                anchors.leftMargin: units.gu(2) * model.level
> +
> +                spacing: units.gu(1)
> +
> +                Label {
> +                    Layout.fillWidth: true
> +
> +                    text: (typeof model.title === "undefined") ? "" : model.title;
> +                    elide: Text.ElideRight
> +                    Component.onCompleted: { if (model.level === 0); font.weight = Font.DemiBold; }
> +                }
> +
> +                Label {
> +                    text: (typeof model.pageIndex === "undefined") ? "" : model.pageIndex + 1;
> +                    Component.onCompleted: { if (model.level === 0); font.weight = Font.DemiBold; }
> +                }
> +            }
> +        }
> +    }
> +}
> 
> === added file 'src/app/qml/pdfView/PdfView.qml'
> --- src/app/qml/pdfView/PdfView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/pdfView/PdfView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,107 @@
> +/*
> + * Copyright (C) 2013-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import com.ubuntu.popplerqmlplugin 1.0 as PDF
> +
> +import "../common/utils.js" as Utils
> +import "../upstreamComponents"
> +
> +PageWithBottomEdge {
> +    id: pdfPage
> +    title: Utils.getNameOfFile(file.path);
> +
> +    // Disable header auto-hide.
> +    // TODO: Show/hide header if a user taps the page
> +    flickable: null
> +
> +    property string currentPage: i18n.tr("Page %1 of %2").arg(pdfView.currentPageIndex + 1).arg(pdfView.count)

Maybe adding a comment for translators here?

> +
> +    bottomEdgeTitle: i18n.tr("Contents")
> +    bottomEdgePageComponent: PdfContentsPage {}
> +    bottomEdgeEnabled: poppler.tocModel.count > 0
> +
> +    PDF.VerticalView {
> +        id: pdfView
> +        objectName: "pdfView"
> +        anchors.fill: parent
> +        spacing: units.gu(2)
> +
> +        clip: true
> +        boundsBehavior: Flickable.StopAtBounds
> +        flickDeceleration: 1500 * units.gridUnit / 8
> +        maximumFlickVelocity: 2500 * units.gridUnit / 8
> +
> +        contentWidth: parent.width * _zoomHelper.scale
> +        cacheBuffer: height * poppler.providersNumber * _zoomHelper.scale * 0.5
> +        interactive: !pinchy.pinch.active
> +
> +        model: poppler
> +        delegate: PdfViewDelegate {
> +            Component.onDestruction: DOC_VIEWER.releaseResources()
> +        }
> +
> +        // FIXME: On zooming, keep the same content position.
> +        PinchArea {
> +            id: pinchy
> +            anchors.fill: parent
> +
> +            pinch {
> +                target: _zoomHelper
> +                minimumScale: 1.0
> +                maximumScale: 2.5
> +            }
> +
> +            onPinchFinished: {
> +                pdfView.returnToBounds();
> +
> +                // This is a bit expensive, so it's safer to put it here.
> +                // It won't be called on desktop (where PinchArea is not used),
> +                // but it's not a problem at the moment (our target is phone).
> +                DOC_VIEWER.releaseResources();
> +            }
> +        }
> +
> +        Item { id: _zoomHelper }
> +    }
> +
> +    Scrollbar { flickableItem: pdfView }
> +    Scrollbar { flickableItem: pdfView; align: Qt.AlignBottom }
> +
> +    PDF.Document {
> +        id: poppler
> +
> +        property bool isLoading: true
> +
> +        Component.onCompleted: path = file.path
> +        onPagesLoaded: {
> +            isLoading = false;
> +
> +            var title = getDocumentInfo("Title")
> +            if (title !== "")
> +                pdfPage.title = title
> +        }
> +    }
> +
> +    // *** HEADER ***
> +    state: "default"
> +    states: PdfViewDefaultHeader {
> +        name: "default"
> +        targetPage: pdfPage
> +        activityRunning: pdfView.currentPageItem.status == Image.Loading || poppler.isLoading
> +    }
> +}
> 
> === added file 'src/app/qml/pdfView/PdfViewDefaultHeader.qml'
> --- src/app/qml/pdfView/PdfViewDefaultHeader.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/pdfView/PdfViewDefaultHeader.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,96 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +PageHeadState {
> +    id: rootItem
> +
> +    property Page targetPage
> +    property alias activityRunning: activity.running
> +
> +    head: targetPage.head
> +
> +    contents: RowLayout {
> +        anchors.fill: parent
> +        spacing: units.gu(1)
> +
> +        ActivityIndicator { id: activity }
> +
> +        Column {
> +            id: layout
> +            Layout.fillWidth: true
> +
> +            Label {
> +                width: parent.width
> +                //horizontalAlignment: Text.AlignHCenter
> +                elide: Text.ElideMiddle
> +
> +                font.weight: Font.DemiBold
> +                text: targetPage.title
> +            }
> +            Label {
> +                width: parent.width
> +                //horizontalAlignment: Text.AlignHCenter
> +                elide: Text.ElideMiddle
> +
> +                fontSize: "small"
> +                text: targetPage.currentPage
> +            }
> +        }
> +    }
> +
> +    backAction: Action {
> +        iconName: "back"
> +        text: (pageStack.depth > 1) ? i18n.tr("Back") : i18n.tr("Close")
> +        onTriggered: {
> +            if (pageStack.depth > 1) {
> +                // Go back to Welcome page
> +                pageStack.pop();
> +            } else {
> +                // File has been imported through Content Hub (or was not chosen through WelcomePage)
> +                // Close the application and show our source app (e.g. ubuntu-filemanager-app, if used to open a document)
> +                Qt.quit()
> +            }
> +        }
> +    }
> +
> +    actions: [
> +        Action {
> +            iconName: "search"
> +            // onTriggered: pageMain.state = "search"
> +            //Disable it until we provide search in Poppler plugin.
> +            enabled: false
> +        },
> +
> +        Action {
> +            objectName:"gotopage"
> +            iconName: "browser-tabs"
> +            text: "Go to page..."
> +            onTriggered: PopupUtils.open(Qt.resolvedUrl("PdfViewGotoDialog.qml"), targetPage)
> +        },
> +
> +        Action {
> +            objectName: "detailsAction"
> +            text: i18n.tr("Details")
> +            iconName: "info"
> +            onTriggered: pageStack.push(Qt.resolvedUrl("../common/DetailsPage.qml"))
> +        }
> +    ]
> +}
> 
> === added file 'src/app/qml/pdfView/PdfViewDelegate.qml'
> --- src/app/qml/pdfView/PdfViewDelegate.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/pdfView/PdfViewDelegate.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,95 @@
> +/*
> + * Copyright (C) 2013-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +
> +Rectangle {
> +    id: pdfPage
> +
> +    property int index: model.index
> +    property bool _previewFetched: false
> +
> +    property alias status: pageImg.status
> +
> +    width: parent.width
> +    height: width * (model.height / model.width)
> +    color: "#E6E6E6"
> +
> +    // Preview page rendering. Used as placeholder while zooming the page.
> +    // We generate the low resolution preview from the texture of the PDF page,
> +    // so that we can keep page rendering as fast as possible.
> +    ShaderEffectSource {
> +        id: previewImg
> +        anchors.fill: parent
> +
> +        // We cannot change its opacity or visibility, otherwise the texture will be refreshed,
> +        // even if live is false.
> +        live: false
> +        textureSize: Qt.size(256, 256 * (model.height / model.width))
> +    }
> +
> +    Image {
> +        id: pageImg
> +        anchors.fill: parent
> +
> +        source: "image://poppler" + (index % poppler.providersNumber) + "/page/" + index;
> +        sourceSize.width: pdfPage.width
> +
> +        onStatusChanged: {
> +            // This is supposed to run the first time PdfViewDelegate gets the page rendering.
> +            if (!_previewFetched) {
> +                if (status == Image.Ready) {
> +                    previewImg.sourceItem = pageImg
> +                    // Re-assign sourceItem property, so the texture is not updated when Image status changes.
> +                    previewImg.sourceItem = pdfPage
> +                }
> +            }
> +        }
> +
> +        // Request a new page rendering. The order, which pages are requested with, depends on the distance from the currentPage
> +        Timer {
> +            id: _zoomTimer
> +            interval: {
> +                var diff = Math.abs(pdfView.currentPageIndex - model.index)
> +                var prov = poppler.providersNumber * 0.5
> +
> +                if (diff < prov)
> +                    return 0
> +                else
> +                    return (diff - prov) * 10
> +            }
> +
> +            onTriggered: {
> +                pageImg.sourceSize.width = pdfPage.width;
> +            }
> +        }
> +    }
> +
> +    // Page rendering depends on the width of PdfViewDelegate.
> +    // Because of this, we have multiple callings to ImageProvider while zooming.
> +    // Just avoid it.
> +    Connections {
> +        target: pinchy
> +
> +        onPinchStarted: _zoomTimer.stop();
> +        onPinchUpdated: {
> +            // This ensures that page image is not reloaded when the maximumScale or minimumScale has already been reached.
> +            if ( !(_zoomHelper.scale >= 2.5 && pinch.scale > 1.0) && !(_zoomHelper.scale <= 1.0 && pinch.scale < 1.0) )
> +                pageImg.sourceSize.width = 0;
> +        }
> +        onPinchFinished: _zoomTimer.restart();
> +    }
> +}
> 
> === added file 'src/app/qml/pdfView/PdfViewGotoDialog.qml'
> --- src/app/qml/pdfView/PdfViewGotoDialog.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/pdfView/PdfViewGotoDialog.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,60 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +Dialog {
> +    id: goToPageDialog
> +    objectName:"PdfViewGotoDialog"
> +
> +    title: i18n.tr("Go to page")
> +    text: i18n.tr("Choose a page between 1 and %1").arg(pdfView.count)
> +
> +    TextField {
> +        id: goToPageTextField
> +        objectName:"goToPageTextField"
> +
> +        width: parent.width
> +
> +        hasClearButton: true
> +        inputMethodHints: Qt.ImhFormattedNumbersOnly
> +        validator: IntValidator{ bottom: 1; top: pdfView.count }
> +
> +        Keys.onReturnPressed: goToPage()
> +        Component.onCompleted: forceActiveFocus()
> +    }
> +
> +    Button {
> +        objectName:"GOButton"
> +        text: i18n.tr("GO!")
> +        color: UbuntuColors.orange
> +
> +        enabled: goToPageTextField.acceptableInput
> +        onClicked: goToPage()
> +    }
> +
> +    Button {
> +        text: i18n.tr("Cancel")
> +        onClicked: PopupUtils.close(goToPageDialog)
> +    }
> +
> +    function goToPage() {
> +        pdfView.positionAtIndex((goToPageTextField.text - 1))
> +        PopupUtils.close(goToPageDialog)
> +    }
> +}
> 
> === added directory 'src/app/qml/textView'
> === added file 'src/app/qml/textView/TextView.qml'
> --- src/app/qml/textView/TextView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/textView/TextView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,70 @@
> +/*
> + * Copyright (C) 2013-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Themes.Ambiance 0.1
> +
> +import "../common/utils.js" as Utils
> +
> +Page {
> +    id: textPage
> +    title: Utils.getNameOfFile(file.path);
> +
> +    TextArea {
> +        id: textAreaMain
> +        objectName: "textAreaMain"
> +
> +        property bool isLoading: true
> +
> +        anchors.fill: parent
> +
> +        // FIXME: If set to true, some of the keyboard hooks are disabled
> +        // And it's not possible to move the cursor with arrow keys.
> +        readOnly: true
> +
> +        text: i18n.tr("Loading...")
> +        font.family: "UbuntuMono"
> +
> +        Component.onCompleted: {
> +            var xhr = new XMLHttpRequest;
> +
> +            xhr.open("GET", file.path);
> +            xhr.onreadystatechange = function() {
> +                if (xhr.readyState === XMLHttpRequest.DONE) {
> +                    textAreaMain.text = xhr.responseText;
> +                    textAreaMain.isLoading = false
> +                }
> +            };
> +
> +            xhr.send();
> +        }
> +
> +        style: TextAreaStyle {
> +            background: Rectangle { color: "white" }
> +        }
> +    }
> +
> +    // *** HEADER ***
> +    state: "default"
> +    states: [
> +        TextViewDefaultHeader {
> +            name: "default"
> +            targetPage: textPage
> +            activityRunning: textAreaMain.isLoading
> +        }
> +    ]
> +}
> 
> === added file 'src/app/qml/textView/TextViewDefaultHeader.qml'
> --- src/app/qml/textView/TextViewDefaultHeader.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/textView/TextViewDefaultHeader.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,82 @@
> +/*
> + * Copyright (C) 2014-2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import QtQuick.Layouts 1.1
> +import Ubuntu.Components.Popups 1.0
> +
> +PageHeadState {
> +    id: rootItem
> +
> +    property Page targetPage
> +    property alias activityRunning: activity.running
> +
> +    head: targetPage.head
> +
> +    contents: RowLayout {
> +        anchors.fill: parent
> +        spacing: units.gu(1)
> +
> +        ActivityIndicator { id: activity }
> +
> +        Column {
> +            id: layout
> +            Layout.fillWidth: true
> +
> +            Label {
> +                width: parent.width
> +                //horizontalAlignment: Text.AlignHCenter
> +                elide: Text.ElideMiddle
> +
> +                font.weight: Font.DemiBold
> +                text: targetPage.title
> +            }
> +            Label {
> +                width: parent.width
> +                //horizontalAlignment: Text.AlignHCenter
> +                elide: Text.ElideMiddle
> +
> +                fontSize: "small"
> +                text: file.description
> +            }
> +        }
> +    }
> +
> +    backAction: Action {
> +        iconName: "back"
> +        text: (pageStack.depth > 1) ? i18n.tr("Back") : i18n.tr("Close")
> +        onTriggered: {
> +            if (pageStack.depth > 1) {
> +                // Go back to Welcome page
> +                pageStack.pop();
> +            } else {
> +                // File has been imported through Content Hub (or was not chosen through WelcomePage)
> +                // Close the application and show our source app (e.g. ubuntu-filemanager-app, if used to open a document)
> +                Qt.quit()
> +            }
> +        }
> +    }
> +
> +    actions: [
> +        Action {
> +            objectName: "detailsAction"
> +            text: i18n.tr("Details")
> +            iconName: "info"
> +            onTriggered: pageStack.push(Qt.resolvedUrl("../common/DetailsPage.qml"))
> +        }
> +    ]
> +}
> 
> === modified file 'src/app/qml/ubuntu-docviewer-app.qml'
> --- src/app/qml/ubuntu-docviewer-app.qml	2015-01-29 19:14:08 +0000
> +++ src/app/qml/ubuntu-docviewer-app.qml	2015-02-27 15:45:56 +0000
> @@ -18,8 +18,9 @@
>  import Ubuntu.Components 1.1
>  import Ubuntu.Components.Popups 1.0
>  import com.ubuntu.fileqmlplugin 1.0
> +import DocumentViewer 1.0
>  
> -import "loadComponent.js" as LoadComponent
> +import "common/loadComponent.js" as LoadComponent
>  
>  MainView {
>      id: mainView
> @@ -27,49 +28,74 @@
>  
>      applicationName: "com.ubuntu.docviewer"
>      useDeprecatedToolbar: false
> -    
> +
> +    property bool pickMode: DOC_VIEWER.pickModeEnabled
> +
>      width: units.gu(50)
>      height: units.gu(75)
>  
> +    function openDocument(path)  {
> +        if (path !== "") {
> +            console.log("Path of the document:", path)
> +
> +            // If a document is already shown, pop() its page.
> +            while (pageStack.depth > 1)
> +                pageStack.pop();
> +
> +            path = path.replace("file://", "")
> +                       .replace("document://", "");
> +
> +            file.path = path;
> +        }
> +    }
> +
> +    function runUnknownTypeDialog() {
> +        PopupUtils.open(Qt.resolvedUrl("common/UnknownTypeDialog.qml"), mainView, { parent: mainView });
> +    }
> +
> +    Component.onCompleted: {
> +        pageStack.push(Qt.resolvedUrl("documentPage/DocumentPage.qml"));
> +
> +        // Open the document, if one has been specified.
> +        openDocument(DOC_VIEWER.documentFile);
> +    }
> +
>      File {
>          id: file
>          objectName: "file"
>  
>          onMimetypeChanged: LoadComponent.load(mimetype)
> -        onErrorChanged: { if (error == -1); PopupUtils.open(Qt.resolvedUrl("ErrorDialog.qml"), mainView, { parent: mainView }) }
> -    }
> -
> -    Component.onCompleted: {
> -        // Check if a value has been specified for "documentPath" argument.
> -        // The value for the argument is parsed in main.cpp.
> -
> -        if (documentPath) {
> -            // If so, send the path to the File plugin and load the document.
> -            console.log("Path argument is:", documentPath);
> -            file.path = documentPath;
> -        } else {
> -            // Otherwise, push a welcome screen in the stack.
> -            pageStack.push(Qt.resolvedUrl("WelcomePage.qml"));
> -        }
> -    }
> -
> -    // Content Importer
> -    // Used when user asks to open a document from ContentHub.
> -    Loader {
> -        id: contentHubLoader
> -
> -        asynchronous: true
> -        source: Qt.resolvedUrl("ContentHubProxy.qml")
> -        onStatusChanged: {
> -            if (status === Loader.Ready) {
> -                item.pageStack = pageStack
> -            }
> -        }
> -    }
> -
> -    function runUnknownTypeDialog() {
> -        PopupUtils.open(Qt.resolvedUrl("UnknownTypeDialog.qml"), mainView, { parent: mainView });
> -    }
> -
> +        onErrorChanged: { if (error == -1); PopupUtils.open(Qt.resolvedUrl("common/ErrorDialog.qml"), mainView, { parent: mainView }) }
> +    }
> +
> +    DocumentsModel { id: folderModel }
>      PageStack { id: pageStack }
> +
> +    Connections {
> +        target: UriHandler
> +        onOpened: {
> +            for (var i = 0; i < uris.length; ++i) {
> +                DOC_VIEWER.parseUri(uris[i])
> +            }
> +        }
> +    }
> +
> +    Connections {
> +        target: DOC_VIEWER
> +
> +        onDocumentFileChanged: {
> +            openDocument(DOC_VIEWER.documentFile);
> +        }
> +
> +        onPickModeEnabledChanged: {
> +            mainView.pickMode = DOC_VIEWER.pickModeEnabled
> +
> +            if (mainView.pickMode) {
> +                // If a document is loaded, pop() its page.
> +                while (pageStack.depth > 1) {
> +                    pageStack.pop()
> +                }
> +            }
> +        }
> +    }
>  }
> 
> === added directory 'src/app/qml/upstreamComponents'
> === added file 'src/app/qml/upstreamComponents/EmptyState.qml'
> --- src/app/qml/upstreamComponents/EmptyState.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/EmptyState.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (C) 2014 Canonical Ltd
> + *
> + * This file is part of Ubuntu Clock App
> + *
> + * Ubuntu Clock App is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 3 as
> + * published by the Free Software Foundation.
> + *
> + * Ubuntu Clock App is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +
> +/*
> + Component which displays an empty state (approved by design). It offers an
> + icon, title and subtitle to describe the empty state.
> +*/
> +
> +Item {
> +    id: emptyState
> +
> +    // Public APIs
> +    property alias iconName: emptyIcon.name
> +    property alias title: emptyLabel.text
> +    property alias subTitle: emptySublabel.text
> +
> +    height: childrenRect.height
> +
> +    Icon {
> +        id: emptyIcon
> +        anchors.horizontalCenter: parent.horizontalCenter
> +        height: units.gu(10)
> +        width: height
> +        color: "#BBBBBB"
> +    }
> +
> +    Label {
> +        id: emptyLabel
> +        anchors.top: emptyIcon.bottom
> +        anchors.topMargin: units.gu(5)
> +        anchors.horizontalCenter: parent.horizontalCenter
> +        fontSize: "large"
> +        font.bold: true
> +    }
> +
> +    Label {
> +        id: emptySublabel
> +        anchors.top: emptyLabel.bottom
> +
> +        width: parent.width
> +        wrapMode: Text.Wrap
> +        horizontalAlignment: Text.AlignHCenter
> +    }
> +}
> 
> === added file 'src/app/qml/upstreamComponents/HeaderButton.qml'
> --- src/app/qml/upstreamComponents/HeaderButton.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/HeaderButton.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,65 @@
> +/*
> + * Copyright (C) 2014 Canonical Ltd
> + *
> + * This file is part of Ubuntu Clock App
> + *
> + * Ubuntu Clock App is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 3 as
> + * published by the Free Software Foundation.
> + *
> + * Ubuntu Clock App is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +
> +AbstractButton {
> +    id: headerButton
> +
> +    property alias iconSource: _icon.source
> +    property alias iconName: _icon.name
> +    property alias text: _label.text
> +
> +    width: units.gu(6)
> +    height: parent ? parent.height : undefined
> +
> +    Rectangle {
> +        anchors.fill: parent
> +        visible: headerButton.pressed
> +        color: Theme.palette.selected.background
> +    }
> +
> +    Column {
> +        id: buttonHolder
> +
> +        width: _label.width
> +        height: childrenRect.height
> +
> +        spacing: units.gu(0.2)
> +        anchors.centerIn: parent
> +        anchors.verticalCenterOffset: units.gu(0.3)
> +
> +        Icon {
> +            id: _icon
> +            color: UbuntuColors.darkGrey
> +            width: units.gu(2.5)
> +            height: width
> +            opacity: headerButton.enabled ? 1.0 : 0.3
> +            anchors.horizontalCenter: parent.horizontalCenter
> +        }
> +
> +        Label {
> +            id: _label
> +            color: UbuntuColors.darkGrey
> +            fontSize: "xx-small"
> +            opacity: headerButton.enabled ? 1.0 : 0.3
> +            anchors.horizontalCenter: _icon.horizontalCenter
> +        }
> +    }
> +}
> 
> === added file 'src/app/qml/upstreamComponents/ListItemWithActions.qml'
> --- src/app/qml/upstreamComponents/ListItemWithActions.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/ListItemWithActions.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,453 @@
> +/*
> + * Copyright (C) 2012-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +
> +Item {
> +    id: root
> +
> +    property Action leftSideAction: null
> +    property list<Action> rightSideActions
> +    property double defaultHeight: units.gu(8)
> +    property bool locked: false
> +    property Action activeAction: null
> +    property var activeItem: null
> +    property bool triggerActionOnMouseRelease: false
> +    property color color: Theme.palette.normal.background
> +    property color selectedColor: "#E6E6E6"
> +    property bool selected: false
> +    property bool selectionMode: false
> +    property alias internalAnchors: mainContents.anchors
> +    default property alias contents: mainContents.children
> +
> +    readonly property double actionWidth: units.gu(4)
> +    readonly property double leftActionWidth: units.gu(10)
> +    readonly property double actionThreshold: actionWidth * 0.4
> +    readonly property double threshold: 0.4
> +    readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft"
> +    readonly property alias swipping: mainItemMoving.running
> +    readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
> +
> +    /* internal */
> +    property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
> +
> +    signal itemClicked(var mouse)
> +    signal itemPressAndHold(var mouse)
> +
> +    function returnToBoundsRTL(direction)
> +    {
> +        var actionFullWidth = actionWidth + units.gu(2)
> +
> +        // go back to normal state if swipping reverse
> +        if (direction === "LTR") {
> +            updatePosition(0)
> +            return
> +        } else if (!triggerActionOnMouseRelease) {
> +            updatePosition(-rightActionsView.width + units.gu(2))
> +            return
> +        }
> +
> +        var xOffset = Math.abs(main.x)
> +        var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
> +        var newX = 0
> +      if (index === _visibleRightSideActions.length) {
> +            newX = -(rightActionsView.width - units.gu(2))
> +        } else if (index >= 1) {
> +            newX = -(actionFullWidth * index)
> +        }
> +        updatePosition(newX)
> +    }
> +
> +    function returnToBoundsLTR(direction)
> +    {
> +        var finalX = leftActionWidth
> +        if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
> +            finalX = 0
> +        updatePosition(finalX)
> +    }
> +
> +    function returnToBounds(direction)
> +    {
> +        if (main.x < 0) {
> +            returnToBoundsRTL(direction)
> +        } else if (main.x > 0) {
> +            returnToBoundsLTR(direction)
> +        } else {
> +            updatePosition(0)
> +        }
> +    }
> +
> +    function contains(item, point, marginX)
> +    {
> +        var itemStartX = item.x - marginX
> +        var itemEndX = item.x + item.width + marginX
> +        return (point.x >= itemStartX) && (point.x <= itemEndX) &&
> +               (point.y >= item.y) && (point.y <= (item.y + item.height));
> +    }
> +
> +    function getActionAt(point)
> +    {
> +        if (contains(leftActionView, point, 0)) {
> +            return leftSideAction
> +        } else if (contains(rightActionsView, point, 0)) {
> +            var newPoint = root.mapToItem(rightActionsView, point.x, point.y)
> +            for (var i = 0; i < rightActionsRepeater.count; i++) {
> +                var child = rightActionsRepeater.itemAt(i)
> +                if (contains(child, newPoint, units.gu(1))) {
> +                    return i
> +                }
> +            }
> +        }
> +        return -1
> +    }
> +
> +    function updateActiveAction()
> +    {
> +        if (triggerActionOnMouseRelease &&
> +            (main.x <= -(root.actionWidth + units.gu(2))) &&
> +            (main.x > -(rightActionsView.width - units.gu(2)))) {
> +            var actionFullWidth = actionWidth + units.gu(2)
> +            var xOffset = Math.abs(main.x)
> +            var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
> +            index = index - 1
> +            if (index > -1) {
> +                root.activeItem = rightActionsRepeater.itemAt(index)
> +                root.activeAction = root._visibleRightSideActions[index]
> +            }
> +        } else {
> +            root.activeAction = null
> +        }
> +    }
> +
> +    function resetSwipe()
> +    {
> +        updatePosition(0)
> +    }
> +
> +    function filterVisibleActions(actions)
> +    {
> +        var visibleActions = []
> +        for(var i = 0; i < actions.length; i++) {
> +            var action = actions[i]
> +            if (action.visible) {
> +                visibleActions.push(action)
> +            }
> +        }
> +        return visibleActions
> +    }
> +
> +    function updatePosition(pos)
> +    {
> +        if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
> +            mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
> +        } else {
> +            mouseArea.state = ""
> +        }
> +        main.x = pos
> +    }
> +
> +    states: [
> +        State {
> +            name: "select"
> +            when: selectionMode || selected
> +            PropertyChanges {
> +                target: selectionIcon
> +                source: Qt.resolvedUrl("ListItemWithActionsCheckBox.qml")
> +                anchors.leftMargin: units.gu(2)
> +            }
> +            PropertyChanges {
> +                target: root
> +                locked: true
> +            }
> +            PropertyChanges {
> +                target: main
> +                x: 0
> +            }
> +        }
> +    ]
> +
> +    height: defaultHeight
> +    clip: height !== defaultHeight
> +
> +    Rectangle {
> +        id: leftActionView
> +
> +        anchors {
> +            top: parent.top
> +            bottom: parent.bottom
> +            right: main.left
> +        }
> +        width: root.leftActionWidth + actionThreshold
> +        visible: leftSideAction
> +        color: UbuntuColors.red
> +
> +        Icon {
> +            anchors {
> +                centerIn: parent
> +                horizontalCenterOffset: actionThreshold / 2
> +            }
> +            name: leftSideAction && _showActions ? leftSideAction.iconName : ""
> +            color: Theme.palette.selected.field
> +            height: units.gu(3)
> +            width: units.gu(3)
> +        }
> +    }
> +
> +    Rectangle {
> +       id: rightActionsView
> +
> +       anchors {
> +           top: main.top
> +           left: main.right
> +           bottom: main.bottom
> +       }
> +       visible: _visibleRightSideActions.length > 0
> +       width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(2) : 0
> +       color: "white"
> +       Row {
> +           anchors{
> +               top: parent.top
> +               left: parent.left
> +               leftMargin: units.gu(2)
> +               right: parent.right
> +               rightMargin: units.gu(2)
> +               bottom: parent.bottom
> +           }
> +           spacing: units.gu(2)
> +           Repeater {
> +               id: rightActionsRepeater
> +
> +               model: _showActions ? _visibleRightSideActions : []
> +               Item {
> +                   property alias image: img
> +
> +                   height: rightActionsView.height
> +                   width: root.actionWidth
> +
> +                   Icon {
> +                       id: img
> +
> +                       anchors.centerIn: parent
> +                       width: units.gu(3)
> +                       height: units.gu(3)
> +                       name: modelData.iconName
> +                       color: root.activeAction === modelData ? UbuntuColors.lightAubergine : UbuntuColors.lightGrey
> +                   }
> +              }
> +           }
> +       }
> +    }
> +
> +
> +    Rectangle {
> +        id: main
> +        objectName: "mainItem"
> +
> +        anchors {
> +            top: parent.top
> +            bottom: parent.bottom
> +        }
> +
> +        width: parent.width
> +        color: root.selected ? root.selectedColor : root.color
> +
> +        Loader {
> +            id: selectionIcon
> +
> +            anchors {
> +                left: main.left
> +                verticalCenter: main.verticalCenter
> +            }
> +            width: (status === Loader.Ready) ? item.implicitWidth : 0
> +            visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
> +            Behavior on width {
> +                NumberAnimation {
> +                    duration: UbuntuAnimation.SnapDuration
> +                }
> +            }
> +        }
> +
> +
> +        Item {
> +            id: mainContents
> +
> +            anchors {
> +                left: selectionIcon.right
> +                leftMargin: units.gu(2)
> +                top: parent.top
> +                topMargin: units.gu(1)
> +                right: parent.right
> +                rightMargin: units.gu(2)
> +                bottom: parent.bottom
> +                bottomMargin: units.gu(1)
> +            }
> +        }
> +
> +        Behavior on x {
> +            UbuntuNumberAnimation {
> +                id: mainItemMoving
> +
> +                easing.type: Easing.OutElastic
> +                duration: UbuntuAnimation.SlowDuration
> +            }
> +        }
> +        Behavior on color {
> +           ColorAnimation {}
> +        }
> +    }
> +
> +    SequentialAnimation {
> +        id: triggerAction
> +
> +        property var currentItem: root.activeItem ? root.activeItem.image : null
> +
> +        running: false
> +        ParallelAnimation {
> +            UbuntuNumberAnimation {
> +                target: triggerAction.currentItem
> +                property: "opacity"
> +                from: 1.0
> +                to: 0.0
> +                duration: UbuntuAnimation.SlowDuration
> +                easing {type: Easing.InOutBack; }
> +            }
> +            UbuntuNumberAnimation {
> +                target: triggerAction.currentItem
> +                properties: "width, height"
> +                from: units.gu(3)
> +                to: root.actionWidth
> +                duration: UbuntuAnimation.SlowDuration
> +                easing {type: Easing.InOutBack; }
> +            }
> +        }
> +        PropertyAction {
> +            target: triggerAction.currentItem
> +            properties: "width, height"
> +            value: units.gu(3)
> +        }
> +        PropertyAction {
> +            target: triggerAction.currentItem
> +            properties: "opacity"
> +            value: 1.0
> +        }
> +        ScriptAction {
> +            script: {
> +                root.activeAction.triggered(root)
> +                mouseArea.state = ""
> +            }
> +        }
> +        PauseAnimation {
> +            duration: 500
> +        }
> +        UbuntuNumberAnimation {
> +            target: main
> +            property: "x"
> +            to: 0
> +
> +        }
> +    }
> +
> +    MouseArea {
> +        id: mouseArea
> +
> +        property bool locked: root.locked || ((root.leftSideAction === null) && (root._visibleRightSideActions.count === 0))
> +        property bool manual: false
> +        property string direction: "None"
> +        property real lastX: -1
> +
> +        anchors.fill: parent
> +        drag {
> +            target: locked ? null : main
> +            axis: Drag.XAxis
> +            minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
> +            maximumX: leftActionView.visible ? leftActionView.width : 0
> +            threshold: root.actionThreshold
> +        }
> +
> +        states: [
> +            State {
> +                name: "LeftToRight"
> +                PropertyChanges {
> +                    target: mouseArea
> +                    drag.maximumX: 0
> +                }
> +            },
> +            State {
> +                name: "RightToLeft"
> +                PropertyChanges {
> +                    target: mouseArea
> +                    drag.minimumX: 0
> +                }
> +            }
> +        ]
> +
> +        onMouseXChanged: {
> +            var offset = (lastX - mouseX)
> +            if (Math.abs(offset) <= root.actionThreshold) {
> +                return
> +            }
> +            lastX = mouseX
> +            direction = offset > 0 ? "RTL" : "LTR";
> +        }
> +
> +        onPressed: {
> +            lastX = mouse.x
> +        }
> +
> +        onReleased: {
> +            if (root.triggerActionOnMouseRelease && root.activeAction) {
> +                triggerAction.start()
> +            } else {
> +                root.returnToBounds(direction)
> +                root.activeAction = null
> +            }
> +            lastX = -1
> +            direction = "None"
> +        }
> +        onClicked: {
> +            if (main.x === 0) {
> +                root.itemClicked(mouse)
> +            } else if (main.x > 0) {
> +                var action = getActionAt(Qt.point(mouse.x, mouse.y))
> +                if (action && action !== -1) {
> +                    action.triggered(root)
> +                }
> +            } else {
> +                var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
> +                if (actionIndex !== -1) {
> +                    root.activeItem = rightActionsRepeater.itemAt(actionIndex)
> +                    root.activeAction = root._visibleRightSideActions[actionIndex]
> +                    triggerAction.start()
> +                    return
> +                }
> +            }
> +            root.resetSwipe()
> +        }
> +
> +        onPositionChanged: {
> +            if (mouseArea.pressed) {
> +                updateActiveAction()
> +            }
> +        }
> +        onPressAndHold: {
> +            if (main.x === 0) {
> +                root.itemPressAndHold(mouse)
> +            }
> +        }
> +        z: -1
> +    }
> +}
> 
> === added file 'src/app/qml/upstreamComponents/ListItemWithActionsCheckBox.qml'
> --- src/app/qml/upstreamComponents/ListItemWithActionsCheckBox.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/ListItemWithActionsCheckBox.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright (C) 2012-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +
> +CheckBox {
> +    checked: root.selected
> +    width: implicitWidth
> +    // disable item mouse area to avoid conflicts with parent mouse area
> +    __mouseArea.enabled: false
> +}
> 
> === added file 'src/app/qml/upstreamComponents/MultipleSelectionGridView.qml'
> --- src/app/qml/upstreamComponents/MultipleSelectionGridView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/MultipleSelectionGridView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,199 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0 as Popups
> +
> +/*!
> +    \qmltype ContactSimpleListView
> +    \inqmlmodule Ubuntu.Contacts 0.1
> +    \ingroup ubuntu
> +    \brief The MultipleSelectionListView provides a ListView with support to multiple selection
> +
> +    The MultipleSelectionListViewprovides a ListView with support to multiple selection which can be used by any
> +    application.
> +
> +    Example:
> +    \qml
> +        import Ubuntu.Contacts 0.1
> +
> +        MultipleSelectionListView {
> +            id: view
> +            anchors.fill: paret
> +            model: 100
> +            delegate: Rectangle {
> +                width: parent.width
> +                height: 100
> +                color: view.selectedItems.indexOf(index) == -1 ? "white" : "blue"
> +
> +                MouseArea {
> +                    anchors.fill: parent
> +                    onClicked: {
> +                        if (view.isInSelectionModel) {
> +                            view.selectItem(index)
> +                        }
> +                    }
> +                    onPressAndHold: view.startSelection()
> +                }
> +            }
> +            onSelectionDone: console.debug("Selected items:" + view.selectedItems)
> +        }
> +    \endqml
> +*/
> +
> +GridView {
> +    id: listView
> +
> +    /*!
> +      \qmlproperty model selectedItems
> +
> +      This property holds the list of selected items
> +    */
> +    readonly property alias selectedItems: visualModel.selectedItems
> +    /*!
> +      \qmlproperty bool multipleSelection
> +
> +      This property holds if the selection will accept multiple items or single items
> +    */
> +    property bool multipleSelection: true
> +
> +    /*!
> +      \qmlproperty model listModel
> +
> +      This property holds the model providing data for the list.
> +    */
> +    property alias listModel: visualModel.model
> +    /*!
> +      \qmlproperty Component listDelegate
> +
> +      The delegate provides a template defining each item instantiated by the view.
> +    */
> +    property alias listDelegate: visualModel.delegate
> +
> +    /*!
> +      \qmlproperty bool isInSelectionMode
> +
> +      This property holds a list with the index of selected items
> +    */
> +    readonly property bool isInSelectionMode: state === "selection"
> +    /*!
> +      This handler is called when the selection mode is finished without be canceled
> +    */
> +    signal selectionDone(var items)
> +    /*!
> +      This handler is called when the selection mode is canceled
> +    */
> +    signal selectionCanceled()
> +
> +    /*!
> +      Start the selection mode on the list view.
> +    */
> +    function startSelection()
> +    {
> +        state = "selection"
> +    }
> +    /*!
> +      Check if the item is selected
> +      Returns true if the item was marked as selected or false if the item is unselected
> +    */
> +    function isSelected(item)
> +    {
> +        if (item && item.VisualDataModel) {
> +            return (item.VisualDataModel.inSelected === true)
> +        } else {
> +            return false
> +        }
> +    }
> +    /*!
> +      Mark the item as selected
> +      Returns true if the item was marked as selected or false if the item is already selected
> +    */
> +    function selectItem(item)
> +    {
> +        if (item.VisualDataModel.inSelected) {
> +            return false
> +        } else {
> +            if (!multipleSelection) {
> +                clearSelection()
> +            }
> +            item.VisualDataModel.inSelected = true
> +            return true
> +        }
> +    }
> +    /*!
> +      Remove the index from the selected list
> +    */
> +    function deselectItem(item)
> +    {
> +        var result = false
> +        if (item.VisualDataModel.inSelected) {
> +            item.VisualDataModel.inSelected = false
> +            result = true
> +        }
> +        return result
> +    }
> +    /*!
> +      Finish the selection mode with sucess
> +    */
> +    function endSelection()
> +    {
> +        selectionDone(listView.selectedItems)
> +        clearSelection()
> +        state = ""
> +    }
> +    /*!
> +      Cancel the selection
> +    */
> +    function cancelSelection()
> +    {
> +        selectionCanceled()
> +        clearSelection()
> +        state = ""
> +    }
> +    /*!
> +      Remove any selected item from the selection list
> +    */
> +    function clearSelection()
> +    {
> +        if (selectedItems.count > 0) {
> +            selectedItems.remove(0, selectedItems.count)
> +        }
> +    }
> +    /*!
> +      Select all items in the list
> +    */
> +    function selectAll()
> +    {
> +        if (multipleSelection) {
> +            visualModel.items.addGroups(0, visualModel.items.count, ["selected"] )
> +        }
> +    }
> +
> +    model: visualModel
> +
> +    MultipleSelectionVisualModel {
> +        id: visualModel
> +    }
> +
> +    Component.onCompleted: {
> +        // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
> +        // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
> +        var scaleFactor = units.gridUnit / 8;
> +        maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
> +        flickDeceleration = flickDeceleration * scaleFactor;
> +    }
> +}
> 
> === added file 'src/app/qml/upstreamComponents/MultipleSelectionListView.qml'
> --- src/app/qml/upstreamComponents/MultipleSelectionListView.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/MultipleSelectionListView.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,199 @@
> +/*
> + * Copyright (C) 2013 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.Popups 1.0 as Popups
> +
> +/*!
> +    \qmltype ContactSimpleListView
> +    \inqmlmodule Ubuntu.Contacts 0.1
> +    \ingroup ubuntu
> +    \brief The MultipleSelectionListView provides a ListView with support to multiple selection
> +
> +    The MultipleSelectionListViewprovides a ListView with support to multiple selection which can be used by any
> +    application.
> +
> +    Example:
> +    \qml
> +        import Ubuntu.Contacts 0.1
> +
> +        MultipleSelectionListView {
> +            id: view
> +            anchors.fill: paret
> +            model: 100
> +            delegate: Rectangle {
> +                width: parent.width
> +                height: 100
> +                color: view.selectedItems.indexOf(index) == -1 ? "white" : "blue"
> +
> +                MouseArea {
> +                    anchors.fill: parent
> +                    onClicked: {
> +                        if (view.isInSelectionModel) {
> +                            view.selectItem(index)
> +                        }
> +                    }
> +                    onPressAndHold: view.startSelection()
> +                }
> +            }
> +            onSelectionDone: console.debug("Selected items:" + view.selectedItems)
> +        }
> +    \endqml
> +*/
> +
> +ListView {
> +    id: listView
> +
> +    /*!
> +      \qmlproperty model selectedItems
> +
> +      This property holds the list of selected items
> +    */
> +    readonly property alias selectedItems: visualModel.selectedItems
> +    /*!
> +      \qmlproperty bool multipleSelection
> +
> +      This property holds if the selection will accept multiple items or single items
> +    */
> +    property bool multipleSelection: true
> +
> +    /*!
> +      \qmlproperty model listModel
> +
> +      This property holds the model providing data for the list.
> +    */
> +    property alias listModel: visualModel.model
> +    /*!
> +      \qmlproperty Component listDelegate
> +
> +      The delegate provides a template defining each item instantiated by the view.
> +    */
> +    property alias listDelegate: visualModel.delegate
> +
> +    /*!
> +      \qmlproperty bool isInSelectionMode
> +
> +      This property holds a list with the index of selected items
> +    */
> +    readonly property bool isInSelectionMode: state === "selection"
> +    /*!
> +      This handler is called when the selection mode is finished without be canceled
> +    */
> +    signal selectionDone(var items)
> +    /*!
> +      This handler is called when the selection mode is canceled
> +    */
> +    signal selectionCanceled()
> +
> +    /*!
> +      Start the selection mode on the list view.
> +    */
> +    function startSelection()
> +    {
> +        state = "selection"
> +    }
> +    /*!
> +      Check if the item is selected
> +      Returns true if the item was marked as selected or false if the item is unselected
> +    */
> +    function isSelected(item)
> +    {
> +        if (item && item.VisualDataModel) {
> +            return (item.VisualDataModel.inSelected === true)
> +        } else {
> +            return false
> +        }
> +    }
> +    /*!
> +      Mark the item as selected
> +      Returns true if the item was marked as selected or false if the item is already selected
> +    */
> +    function selectItem(item)
> +    {
> +        if (item.VisualDataModel.inSelected) {
> +            return false
> +        } else {
> +            if (!multipleSelection) {
> +                clearSelection()
> +            }
> +            item.VisualDataModel.inSelected = true
> +            return true
> +        }
> +    }
> +    /*!
> +      Remove the index from the selected list
> +    */
> +    function deselectItem(item)
> +    {
> +        var result = false
> +        if (item.VisualDataModel.inSelected) {
> +            item.VisualDataModel.inSelected = false
> +            result = true
> +        }
> +        return result
> +    }
> +    /*!
> +      Finish the selection mode with sucess
> +    */
> +    function endSelection()
> +    {
> +        selectionDone(listView.selectedItems)
> +        clearSelection()
> +        state = ""
> +    }
> +    /*!
> +      Cancel the selection
> +    */
> +    function cancelSelection()
> +    {
> +        selectionCanceled()
> +        clearSelection()
> +        state = ""
> +    }
> +    /*!
> +      Remove any selected item from the selection list
> +    */
> +    function clearSelection()
> +    {
> +        if (selectedItems.count > 0) {
> +            selectedItems.remove(0, selectedItems.count)
> +        }
> +    }
> +    /*!
> +      Select all items in the list
> +    */
> +    function selectAll()
> +    {
> +        if (multipleSelection) {
> +            visualModel.items.addGroups(0, visualModel.items.count, ["selected"] )
> +        }
> +    }
> +
> +    model: visualModel
> +
> +    MultipleSelectionVisualModel {
> +        id: visualModel
> +    }
> +
> +    Component.onCompleted: {
> +        // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
> +        // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
> +        var scaleFactor = units.gridUnit / 8;
> +        maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
> +        flickDeceleration = flickDeceleration * scaleFactor;
> +    }
> +}
> 
> === added file 'src/app/qml/upstreamComponents/MultipleSelectionVisualModel.qml'
> --- src/app/qml/upstreamComponents/MultipleSelectionVisualModel.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/MultipleSelectionVisualModel.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,31 @@
> +/*
> + * Copyright (C) 2012-2013 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.3
> +
> +VisualDataModel {
> +    id: contactVisualModel
> +
> +    property alias selectedItems: selectedGroup
> +
> +    groups: [
> +        VisualDataGroup {
> +            id: selectedGroup
> +
> +            name: "selected"
> +        }
> +    ]
> +}
> 
> === added file 'src/app/qml/upstreamComponents/PageWithBottomEdge.qml'
> --- src/app/qml/upstreamComponents/PageWithBottomEdge.qml	1970-01-01 00:00:00 +0000
> +++ src/app/qml/upstreamComponents/PageWithBottomEdge.qml	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,407 @@
> +/*
> + * Copyright (C) 2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/*
> +    Example:
> +
> +    MainView {
> +        objectName: "mainView"
> +
> +        applicationName: "com.ubuntu.developer.boiko.bottomedge"
> +
> +        width: units.gu(100)
> +        height: units.gu(75)
> +
> +        Component {
> +            id: pageComponent
> +
> +            PageWithBottomEdge {
> +                id: mainPage
> +                title: i18n.tr("Main Page")
> +
> +                Rectangle {
> +                    anchors.fill: parent
> +                    color: "white"
> +                }
> +
> +                bottomEdgePageComponent: Page {
> +                    title: "Contents"
> +                    anchors.fill: parent
> +                    //anchors.topMargin: contentsPage.flickable.contentY
> +
> +                    ListView {
> +                        anchors.fill: parent
> +                        model: 50
> +                        delegate: ListItems.Standard {
> +                            text: "One Content Item: " + index
> +                        }
> +                    }
> +                }
> +                bottomEdgeTitle: i18n.tr("Bottom edge action")
> +            }
> +        }
> +
> +        PageStack {
> +            id: stack
> +            Component.onCompleted: stack.push(pageComponent)
> +        }
> +    }
> +
> +*/
> +
> +import QtQuick 2.2
> +import Ubuntu.Components 1.1
> +
> +Page {
> +    id: page
> +
> +    property alias bottomEdgePageComponent: edgeLoader.sourceComponent
> +    property alias bottomEdgePageSource: edgeLoader.source
> +    property alias bottomEdgeTitle: tipLabel.text
> +    property bool bottomEdgeEnabled: true
> +    property int bottomEdgeExpandThreshold: page.height * 0.2
> +    property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
> +    property bool reloadBottomEdgePage: true
> +
> +    readonly property alias bottomEdgePage: edgeLoader.item
> +    readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active)
> +    readonly property bool isCollapsed: (bottomEdge.y === page.height)
> +    readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready)
> +
> +    property bool _showEdgePageWhenReady: false
> +    property int _areaWhenExpanded: 0
> +
> +    signal bottomEdgeReleased()
> +    signal bottomEdgeDismissed()
> +
> +
> +    function showBottomEdgePage(source, properties)
> +    {
> +        edgeLoader.setSource(source, properties)
> +        _showEdgePageWhenReady = true
> +    }
> +
> +    function setBottomEdgePage(source, properties)
> +    {
> +        edgeLoader.setSource(source, properties)
> +    }
> +
> +    function _pushPage()
> +    {
> +        if (edgeLoader.status === Loader.Ready) {
> +            edgeLoader.item.active = true
> +            page.pageStack.push(edgeLoader.item)
> +            if (edgeLoader.item.flickable) {
> +                edgeLoader.item.flickable.contentY = -page.header.height
> +                edgeLoader.item.flickable.returnToBounds()
> +            }
> +            if (edgeLoader.item.ready)
> +                edgeLoader.item.ready()
> +        }
> +    }
> +
> +
> +    Component.onCompleted: {
> +        // avoid a binding on the expanded height value
> +        var expandedHeight = height;
> +        _areaWhenExpanded = expandedHeight;
> +    }
> +
> +    onActiveChanged: {
> +        if (active) {
> +            bottomEdge.state = "collapsed"
> +        }
> +    }
> +
> +    onBottomEdgePageLoadedChanged: {
> +        if (_showEdgePageWhenReady && bottomEdgePageLoaded) {
> +            bottomEdge.state = "expanded"
> +            _showEdgePageWhenReady = false
> +        }
> +    }
> +
> +    Rectangle {
> +        id: bgVisual
> +
> +        color: "black"
> +        anchors.fill: page
> +        opacity: 0.7 * ((page.height - bottomEdge.y) / page.height)
> +        z: 1
> +    }
> +
> +    UbuntuShape {
> +        id: tip
> +        objectName: "bottomEdgeTip"
> +
> +        property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y)
> +
> +        enabled: mouseArea.enabled
> +        visible: page.bottomEdgeEnabled
> +        anchors {
> +            bottom: parent.bottom
> +            horizontalCenter: bottomEdge.horizontalCenter
> +            bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1)
> +            Behavior on bottomMargin {
> +                SequentialAnimation {
> +                    // wait some msecs in case of the focus change again, to avoid flickering
> +                    PauseAnimation {
> +                        duration: 300
> +                    }
> +                    UbuntuNumberAnimation {
> +                        duration: UbuntuAnimation.SnapDuration
> +                    }
> +                }
> +            }
> +        }
> +
> +        z: 1
> +        width: tipLabel.paintedWidth + units.gu(6)
> +        height: bottomEdge.tipHeight + units.gu(1)
> +        color: Theme.palette.normal.overlay
> +        Label {
> +            id: tipLabel
> +
> +            anchors {
> +                top: parent.top
> +                left: parent.left
> +                right: parent.right
> +            }
> +            height: bottomEdge.tipHeight
> +            verticalAlignment: Text.AlignVCenter
> +            horizontalAlignment: Text.AlignHCenter
> +            opacity: tip.hidden ? 0.0 : 1.0
> +            Behavior on opacity {
> +                UbuntuNumberAnimation {
> +                    duration: UbuntuAnimation.SnapDuration
> +                }
> +            }
> +        }
> +    }
> +
> +    Rectangle {
> +        id: shadow
> +
> +        anchors {
> +            left: parent.left
> +            right: parent.right
> +            bottom: parent.bottom
> +        }
> +        height: units.gu(1)
> +        z: 1
> +        opacity: 0.0
> +        gradient: Gradient {
> +            GradientStop { position: 0.0; color: "transparent" }
> +            GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
> +        }
> +    }
> +
> +    MouseArea {
> +        id: mouseArea
> +
> +        property real previousY: -1
> +        property string dragDirection: "None"
> +
> +        preventStealing: true
> +        drag {
> +            axis: Drag.YAxis
> +            target: bottomEdge
> +            minimumY: bottomEdge.pageStartY
> +            maximumY: page.height
> +        }
> +        enabled: edgeLoader.status == Loader.Ready
> +        visible: page.bottomEdgeEnabled
> +
> +        anchors {
> +            left: parent.left
> +            right: parent.right
> +            bottom: parent.bottom
> +
> +        }
> +        height: bottomEdge.tipHeight
> +        z: 1
> +
> +        onReleased: {
> +            page.bottomEdgeReleased()
> +            if ((dragDirection === "BottomToTop") &&
> +                bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
> +                bottomEdge.state = "expanded"
> +            } else {
> +                bottomEdge.state = "collapsed"
> +            }
> +            previousY = -1
> +            dragDirection = "None"
> +        }
> +
> +        onPressed: {
> +            previousY = mouse.y
> +            tip.forceActiveFocus()
> +        }
> +
> +        onMouseYChanged: {
> +            var yOffset = previousY - mouseY
> +            // skip if was a small move
> +            if (Math.abs(yOffset) <= units.gu(2)) {
> +                return
> +            }
> +            previousY = mouseY
> +            dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
> +        }
> +    }
> +
> +    Rectangle {
> +        id: bottomEdge
> +        objectName: "bottomEdge"
> +
> +        readonly property int tipHeight: units.gu(3)
> +        readonly property int pageStartY: 0
> +
> +        z: 1
> +        color: Theme.palette.normal.background
> +        clip: true
> +        anchors {
> +            left: parent.left
> +            right: parent.right
> +        }
> +        height: page.height
> +        y: height
> +        visible: !page.isCollapsed
> +        state: "collapsed"
> +        states: [
> +            State {
> +                name: "collapsed"
> +                PropertyChanges {
> +                    target: bottomEdge
> +                    y: bottomEdge.height
> +                }
> +            },
> +            State {
> +                name: "expanded"
> +                PropertyChanges {
> +                    target: bottomEdge
> +                    y: bottomEdge.pageStartY
> +                }
> +            },
> +            State {
> +                name: "floating"
> +                when: mouseArea.drag.active
> +                PropertyChanges {
> +                    target: shadow
> +                    opacity: 1.0
> +                }
> +            }
> +        ]
> +
> +        transitions: [
> +            Transition {
> +                to: "expanded"
> +                SequentialAnimation {
> +                    alwaysRunToEnd: true
> +
> +                    SmoothedAnimation {
> +                        target: bottomEdge
> +                        property: "y"
> +                        duration: UbuntuAnimation.FastDuration
> +                        easing.type: Easing.Linear
> +                    }
> +                    SmoothedAnimation {
> +                        target: edgeLoader
> +                        property: "anchors.topMargin"
> +                        to: - units.gu(4)
> +                        duration: UbuntuAnimation.FastDuration
> +                        easing.type: Easing.Linear
> +                    }
> +                    SmoothedAnimation {
> +                        target: edgeLoader
> +                        property: "anchors.topMargin"
> +                        to: 0
> +                        duration: UbuntuAnimation.FastDuration
> +                        easing: UbuntuAnimation.StandardEasing
> +                    }
> +                    ScriptAction {
> +                        script: page._pushPage()
> +                    }
> +                }
> +            },
> +            Transition {
> +                from: "expanded"
> +                to: "collapsed"
> +                SequentialAnimation {
> +                    alwaysRunToEnd: true
> +
> +                    ScriptAction {
> +                        script: {
> +                            Qt.inputMethod.hide()
> +                            edgeLoader.item.parent = edgeLoader
> +                            edgeLoader.item.anchors.fill = edgeLoader
> +                            edgeLoader.item.active = false
> +                        }
> +                    }
> +                    SmoothedAnimation {
> +                        target: bottomEdge
> +                        property: "y"
> +                        duration: UbuntuAnimation.SlowDuration
> +                    }
> +                    ScriptAction {
> +                        script: {
> +                            // destroy current bottom page
> +                            if (page.reloadBottomEdgePage) {
> +                                edgeLoader.active = false
> +                                // tip will receive focus on page active true
> +                            } else {
> +                                tip.forceActiveFocus()
> +                            }
> +
> +                            // notify
> +                            page.bottomEdgeDismissed()
> +
> +                            edgeLoader.active = true
> +                        }
> +                    }
> +                }
> +            },
> +            Transition {
> +                from: "floating"
> +                to: "collapsed"
> +                SmoothedAnimation {
> +                    target: bottomEdge
> +                    property: "y"
> +                    duration: UbuntuAnimation.FastDuration
> +                }
> +            }
> +        ]
> +
> +        Loader {
> +            id: edgeLoader
> +
> +            asynchronous: true
> +            anchors.fill: parent
> +            //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
> +            Binding {
> +                target: edgeLoader.status === Loader.Ready ? edgeLoader : null
> +                property: "anchors.topMargin"
> +                value:  edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
> +                when: !page.isReady
> +            }
> +
> +            onLoaded: {
> +                tip.forceActiveFocus()
> +                if (page.isReady && edgeLoader.item.active !== true) {
> +                    page._pushPage()
> +                }
> +            }
> +        }
> +    }
> +}
> 
> === removed file 'src/app/qml/utils.js'
> --- src/app/qml/utils.js	2015-01-29 18:45:21 +0000
> +++ src/app/qml/utils.js	1970-01-01 00:00:00 +0000
> @@ -1,34 +0,0 @@
> -/*
> - * Copyright (C) 2013-2015 Canonical, Ltd.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -.pragma library
> -
> -function printSize(size) {
> -    if (size >= 1073741824)
> -        return (size / 1073741824).toFixed(2) + " GiB";
> -
> -    if (size >= 1048576)
> -        return (size / 1048576).toFixed(2) + " MiB";
> -
> -    if (size >= 1024)
> -        return parseInt(size / 1024) + " KiB";
> -
> -    return size + " byte";
> -};
> -
> -function getNameOfFile(path) {
> -    return path.toString().substring(path.lastIndexOf('/') + 1);
> -}
> 
> === added directory 'src/app/quick'
> === added file 'src/app/quick/documentmodel.cpp'
> --- src/app/quick/documentmodel.cpp	1970-01-01 00:00:00 +0000
> +++ src/app/quick/documentmodel.cpp	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,212 @@
> +/*
> + *  Copyright (C) 2014, 2015
> + *                  Stefano Verzegnassi <stefano92.100@xxxxxxxxx>
> + *
> + *  This is free software: you can redistribute it and/or modify
> + *  it under the terms of the  GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License.
> + *
> + *  This is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program. If not, see http://www.gnu.org/licenses/.
> + */
> +
> +#include "documentmodel.h"
> +
> +#include <QMimeDatabase>
> +#include <QDateTime>
> +#include <QFileInfo>
> +#include <QDir>
> +#include <QDebug>
> +
> +DocumentModel::DocumentModel(QObject *parent)
> +    : QAbstractListModel(parent)
> +{
> +    m_docsMonitor = new QFileSystemWatcher;
> +
> +    connect(m_docsMonitor, SIGNAL(directoryChanged(QString)), this,
> +            SLOT(_q_directoryChanged(QString)));
> +
> +    m_docsMonitor->addPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
> +
> +    Q_FOREACH (const QString dir, m_docsMonitor->directories()) {
> +        parseDirectoryContent(dir);
> +    }
> +}
> +
> +void DocumentModel::_q_directoryChanged(QString path)
> +{
> +    QMutableListIterator<DocumentItem> i(m_docs);
> +    int n = 0;
> +    int m = 0;
> +
> +    while (i.hasNext()) {
> +        if (i.next().path.startsWith(path)) {
> +            beginRemoveRows(QModelIndex(), n-m, n-m);
> +            i.remove();
> +            endRemoveRows();
> +            m++;
> +        }
> +        n++;
> +    }
> +
> +    parseDirectoryContent(path);
> +}
> +
> +void DocumentModel::parseDirectoryContent(QString path)
> +{
> +    QDirIterator dir(path, QDir::Files | QDir::NoDotAndDotDot | QDir::Readable,
> +                     QDirIterator::Subdirectories);
> +
> +    while (dir.hasNext()) {
> +        dir.next();
> +
> +        QDateTime now = QDateTime::currentDateTime();
> +        //qDebug() << "Current date is:" << now.toString("dd-MM-yyyy");
> +
> +        QMimeDatabase db;
> +        QString mimetype = db.mimeTypeForFile(dir.filePath()).name();
> +
> +        if (mimetype.startsWith("text/") || mimetype == "application/pdf") {
> +            QFileInfo file(dir.filePath());
> +            DocumentItem item;
> +
> +            QDateTime lastAccess = file.lastRead();
> +
> +            item.name = file.fileName();
> +            item.path = file.absoluteFilePath();
> +            item.mimetype = mimetype;
> +            item.date = lastAccess.toMSecsSinceEpoch();
> +            item.size = file.size();
> +
> +            qint64 dateDiff = lastAccess.daysTo(now);
> +            if (dateDiff == 0) {
> +                item.dateDiff = 0;
> +            }
> +            else if (dateDiff == 1) {
> +                item.dateDiff = 1;
> +            }
> +            else if (dateDiff < 7) {
> +                item.dateDiff = 2;
> +            }
> +            else if (dateDiff < 30) {
> +                item.dateDiff = 3;
> +            }
> +            else {
> +                item.dateDiff = 4;
> +            }
> +            //qDebug() << "Item" << item.name << "Date:" << item.date;
> +            //qDebug() << "Item" << item.name << "Item date is:" << lastAccess.toString("dd-MM-yyyy") << "diff is" << dateDiff << "DateDiff is:" << item.dateDiff;
> +
> +            addDocumentEntry(item);
> +        }
> +    }
> +}
> +
> +QHash<int, QByteArray> DocumentModel::roleNames() const
> +{
> +    QHash<int, QByteArray> roles;
> +    roles[PathRole] = "path";
> +    roles[NameRole] = "name";
> +    roles[MimetypeRole] = "mimetype";
> +    roles[DateRole] = "date";
> +    roles[DateDiffRole] = "dateDiff";
> +    roles[SizeRole] = "size";
> +
> +    return roles;
> +}
> +
> +void DocumentModel::addDocumentEntry(DocumentItem item)
> +{
> +    beginInsertRows(QModelIndex(), rowCount(), rowCount());
> +    m_docs.append(item);
> +    endInsertRows();
> +}
> +
> +void DocumentModel::removeDocumentEntry(int index)
> +{
> +    beginRemoveRows(QModelIndex(), rowCount(), rowCount());
> +    m_docs.removeAt(index);
> +    endRemoveRows();
> +}
> +
> +int DocumentModel::rowCount(const QModelIndex & parent) const
> +{
> +    Q_UNUSED(parent)
> +    return m_docs.count();
> +}
> +
> +QVariant DocumentModel::data(const QModelIndex & index, int role) const
> +{
> +    if (index.row() < 0 || index.row() > m_docs.count())
> +        return QVariant();
> +
> +    const DocumentItem &item = m_docs.at(index.row());
> +
> +    switch (role) {
> +    case PathRole:
> +        return item.path;
> +    case NameRole:
> +        return item.name;
> +    case MimetypeRole:
> +        return item.mimetype;
> +    case DateRole:
> +        return item.date;
> +    case DateDiffRole:
> +        return item.dateDiff;
> +    case SizeRole:
> +        return item.size;
> +    default:
> +        return 0;
> +    }
> +}
> +
> +DocumentModel::~DocumentModel()
> +{
> +    delete m_docsMonitor;
> +}
> +
> +SortFilterDocumentModel::SortFilterDocumentModel(QObject *parent)
> +    : QSortFilterProxyModel(parent)
> +{
> +    m_model = new DocumentModel();
> +    setSourceModel(m_model);
> +    connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> +    connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> +
> +    setSortRole(DocumentModel::DateRole);
> +    setSortCaseSensitivity(Qt::CaseInsensitive);
> +    setSortLocaleAware(true);
> +
> +    sort(0, Qt::DescendingOrder);
> +    setDynamicSortFilter(true);
> +}
> +
> +SortFilterDocumentModel::~SortFilterDocumentModel()
> +{
> +    delete m_model;
> +}
> +
> +int SortFilterDocumentModel::rowCount()
> +{
> +    return m_model->rowCount();
> +}
> +
> +bool SortFilterDocumentModel::rm(QString path)
> +{
> +    bool result = false;
> +    QDir dir(path);
> +
> +    if (dir.exists()) {
> +        result = dir.removeRecursively();
> +    } else {
> +        QFile fi(path);
> +        result = fi.remove();
> +    }
> +
> +    return result;
> +}
> 
> === added file 'src/app/quick/documentmodel.h'
> --- src/app/quick/documentmodel.h	1970-01-01 00:00:00 +0000
> +++ src/app/quick/documentmodel.h	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,89 @@
> +/*
> +  Copyright (C) 2014, 2015 Stefano Verzegnassi <stefano92.100@xxxxxxxxx>
> +
> +    This is free software: you can redistribute it and/or modify
> +  it under the terms of the  GNU General Public License as published by
> + the Free Software Foundation, either version 3 of the License.
> +
> +    This is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +  GNU General Public License for more details.
> +
> +    You should have received a copy of the GNU General Public License
> +  along with this program. If not, see http://www.gnu.org/licenses/.
> +*/
> +
> +#ifndef DOCUMENTMODEL_H
> +#define DOCUMENTMODEL_H
> +
> +#include <QAbstractListModel>
> +#include <QSortFilterProxyModel>
> +
> +#include <QStandardPaths>
> +#include <QFileSystemWatcher>
> +#include <QDirIterator>
> +
> +struct DocumentItem {
> +    QString name;
> +    QString path;
> +    QString mimetype;
> +    qint64 date;
> +    int dateDiff;
> +    qint64 size;
> +};
> +
> +class DocumentModel : public QAbstractListModel
> +{
> +    Q_OBJECT
> +public:
> +    enum Roles {
> +        NameRole = Qt::UserRole + 1,
> +        PathRole,
> +        MimetypeRole,
> +        DateRole,
> +        DateDiffRole,
> +        SizeRole
> +    };
> +
> +    DocumentModel(QObject *parent = 0);
> +    ~DocumentModel();
> +
> +    QHash<int, QByteArray> roleNames() const;
> +
> +    int rowCount(const QModelIndex & parent = QModelIndex()) const;
> +    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
> +
> +    void parseDirectoryContent(QString path);
> +
> +private Q_SLOTS:
> +    void _q_directoryChanged(QString path);
> +
> +private:   
> +    void addDocumentEntry(DocumentItem item);
> +    void removeDocumentEntry(int index);
> +
> +    QList<DocumentItem> m_docs;
> +    QFileSystemWatcher *m_docsMonitor;
> +};
> +
> +class SortFilterDocumentModel : public QSortFilterProxyModel
> +{
> +    Q_OBJECT
> +    Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
> +
> +public:        
> +    SortFilterDocumentModel(QObject *parent = 0);
> +    ~SortFilterDocumentModel();
> +
> +    int rowCount();
> +    Q_INVOKABLE bool rm(QString path);
> +
> +Q_SIGNALS:
> +    void countChanged();
> +
> +private:
> +    DocumentModel *m_model;
> +};
> +
> +#endif // DOCUMENTMODEL_H
> 
> === added file 'src/app/urlhandler.cpp'
> --- src/app/urlhandler.cpp	1970-01-01 00:00:00 +0000
> +++ src/app/urlhandler.cpp	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,70 @@
> +/*
> + * Copyright (C) 2014 Canonical, Ltd.
> + *
> + * Authors:
> + *  Arthur Mello <arthur.mello@xxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "urlhandler.h"
> +
> +#include <QUrl>
> +#include <QStringList>
> +#include <QFileInfo>
> +#include <QDir>
> +#include <QDebug>
> +
> +UrlHandler::UrlHandler()
> +    : m_documentFile("")
> +{
> +    m_validSchemes << "document";
> +}
> +
> +/*!
> + * @brief UrlHandler::processUri parsers our input uri and sets attributes accordingly.
> + * @param QString uri to parse and set attributes.
> + * @return false if invalid parameter is input.
> + */
> +bool UrlHandler::processUri(const QString& arg)
> +{
> +    QUrl uri(arg);
> +
> +    if (!m_validSchemes.contains(uri.scheme())) {
> +        return false;
> +    }
> +
> +    if (uri.scheme() == "document") {
> +        uri.setScheme("file");
> +    }
> +
> +    if (uri.isRelative()) {
> +        uri = QUrl::fromLocalFile(QDir::current().absoluteFilePath(arg));
> +    }
> +
> +    // Check if it's a local file
> +    if (uri.isValid() && uri.isLocalFile()) {
> +        QFileInfo info(uri.toLocalFile());
> +        if (info.exists() && info.isFile()) {
> +            m_documentFile = info.absoluteFilePath();
> +            return true;
> +        } else {
> +            qWarning() << "File not found:" << uri << info.exists() << info.isFile();
> +        }
> +    } else {
> +        qWarning() << "Invalid uri:" << uri;
> +    }
> +
> +    return false;
> +}
> 
> === added file 'src/app/urlhandler.h'
> --- src/app/urlhandler.h	1970-01-01 00:00:00 +0000
> +++ src/app/urlhandler.h	2015-02-27 15:45:56 +0000
> @@ -0,0 +1,43 @@
> +/*
> + * Copyright (C) 2014 Canonical, Ltd.
> + *
> + * Authors:
> + *  Arthur Mello <arthur.mello@xxxxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef URLHANDLER_H
> +#define URLHANDLER_H
> +
> +#include <QString>
> +#include <QList>
> +
> +/*!
> + * @brief The UrlHandler is used to parse calls of docviewer from the url schema.
> + */
> +class UrlHandler
> +{
> +public:
> +    UrlHandler();
> +
> +    bool processUri(const QString &arg);
> +    const QString &documentFile() { return m_documentFile; }
> +
> +private:
> +    QList<QString> m_validSchemes;
> +    QString m_documentFile;
> +};
> +
> +#endif // URLHANDLER_H
> 
> === removed file 'tests/autopilot/ubuntu_docviewer_app/files/ubuntu-touch.jpg'
> Binary files tests/autopilot/ubuntu_docviewer_app/files/ubuntu-touch.jpg	2013-06-30 15:10:38 +0000 and tests/autopilot/ubuntu_docviewer_app/files/ubuntu-touch.jpg	1970-01-01 00:00:00 +0000 differ
> === modified file 'tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py'
> --- tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py	2015-02-04 16:08:33 +0000
> +++ tests/autopilot/ubuntu_docviewer_app/tests/test_docviewer.py	2015-02-27 15:45:56 +0000
> @@ -8,7 +8,7 @@
>  """Docviewer app autopilot tests."""
>  
>  from autopilot.matchers import Eventually
> -from testtools.matchers import Equals, Contains, GreaterThan
> +from testtools.matchers import Equals, GreaterThan
>  
>  from ubuntu_docviewer_app.tests import DocviewerAppTestCase
>  
> @@ -34,20 +34,6 @@
>          self.assertThat(
>              text_area.text, Eventually(Equals('TEST\n')))
>  
> -    def test_open_image_file(self):
> -
> -        self.filepath = 'ubuntu_docviewer_app/files/ubuntu-touch.jpg'
> -
> -        self.launch_app()
> -
> -        image_item = self.app.main_view.select_single(
> -            "QQuickImage", objectName="imageRenderer")
> -
> -        self.assertThat(
> -            image_item.status, Eventually(Equals(1)))
> -        self.assertThat(
> -            image_item.source, Contains(self.filepath))
> -
>      def test_unknown_file_type(self):
>          self.filepath = 'ubuntu_docviewer_app/files/unknown.type'
>  
> 


-- 
https://code.launchpad.net/~verzegnassi-stefano/ubuntu-docviewer-app/document-hub2/+merge/251166
Your team Ubuntu Document Viewer Developers is subscribed to branch lp:ubuntu-docviewer-app.