diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index f73c4d4..51aae88 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,79 +1,79 @@ - + - + diff --git a/buho.pro b/buho.pro index 5791620..49460e7 100644 --- a/buho.pro +++ b/buho.pro @@ -1,62 +1,74 @@ QT += qml QT += quick QT += sql QT += widgets QT += quickcontrols2 CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ src/db/db.cpp \ src/db/dbactions.cpp \ src/buho.cpp \ src/documenthandler.cpp \ - src/linker.cpp + src/linker.cpp \ + src/utils/htmlparser.cpp RESOURCES += \ qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target include(mauikit/mauikit.pri) linux:unix:!android { message(Building for Linux KDE) } else:android { message(Building helpers for Android) include($$PWD/3rdparty/kirigami/kirigami.pri) include($$PWD/android/android.pri) DEFINES += STATIC_KIRIGAMI } else { message("Unknown configuration") } DISTFILES += \ src/db/script.sql \ - src/utils/owl.js + src/utils/owl.js \ + android/AndroidManifest.xml \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/gradlew \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew.bat HEADERS += \ src/db/db.h \ src/db/dbactions.h \ src/buho.h \ src/utils/owl.h \ src/documenthandler.h \ - src/linker.h + src/linker.h \ + src/utils/htmlparser.h + +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + diff --git a/main.cpp b/main.cpp index c5d3418..f9b9296 100644 --- a/main.cpp +++ b/main.cpp @@ -1,56 +1,59 @@ #include #include #include #include #ifdef STATIC_KIRIGAMI #include "3rdparty/kirigami/src/kirigamiplugin.h" #endif #ifdef Q_OS_ANDROID #include #include #else #include #endif #include "mauikit/src/mauikit.h" #include "src/buho.h" #include "src/documenthandler.h" - +#include "src/linker.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #ifdef Q_OS_ANDROID QGuiApplication app(argc, argv); QIcon::setThemeName("Luv"); QQuickStyle::setStyle("material"); #else QApplication app(argc, argv); #endif #ifdef STATIC_KIRIGAMI KirigamiPlugin::getInstance().registerTypes(); #endif #ifdef MAUI_APP MauiKit::getInstance().registerTypes(); #endif Buho owl; QQmlApplicationEngine engine; auto context = engine.rootContext(); context->setContextProperty("owl", &owl); + Linker linker; + + context->setContextProperty("linker", &linker); qmlRegisterType("org.buho.editor", 1, 0, "DocumentHandler"); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); } diff --git a/main.qml b/main.qml index cb67dda..6ef6e3e 100644 --- a/main.qml +++ b/main.qml @@ -1,130 +1,142 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.0 as Kirigami import org.kde.maui 1.0 as Maui import "src/widgets" import "src/views/notes" +import "src/views/links" Maui.ApplicationWindow { id: root title: qsTr("Buho") /***** PROPS *****/ property var views : ({ notes: 0, links: 1, books: 2 }) headBar.middleContent: Row { spacing: space.medium Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly iconName: "draw-text" text: qsTr("Notes") } Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly iconName: "link" text: qsTr("Links") } Maui.ToolButton { display: root.isWide ? ToolButton.TextBesideIcon : ToolButton.IconOnly iconName: "document-new" text: qsTr("Books") } } footBar.middleContent: Maui.PieButton { id: addButton iconName: "list-add" model: ListModel { ListElement {iconName: "document-new"; mid: "page"} ListElement {iconName: "link"; mid: "link"} ListElement {iconName: "draw-text"; mid: "note"} } onItemClicked: { if(item.mid === "note") newNoteDialog.open() + else if(item.mid === "link") + newLinkDialog.open() } } footBar.leftContent: Maui.ToolButton { iconName: "document-share" } footBar.rightContent: Maui.ToolButton { iconName: "archive-remove" } /***** COMPONENTS *****/ Connections { target: owl onNoteInserted: notesView.append(note) } NewNoteDialog { id: newNoteDialog onNoteSaved: owl.insertNote(note.title, note.body, note.color, note.tags) } NewNoteDialog { id: editNote onNoteSaved: { if(owl.updateNote(notesView.currentNote.id, note.title, note.body, note.color, note.tags)) notesView.cardsView.currentItem.update(note) } } + NewLinkDialog + { + id: newLinkDialog + } + /***** VIEWS *****/ SwipeView { anchors.fill: parent currentIndex: views.notes NotesView { id: notesView onNoteClicked: setNote(note) + } + LinksView + { + id: linksView } } Component.onCompleted: { notesView.populate() } function setNote(note) { notesView.currentNote = note editNote.fill(note) } } diff --git a/qml.qrc b/qml.qrc index 2e2c3ee..81f77e9 100644 --- a/qml.qrc +++ b/qml.qrc @@ -1,11 +1,12 @@ main.qml src/db/script.sql src/widgets/NewNoteDialog.qml src/widgets/CardsView.qml src/views/notes/NotesView.qml src/widgets/CardDelegate.qml src/widgets/NewLinkDialog.qml + src/views/links/LinksView.qml diff --git a/src/linker.cpp b/src/linker.cpp index 3509fa9..4d77fc3 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -1,6 +1,88 @@ #include "linker.h" +#include +#include -linker::linker(QObject *parent) : QObject(parent) +#include "utils/htmlparser.h" + +Linker::Linker(QObject *parent) : QObject(parent) +{ + +} + +/* extract needs to extract from a url the title, the body and a preview image*/ +void Linker::extract(const QString &url) +{ + auto data = this->getUrl(url); + htmlParser parser; + parser.setHtml(data); + qDebug()<error()) + { + qDebug() << reply->error(); + return QByteArray(); + } + + if(reply->bytesAvailable()) + { + auto data = reply->readAll(); + reply->deleteLater(); + + return data; + } + + return QByteArray(); +} + +void Linker::query(QByteArray &array, QString qq) +{ + auto frame = new QWebPage(this); + + QWebSettings::setObjectCacheCapacities(0,0,0); + frame->settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls,false); + frame->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,false); + + connect(frame->mainFrame(), &QWebFrame::loadFinished, [qq, this](bool ok) + { + this->parsingWork(qq); + }); + + + qDebug() << "Count Chars :: " << array.count(); + frame->mainFrame()->setHtml(array); + + doc = frame->mainFrame()->documentElement(); + +} + +void Linker::parsingWork(QString query) +{ + qDebug() << "Start parsing content ....."; + + QWebElementCollection linkCollection = doc.findAll(query); + qDebug() << "Found " << linkCollection.count() << " links"; + + foreach (QWebElement link, linkCollection) + { + qDebug() << "found link " << link.toPlainText(); + } + qDebug() << "stop parsing content ....."; } diff --git a/src/linker.h b/src/linker.h index 4c61744..ab68471 100644 --- a/src/linker.h +++ b/src/linker.h @@ -1,17 +1,34 @@ #ifndef LINKER_H #define LINKER_H #include +#include +#include +#include +#include +#include -class linker : public QObject +typedef QVariantMap LINK; + +class Linker : public QObject { Q_OBJECT public: - explicit linker(QObject *parent = nullptr); + explicit Linker(QObject *parent = nullptr); + + Q_INVOKABLE void extract(const QString &url); + +private: + QByteArray getUrl(const QString &url); + void query(QByteArray &array, QString qq); + QWebElement doc; signals: + void previewReady(LINK data); public slots: + void parsingWork(QString query); + }; -#endif // LINKER_H \ No newline at end of file +#endif // LINKER_H diff --git a/src/utils/htmlparser.cpp b/src/utils/htmlparser.cpp new file mode 100644 index 0000000..7b20077 --- /dev/null +++ b/src/utils/htmlparser.cpp @@ -0,0 +1,122 @@ +#include "htmlparser.h" + +htmlParser::htmlParser(QObject *parent) : QObject(parent) +{ + +} + +void htmlParser::setHtml(const QByteArray &array) +{ + this->html = array; + +} + + +QString htmlParser::extractProp(const QString &tag,const QString &prop) +{ +// qDebug()<<"extractProp"<html).split(">")); + + for(auto i =0; i") && tag.contains(attribute)) + { + QString subResult; +// qDebug()<html.size()) break; + } + + results<html); + QStringList tags; + int i =0; + while(html.size()>i) + { + if(html.at(i)=="<") + { + QString tag; + + while(html.at(i)!=">") + { + tag+=html.at(i); + i++; + if(i>html.size()) break; + } + tag+=">"; + + + if(findTag(tag,tagRef)) + { + tags<html.size()) break; + } + + return tags; +}*/ + +bool htmlParser::findTag(const QString &txt, const QString &tagRef) +{ + // qDebug()<<"size of tag<<"<txt.size()) break; + } + subTag+=">"; + } + + i++; + if(i>txt.size()) break; + } + + + if(tagRef==subTag) return true; + else return false; +} diff --git a/src/utils/htmlparser.h b/src/utils/htmlparser.h new file mode 100644 index 0000000..3a6e4f6 --- /dev/null +++ b/src/utils/htmlparser.h @@ -0,0 +1,29 @@ +#ifndef HTMLPARSER_H +#define HTMLPARSER_H +#include +#include +#include +#include + +class htmlParser : public QObject +{ + Q_OBJECT +public: + explicit htmlParser(QObject *parent = nullptr); + void setHtml(const QByteArray &html); + QStringList parseTag(const QString &tagRef, const QString &attribute=""); // return all tag matches with content + bool findTag(const QString &txt, const QString &tagRef); + QString extractProp(const QString &tag, const QString &prop); + + +private: + QByteArray html; + +signals: + void finishedParsingTags(const QStringList &tags); + void finishedExtractingProp(const QString &prop); + +public slots: +}; + +#endif // HTMLPARSER_H diff --git a/src/views/links/LinksView.qml b/src/views/links/LinksView.qml new file mode 100644 index 0000000..3dcf8ba --- /dev/null +++ b/src/views/links/LinksView.qml @@ -0,0 +1,8 @@ +import QtQuick 2.9 +import "../../widgets" +import org.kde.maui 1.0 as Maui + +Maui.Page +{ + id: control +} diff --git a/src/widgets/CardDelegate.qml b/src/widgets/CardDelegate.qml index c36504b..833479b 100644 --- a/src/widgets/CardDelegate.qml +++ b/src/widgets/CardDelegate.qml @@ -1,116 +1,120 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.2 as Kirigami ItemDelegate { id: control property string noteColor : color ? color : "pink" property int cardWidth: Kirigami.Units.devicePixelRatio*200 property int cardHeight: Kirigami.Units.devicePixelRatio*120 - width: cardWidth - height: cardHeight + height: cardHeight background: Rectangle { color: "transparent" } DropShadow { anchors.fill: card visible: card.visible horizontalOffset: 0 verticalOffset: 3 radius: 8.0 samples: 17 color: "#80000000" source: card } Rectangle { id: card z: -999 anchors.centerIn: control anchors.fill: control border.color: Qt.darker(noteColor, 1.2) color: noteColor radius: Kirigami.Units.devicePixelRatio*3 } ColumnLayout { anchors.fill: parent spacing: 0 + clip: true Label { id: title visible: title.text.length > 0 Layout.leftMargin: space.medium Layout.topMargin: space.medium Layout.rightMargin: space.medium Layout.fillWidth: true text: model.title + color: Qt.darker(model.color, 3) + font.weight: Font.Bold font.bold: true font.pointSize: fontSizes.large } TextArea { id: body Layout.leftMargin: space.medium Layout.bottomMargin: space.medium Layout.rightMargin: space.medium Layout.topMargin: title.visible ? 0 : space.medium Layout.fillHeight: true Layout.fillWidth: true enabled: false text: model.body + color: Qt.darker(model.color, 3) + textFormat: TextEdit.RichText font.pointSize: fontSizes.big background: Rectangle { color: "transparent" } } Item { id: preview Layout.fillHeight: true Layout.fillWidth: true Layout.maximumHeight: control.height * 0.3 Image { id: img visible: model.preview asynchronous: true height: parent.height width: parent.width sourceSize.height: height sourceSize.width: width fillMode: Image.PreserveAspectCrop - source: model.preview + source: model.preview || "" } } } function update(note) { title.text = note.title body.text = note.body noteColor = note.color } } diff --git a/src/widgets/CardsView.qml b/src/widgets/CardsView.qml index 39caabf..645beba 100644 --- a/src/widgets/CardsView.qml +++ b/src/widgets/CardsView.qml @@ -1,53 +1,53 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.2 as Kirigami import org.kde.maui 1.0 as Maui GridView { property alias holder : holder - property int itemWidth : Kirigami.Units.devicePixelRatio * 200 + readonly property int defaultSize : Kirigami.Units.devicePixelRatio * 200 + property int itemWidth : !isMobile ? Kirigami.Units.devicePixelRatio * 200 : parent.width property int itemHeight: Kirigami.Units.devicePixelRatio * 120 property int itemSpacing: space.huge signal itemClicked(int index) cellWidth: itemWidth + itemSpacing cellHeight: itemHeight + itemSpacing Maui.Holder { id: holder visible: count < 1 message: "

No notes!

You can create new notes
links and books

" } model: ListModel { } delegate: CardDelegate { id: delegate cardWidth: itemWidth cardHeight: itemHeight onClicked: { currentIndex = index itemClicked(index) - } } - onWidthChanged: adaptGrid() + onWidthChanged: if(!isMobile) adaptGrid() function adaptGrid() { var amount = parseInt(width/(itemWidth + itemSpacing),10) var leftSpace = parseInt(width-(amount*(itemWidth + itemSpacing)), 10) var size = parseInt((itemWidth + itemSpacing)+(parseInt(leftSpace/amount, 10)), 10) size = size > itemWidth + itemSpacing ? size : itemWidth + itemSpacing cellWidth = size } } diff --git a/src/widgets/NewLinkDialog.qml b/src/widgets/NewLinkDialog.qml index c6deee4..de67cee 100644 --- a/src/widgets/NewLinkDialog.qml +++ b/src/widgets/NewLinkDialog.qml @@ -1,125 +1,137 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.maui 1.0 as Maui import org.buho.editor 1.0 Popup { parent: ApplicationWindow.overlay - height: parent.height * (isMobile ? 0.8 : 0.7) + height: previewReady ? parent.height * (isMobile ? 0.8 : 0.7) : + toolBarHeight width: parent.width * (isMobile ? 0.9 : 0.7) signal linkSaved(var note) property bool previewReady : false x: (parent.width / 2) - (width / 2) y: (parent.height /2 ) - (height / 2) padding: 1 Rectangle { id: bg - color: selectedColor + color: "transparent" z: -1 anchors.fill: parent } ColumnLayout { id: content anchors.fill: parent TextField { id: link Layout.fillWidth: true Layout.margins: space.medium height: 24 + placeholderText: qsTr("URL") + font.weight: Font.Bold + font.bold: true + font.pointSize: fontSizes.large + background: Rectangle + { + color: "transparent" + } + + onAccepted: linker.extract(link.text) + } + + TextField + { + id: title + visible: previewReady + Layout.fillWidth: true + Layout.margins: space.medium + height: 24 placeholderText: qsTr("Title") font.weight: Font.Bold font.bold: true background: Rectangle { color: "transparent" } } ScrollView { Layout.fillHeight: true Layout.fillWidth: true Layout.margins: space.medium - + visible: previewReady TextArea { id: body placeholderText: qsTr("Body") textFormat : TextEdit.AutoText enabled: false background: Rectangle { color: "transparent" } } } Row { Layout.fillWidth: true width: parent.width Layout.margins: space.medium Layout.alignment: Qt.AlignRight spacing: space.medium + visible: previewReady Button { id: save text: qsTr("Save") onClicked: { - close() - noteSaved({ - title: title.text, - body: body.text, - color: selectedColor, - tags: "" - }) - clearNote() - + linker.extract(link.text) + clear() } + } Button { id: discard text: qsTr("Discard") - onClicked: - { - close() - clearNote() - } + onClicked: clear() } - } } - function clearNote() + function clear() { title.clear() body.clear() + close() + } function fill(note) { document.load("qrc:/texteditor.html") title.text = note.title body.text = note.body selectedColor = note.color open() } } diff --git a/src/widgets/NewNoteDialog.qml b/src/widgets/NewNoteDialog.qml index 1bfe6b4..f1be6a2 100644 --- a/src/widgets/NewNoteDialog.qml +++ b/src/widgets/NewNoteDialog.qml @@ -1,275 +1,276 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import org.kde.maui 1.0 as Maui import org.buho.editor 1.0 Popup { parent: ApplicationWindow.overlay height: parent.height * (isMobile ? 0.8 : 0.7) width: parent.width * (isMobile ? 0.9 : 0.7) property string selectedColor : "#ffffe6" signal noteSaved(var note) x: (parent.width / 2) - (width / 2) y: (parent.height /2 ) - (height / 2) padding: 1 Rectangle { id: bg color: selectedColor z: -1 anchors.fill: parent } ColumnLayout { id: content anchors.fill: parent Maui.ToolBar { + position: ToolBar.Header + z: 999 Layout.fillWidth: true leftContent: [ Maui.ToolButton { iconName: "format-text-bold" focusPolicy: Qt.TabFocus iconColor: checked ? highlightColor : textColor checkable: true checked: document.bold onClicked: document.bold = !document.bold }, Maui.ToolButton { iconName: "format-text-italic-symbolic" iconColor: checked ? highlightColor : textColor focusPolicy: Qt.TabFocus checkable: true checked: document.italic onClicked: document.italic = !document.italic }, Maui.ToolButton { iconName: "format-text-underline-symbolic" }, Maui.ToolButton { iconName: "format-text-uppercase" } ] rightContent: Row { spacing: space.medium Rectangle { color:"#ffded4" anchors.verticalCenter: parent.verticalCenter height: iconSizes.medium width: height radius: Math.max(height, width) border.color: borderColor MouseArea { anchors.fill: parent onClicked: selectedColor = parent.color } } Rectangle { color:"#d3ffda" anchors.verticalCenter: parent.verticalCenter height: iconSizes.medium width: height radius: Math.max(height, width) border.color: borderColor MouseArea { anchors.fill: parent onClicked: selectedColor = parent.color } } Rectangle { color:"#caf3ff" anchors.verticalCenter: parent.verticalCenter height: iconSizes.medium width: height radius: Math.max(height, width) border.color: borderColor MouseArea { anchors.fill: parent onClicked: selectedColor = parent.color } } Rectangle { color:"#ccc1ff" anchors.verticalCenter: parent.verticalCenter height: iconSizes.medium width: height radius: Math.max(height, width) border.color: borderColor MouseArea { anchors.fill: parent onClicked: selectedColor = parent.color } } Rectangle { color:"#ffcdf4" anchors.verticalCenter: parent.verticalCenter height: iconSizes.medium width: height radius: Math.max(height, width) border.color: borderColor MouseArea { anchors.fill: parent onClicked: selectedColor = parent.color } } Maui.ToolButton { iconName: "overflow-menu" } } } TextField { id: title Layout.fillWidth: true Layout.margins: space.medium height: 24 placeholderText: qsTr("Title") font.weight: Font.Bold font.bold: true + color: Qt.darker(selectedColor, 2.5) background: Rectangle { color: "transparent" } } DocumentHandler { id: document document: body.textDocument cursorPosition: body.cursorPosition selectionStart: body.selectionStart selectionEnd: body.selectionEnd // textColor: TODO -// onLoaded: { -// body.text = text -// } + // onLoaded: { + // body.text = text + // } onError: { body.text = message body.visible = true } } ScrollView { Layout.fillHeight: true Layout.fillWidth: true Layout.margins: space.medium TextArea { id: body placeholderText: qsTr("Body") selectByKeyboard :!isMobile selectByMouse : !isMobile textFormat : TextEdit.AutoText + color: Qt.darker(selectedColor, 2.5) background: Rectangle { color: "transparent" } } } Row { Layout.fillWidth: true width: parent.width Layout.margins: space.medium Layout.alignment: Qt.AlignRight spacing: space.medium Button { id: save text: qsTr("Save") onClicked: { - close() - noteSaved({ - title: title.text, - body: body.text, - color: selectedColor, - tags: "" - }) + if(body.text.length > 0) + noteSaved({ + title: title.text, + body: body.text, + color: selectedColor, + tags: "" + }) clearNote() - + close() } } Button { id: discard text: qsTr("Discard") onClicked: { close() clearNote() } } } } + onOpened: body.forceActiveFocus() + function clearNote() { title.clear() body.clear() } function fill(note) { document.load("qrc:/texteditor.html") title.text = note.title body.text = note.body selectedColor = note.color open() } - - function bold() - { -// body.sele - } }