From 17fb33475e6f3938113e6d28b53118d4f1181953 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Tue, 22 Jun 2021 19:20:07 +0200 Subject: [PATCH 1/8] Distraction free, Writing games Distraction free: header bar can now hide Writing games: forbid Ctrl+Z --- src/app/src/qml/RootPage.qml | 78 ++++++++++++++++- src/app/src/qml/RootPageForm.ui.qml | 83 +++++++++++-------- src/app/src/qml/main.qml | 13 ++- .../WritingGamesPageToolbox.qml | 6 +- 4 files changed, 141 insertions(+), 39 deletions(-) diff --git a/src/app/src/qml/RootPage.qml b/src/app/src/qml/RootPage.qml index cbd9f0030..8c9b5876f 100755 --- a/src/app/src/qml/RootPage.qml +++ b/src/app/src/qml/RootPage.qml @@ -18,7 +18,7 @@ RootPageForm { Component.onCompleted: { - populateProjectPageModel() + populateProjectPageModel() } //--------------------------------------------------------- @@ -279,6 +279,82 @@ RootPageForm { //------------------------------------------------------------------------- //-------------------------------------------------------------------------- //------------------------------------------------------------------------- + Connections{ + target: rootWindow.protectedSignals + function onFullScreenCalled(value){ + headerRowLayout.visible = !value + } + + + } + + Item{ + id: headerShowZone + + anchors.leftMargin: 50 + anchors.rightMargin: 50 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 10 + visible: !headerRowLayout.visible && rootWindow.isDistractionFree + TapHandler{ + onTapped: function(eventPoint){ + + if(eventPoint.event.device.type === PointerDevice.Mouse){ + Globals.touchUsed = false + } + if(eventPoint.event.device.type === PointerDevice.TouchScreen + | eventPoint.event.device.type === PointerDevice.Stylus){ + Globals.touchUsed = true + + if(headerStayVisibleTimer.running){ + headerStayVisibleTimer.stop() + } + headerStayVisibleTimer.start() + } + + + headerRowLayout.visible = true + } + } + HoverHandler{ + acceptedDevices: PointerDevice.Mouse + + onHoveredChanged: { + if(hovered){ + headerRowLayout.visible = true + } + } + } + } + + headerStayVisibleHoverHandler.enabled: headerRowLayout.visible && rootWindow.isDistractionFree + headerStayVisibleHoverHandler.onHoveredChanged: { + if(!headerStayVisibleHoverHandler.hovered){ // leaving + headerRowLayout.visible = false + } + } + + headerStayVisibleTapHandler.enabled: headerRowLayout.visible && rootWindow.isDistractionFree + headerStayVisibleTapHandler.onTapped: function(eventPoint){ + if(headerStayVisibleTimer.running){ + headerStayVisibleTimer.stop() + } + headerStayVisibleTimer.start() + } + + + + Timer{ + id: headerStayVisibleTimer + interval: 5000 + onTriggered: { + headerRowLayout.visible = false + } + } + + //------------------------------------------------------------ //------------------------------------------------------------ diff --git a/src/app/src/qml/RootPageForm.ui.qml b/src/app/src/qml/RootPageForm.ui.qml index 205562b9c..aad2a2599 100755 --- a/src/app/src/qml/RootPageForm.ui.qml +++ b/src/app/src/qml/RootPageForm.ui.qml @@ -8,8 +8,8 @@ import "Items" Item { id: rootPageBase property alias viewManager: multiViewArea.viewManager -// property int leftBasePreferredWidth: 0 -// property int rightBasePreferredWidth: 0 + // property int leftBasePreferredWidth: 0 + // property int rightBasePreferredWidth: 0 property alias mainMenuButton: mainMenuButton property alias showWelcomeButton: showWelcomeButton property alias baseForDrawers: baseForDrawers @@ -18,7 +18,9 @@ Item { property alias showLeftDockButton: showLeftDockButton property alias showRightDockButton: showRightDockButton property alias topToolBarRepeater: topToolBarRepeater - + property alias headerRowLayout: headerRowLayout + property alias headerStayVisibleHoverHandler: headerStayVisibleHoverHandler + property alias headerStayVisibleTapHandler: headerStayVisibleTapHandler property int showLeftDockButtonWidth: 30 property int showRightDockButtonWidth: 30 @@ -35,6 +37,19 @@ Item { Layout.preferredHeight: 30 Layout.fillWidth: true + + HoverHandler{ + id: headerStayVisibleHoverHandler + acceptedDevices: PointerDevice.Mouse + } + + TapHandler{ + id: headerStayVisibleTapHandler + acceptedDevices: PointerDevice.TouchScreen + | PointerDevice.Stylus + + + } SkrToolButton { id: showLeftDockButton @@ -70,8 +85,9 @@ Item { Breadcrumb { id: breadcrumb Layout.preferredHeight: 30 - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.alignment: Qt.AlignCenter Layout.fillWidth: true + Layout.minimumWidth: 300 } RowLayout{ @@ -86,6 +102,7 @@ Item { Item { id: stretcher Layout.fillWidth: true + Layout.minimumWidth: rootPageBase.width / 6 > 300 ? 300 : 0 } @@ -126,51 +143,51 @@ Item { id: columnLayout anchors.fill: parent anchors.leftMargin: ApplicationWindow.window.compactMode ? 0 : leftDrawer.width - * leftDrawer.position + * leftDrawer.position anchors.rightMargin: ApplicationWindow.window.compactMode ? 0 : rightDrawer.width - * rightDrawer.position - + * rightDrawer.position -// RowLayout { -// id: rowLayout -// Layout.fillHeight: true -// Layout.fillWidth: true -// spacing: 0 -// Item { -// id: leftBase -// Layout.preferredWidth: leftBasePreferredWidth -// Layout.maximumWidth: leftBasePreferredWidth -// visible: !Globals.compactMode -// Layout.fillHeight: true + // RowLayout { + // id: rowLayout + // Layout.fillHeight: true + // Layout.fillWidth: true + // spacing: 0 + // Item { + // id: leftBase + // Layout.preferredWidth: leftBasePreferredWidth + // Layout.maximumWidth: leftBasePreferredWidth + // visible: !Globals.compactMode + // Layout.fillHeight: true -// } + // } - Item { - id: middleBase - anchors.fill: parent - MultiViewArea{ - id: multiViewArea + Item { + id: middleBase + anchors.fill: parent - anchors.fill: parent - } + MultiViewArea{ + id: multiViewArea + anchors.fill: parent } + } + -// Item { -// id: rightBase -// Layout.preferredWidth: rightBasePreferredWidth -// visible: !Globals.compactMode -// Layout.fillHeight: true + // Item { + // id: rightBase + // Layout.preferredWidth: rightBasePreferredWidth + // visible: !Globals.compactMode + // Layout.fillHeight: true -// } + // } - // } + // } } } diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index 98b504ec8..be377f350 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -244,6 +244,14 @@ ApplicationWindow { anchors.fill: parent } + + + //------------------------------------------------------------------ + //---------Fullscreen--------- + //------------------------------------------------------------------ + + property bool isDistractionFree: false + Connections { target: protectedSignals function onFullScreenCalled(value) { @@ -255,12 +263,9 @@ ApplicationWindow { visibility = Window.AutomaticVisibility } SkrTheme.setDistractionFree(value) + rootWindow.isDistractionFree = value } } - - //------------------------------------------------------------------ - //---------Fullscreen--------- - //------------------------------------------------------------------ Action { id: fullscreenAction diff --git a/src/plugins/writingGamesPageToolbox/WritingGamesPageToolbox/WritingGamesPageToolbox.qml b/src/plugins/writingGamesPageToolbox/WritingGamesPageToolbox/WritingGamesPageToolbox.qml index fbc5718d3..9d096b107 100755 --- a/src/plugins/writingGamesPageToolbox/WritingGamesPageToolbox/WritingGamesPageToolbox.qml +++ b/src/plugins/writingGamesPageToolbox/WritingGamesPageToolbox/WritingGamesPageToolbox.qml @@ -50,8 +50,12 @@ WritingGamesPageToolboxForm { event.accepted = true return true - } + } + if((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_Z){ + event.accepted = true + return true + } return false } From 3d2b7825686198ee3ba86ab6e51a9a40f0195f78 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Tue, 22 Jun 2021 21:11:32 +0200 Subject: [PATCH 2/8] Icons Icons: fix some icons not following the theme --- src/app/src/qml/Commons/NewItemPopup.qml | 73 +++++---- .../src/qml/Commons/NewItemPopupForm.ui.qml | 1 + src/app/src/qml/Commons/RelationshipPanel.qml | 144 +++++++++--------- src/app/src/qml/RootPage.qml | 74 ++++++++- src/app/src/qml/RootPageForm.ui.qml | 33 +--- src/app/src/qml/main.qml | 2 +- 6 files changed, 193 insertions(+), 134 deletions(-) diff --git a/src/app/src/qml/Commons/NewItemPopup.qml b/src/app/src/qml/Commons/NewItemPopup.qml index 27add8701..50288e419 100755 --- a/src/app/src/qml/Commons/NewItemPopup.qml +++ b/src/app/src/qml/Commons/NewItemPopup.qml @@ -1,6 +1,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 +import "../Items" NewItemPopupForm { id: root @@ -44,18 +45,18 @@ NewItemPopupForm { listView.delegate: delegateComponent - //--------------------------------------------------------- createButton.enabled: chosenPageType !== "" + //--------------------------------------------------------- createButton.enabled: chosenPageType !== "" createButton.onClicked: { pageTypeChosen(chosenPageType) root.close() } -//--------------------------------------------------------- + //--------------------------------------------------------- cancelButton.onClicked: { root.close() } -//--------------------------------------------------------- + //--------------------------------------------------------- listView.onCurrentIndexChanged: { chosenPageType = listView.currentItem.type @@ -65,21 +66,28 @@ NewItemPopupForm { Component{ id: delegateComponent - ItemDelegate { + SkrListItemPane { id: itemDelegate width: parent.width - text: getPageTypeText(type) + height: 40 property string type: modelData - icon.source: getIconUrlFromPageType(type) - onClicked: { - itemDelegate.ListView.view.currentIndex = model.index - detailsTextArea.text = skrTreeManager.getPageDetailText(type) - chosenPageType = itemDelegate.type - } - onDoubleClicked: { - itemDelegate.ListView.view.currentIndex = model.index - pageTypeChosen(modelData) - root.close() + + Item{ + anchors.fill: parent + z:1 + TapHandler{ + onTapped: function(eventPoint){ + itemDelegate.ListView.view.currentIndex = model.index + detailsTextArea.text = skrTreeManager.getPageDetailText(type) + chosenPageType = itemDelegate.type + } + onDoubleTapped: function(eventPoint){ + itemDelegate.ListView.view.currentIndex = model.index + pageTypeChosen(modelData) + root.close() + } + + } } @@ -87,20 +95,27 @@ NewItemPopupForm { id: rowLayout spacing: 2 anchors.fill: parent - Rectangle { - id: currentItemIndicator - color: "#cccccc" - Layout.fillHeight: true - Layout.preferredWidth: 5 - visible: listView.currentIndex === model.index - } - - Item{ - id: stretcher - Layout.fillHeight: true - Layout.fillWidth: true - - } + Rectangle { + id: currentItemIndicator + color: "#cccccc" + Layout.fillHeight: true + Layout.preferredWidth: 5 + visible: listView.currentIndex === model.index + } + + RowLayout{ + Layout.fillHeight: true + Layout.fillWidth: true + + SkrToolButton{ + icon.source: getIconUrlFromPageType(type) + } + + SkrLabel{ + text: getPageTypeText(type) + } + + } } } diff --git a/src/app/src/qml/Commons/NewItemPopupForm.ui.qml b/src/app/src/qml/Commons/NewItemPopupForm.ui.qml index 7ebe1507e..f82fa1b6f 100755 --- a/src/app/src/qml/Commons/NewItemPopupForm.ui.qml +++ b/src/app/src/qml/Commons/NewItemPopupForm.ui.qml @@ -27,6 +27,7 @@ SkrPopup { focus: true Layout.preferredWidth: 150 Layout.fillHeight: true + spacing: 2 } diff --git a/src/app/src/qml/Commons/RelationshipPanel.qml b/src/app/src/qml/Commons/RelationshipPanel.qml index d2462cd0d..ea737c5df 100644 --- a/src/app/src/qml/Commons/RelationshipPanel.qml +++ b/src/app/src/qml/Commons/RelationshipPanel.qml @@ -594,97 +594,103 @@ RelationshipPanelForm { property string tip - SkrToolTip { - text: control.tip ? control.tip : text - visible: hoverHandler.hovered && text.length !== 0 - } + Item { + anchors.fill: parent + z: 1 + + SkrToolTip { + text: control.tip ? control.tip : text + visible: hoverHandler.hovered && text.length !== 0 + } - HoverHandler{ - id: hoverHandler - } + HoverHandler{ + id: hoverHandler + } - TapHandler{ - id: tapHandler - onSingleTapped: function(eventPoint) { - control.forceActiveFocus() - gridView.currentIndex = model.index + TapHandler{ + id: tapHandler - root.openTreeItemInPanel(projectId, model.treeItemId) + onSingleTapped: function(eventPoint) { + control.forceActiveFocus() + gridView.currentIndex = model.index - } + root.openTreeItemInPanel(projectId, model.treeItemId) + } - onDoubleTapped: function(eventPoint) { - control.forceActiveFocus() - gridView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - viewManager.loadTreeItem(root.projectId, model.treeItemId) - } - grabPermissions: PointerHandler.TakeOverForbidden + onDoubleTapped: function(eventPoint) { + control.forceActiveFocus() + gridView.currentIndex = model.index + priv.currentTreeItemId = model.treeItemId + viewManager.loadTreeItem(root.projectId, model.treeItemId) + } - } - TapHandler{ - id: middleClickTapHandler - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.MiddleButton + grabPermissions: PointerHandler.TakeOverForbidden - onSingleTapped: function(eventPoint) { - control.forceActiveFocus() - gridView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - viewManager.loadTreeItemAtAnotherView(root.projectId, model.treeItemId) } + TapHandler{ + id: middleClickTapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.MiddleButton - grabPermissions: PointerHandler.TakeOverForbidden + onSingleTapped: function(eventPoint) { + control.forceActiveFocus() + gridView.currentIndex = model.index + priv.currentTreeItemId = model.treeItemId + viewManager.loadTreeItemAtAnotherView(root.projectId, model.treeItemId) + } - } + grabPermissions: PointerHandler.TakeOverForbidden + + } - TapHandler { - id: rightClickTapHandler - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.RightButton - onSingleTapped: function(eventPoint) { - control.forceActiveFocus() - gridView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId + TapHandler { + id: rightClickTapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.RightButton + onSingleTapped: function(eventPoint) { + control.forceActiveFocus() + gridView.currentIndex = model.index + priv.currentTreeItemId = model.treeItemId - if (menu.visible) { - menu.close() - return + if (menu.visible) { + menu.close() + return + } + + menu.open() + eventPoint.accepted = true } - menu.open() - eventPoint.accepted = true + grabPermissions: PointerHandler.TakeOverForbidden } - grabPermissions: PointerHandler.TakeOverForbidden - } + //---------------------------------------------------------------------- + //-----------touch handler-------------------------------------------------- + //---------------------------------------------------------------------- + TapHandler { + id: touchHandler + acceptedDevices: PointerDevice.TouchScreen + acceptedPointerTypes: PointerDevice.Finger - //---------------------------------------------------------------------- - //-----------touch handler-------------------------------------------------- - //---------------------------------------------------------------------- - TapHandler { - id: touchHandler - acceptedDevices: PointerDevice.TouchScreen - acceptedPointerTypes: PointerDevice.Finger + onLongPressed: { + control.forceActiveFocus() + gridView.currentIndex = model.index + priv.currentTreeItemId = model.treeItemId - onLongPressed: { - control.forceActiveFocus() - gridView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId + if (menu.visible) { + menu.close() + return + } - if (menu.visible) { - menu.close() - return + menu.open() + eventPoint.accepted = true } - - menu.open() - eventPoint.accepted = true } } @@ -913,11 +919,11 @@ RelationshipPanelForm { anchors.fill: parent focus: true - Image { - source: skrTreeManager.getIconUrlFromPageType(type) + SkrToolButton { + icon.source: skrTreeManager.getIconUrlFromPageType(type) Layout.alignment: Qt.AlignHCenter | Qt.AlignTop - Layout.preferredHeight: 30 - Layout.preferredWidth: 30 + Layout.preferredHeight: 40 + Layout.preferredWidth: 40 } SkrLabel { text: title diff --git a/src/app/src/qml/RootPage.qml b/src/app/src/qml/RootPage.qml index 8c9b5876f..c85ff79ac 100755 --- a/src/app/src/qml/RootPage.qml +++ b/src/app/src/qml/RootPage.qml @@ -282,7 +282,13 @@ RootPageForm { Connections{ target: rootWindow.protectedSignals function onFullScreenCalled(value){ - headerRowLayout.visible = !value + if(value){ + hideHeaderRowLayout() + } + else { + showHeaderRowLayout() + } + } @@ -291,7 +297,7 @@ RootPageForm { Item{ id: headerShowZone - anchors.leftMargin: 50 + anchors.leftMargin: 0 anchors.rightMargin: 50 anchors.top: parent.top anchors.left: parent.left @@ -315,7 +321,7 @@ RootPageForm { } - headerRowLayout.visible = true + showHeaderRowLayout() } } HoverHandler{ @@ -323,7 +329,7 @@ RootPageForm { onHoveredChanged: { if(hovered){ - headerRowLayout.visible = true + showHeaderRowLayout() } } } @@ -332,7 +338,7 @@ RootPageForm { headerStayVisibleHoverHandler.enabled: headerRowLayout.visible && rootWindow.isDistractionFree headerStayVisibleHoverHandler.onHoveredChanged: { if(!headerStayVisibleHoverHandler.hovered){ // leaving - headerRowLayout.visible = false + hideHeaderRowLayout() } } @@ -350,11 +356,67 @@ RootPageForm { id: headerStayVisibleTimer interval: 5000 onTriggered: { - headerRowLayout.visible = false + hideHeaderRowLayout() } } + function showHeaderRowLayout() { +// if(SkrSettings.ePaperSettings.animationEnabled){ +// showHeaderAnimation.start() +// } +// else{ + headerRowLayout.visible = true +// } + } + function hideHeaderRowLayout() { + if(!rootWindow.isDistractionFree){ + return + } + +// if(SkrSettings.ePaperSettings.animationEnabled){ +// hideHeaderAnimation.start() +// } +// else{ + headerRowLayout.visible = false +// } + } + +// SequentialAnimation { +// id: hideHeaderAnimation + +// NumberAnimation{ +// //target: headerRowLayout +// property: "headerRowLayoutPreferredHeight" +// from: 30 +// to: 0 +// easing.type: Easing.InQuad +// duration: 10000 +// } +// PropertyAction { +// target: headerRowLayout +// property: "visible" +// value: false +// } +// } +// SequentialAnimation { +// id: showHeaderAnimation + +// PropertyAction { +// target: headerRowLayout +// property: "visible" +// value: true +// } +// NumberAnimation{ +// //target: headerRowLayout +// property: "headerRowLayoutPreferredHeight" +// from: 0 +// to: 30 +// easing.type: Easing.InQuad +// duration: 10000 +// } + +// } //------------------------------------------------------------ //------------------------------------------------------------ diff --git a/src/app/src/qml/RootPageForm.ui.qml b/src/app/src/qml/RootPageForm.ui.qml index aad2a2599..1978d63cc 100755 --- a/src/app/src/qml/RootPageForm.ui.qml +++ b/src/app/src/qml/RootPageForm.ui.qml @@ -19,6 +19,7 @@ Item { property alias showRightDockButton: showRightDockButton property alias topToolBarRepeater: topToolBarRepeater property alias headerRowLayout: headerRowLayout + property alias headerRowLayoutPreferredHeight: headerRowLayout.layoutPreferredHeight property alias headerStayVisibleHoverHandler: headerStayVisibleHoverHandler property alias headerStayVisibleTapHandler: headerStayVisibleTapHandler property int showLeftDockButtonWidth: 30 @@ -31,10 +32,10 @@ Item { RowLayout { id: headerRowLayout - width: 100 - height: 100 spacing: 0 - Layout.preferredHeight: 30 + + property int layoutPreferredHeight: 30 + Layout.preferredHeight: layoutPreferredHeight Layout.fillWidth: true @@ -148,21 +149,6 @@ Item { * rightDrawer.position - // RowLayout { - // id: rowLayout - // Layout.fillHeight: true - // Layout.fillWidth: true - // spacing: 0 - - // Item { - // id: leftBase - // Layout.preferredWidth: leftBasePreferredWidth - // Layout.maximumWidth: leftBasePreferredWidth - // visible: !Globals.compactMode - // Layout.fillHeight: true - - - // } Item { @@ -177,17 +163,6 @@ Item { } - - // Item { - // id: rightBase - // Layout.preferredWidth: rightBasePreferredWidth - // visible: !Globals.compactMode - // Layout.fillHeight: true - - // } - - - // } } } diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index be377f350..7ba7d3ce3 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -262,8 +262,8 @@ ApplicationWindow { } else { visibility = Window.AutomaticVisibility } - SkrTheme.setDistractionFree(value) rootWindow.isDistractionFree = value + SkrTheme.setDistractionFree(value) } } Action { From bdde9246a5af88c6167108e1dc01a60f604f1d73 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Tue, 22 Jun 2021 23:07:16 +0200 Subject: [PATCH 3/8] cleaning --- src/libskribisto-data/src/CMakeLists.txt | 2 +- .../src/tasks/sql/plmimporter.cpp | 60 ------------------- .../src/tasks/sql/plmimporter.h | 3 +- 3 files changed, 2 insertions(+), 63 deletions(-) diff --git a/src/libskribisto-data/src/CMakeLists.txt b/src/libskribisto-data/src/CMakeLists.txt index 4e9fd3514..37c362d51 100755 --- a/src/libskribisto-data/src/CMakeLists.txt +++ b/src/libskribisto-data/src/CMakeLists.txt @@ -102,7 +102,7 @@ set(QRC qt_add_resources(RESOURCES ${QRC}) # Tell CMake to create the executable -add_library(skribisto-data SHARED ${data_SRCS} ${data_HDR} ${models_SRCS} ${RESOURCES}) +add_library(skribisto-data SHARED ${data_SRCS} ${data_HDR} ${RESOURCES}) target_link_libraries(skribisto-data PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Xml) diff --git a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp index 86aecdcc7..42a9a2cdd 100755 --- a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp +++ b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp @@ -290,67 +290,7 @@ QSqlDatabase PLMImporter::createEmptySQLiteProject(int projectId, SKRResult& res } // ----------------------------------------------------------------------------------------------- -int PLMImporter::decompress(FILE *source, FILE *dest) -{ - int ret; - unsigned have; - z_stream strm; - unsigned char in[16384]; - unsigned char out[16384]; - - /* allocate inflate state */ - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); - - if (ret != Z_OK) return ret; - - /* decompress until deflate stream ends or end of file */ - do { - strm.avail_in = fread(in, 1, 16384, source); - - if (ferror(source)) { - (void)inflateEnd(&strm); - return Z_ERRNO; - } - - if (strm.avail_in == 0) break; - strm.next_in = in; - /* run inflate() on input until output buffer not full */ - do { - strm.avail_out = 16384; - strm.next_out = out; - ret = inflate(&strm, Z_NO_FLUSH); - assert(ret != Z_STREAM_ERROR); /* state not clobbered */ - - switch (ret) { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; /* and fall through */ - - case Z_DATA_ERROR: - case Z_MEM_ERROR: - (void)inflateEnd(&strm); - return ret; - } - have = 16384 - strm.avail_out; - - if ((fwrite(out, 1, have, dest) != have) || ferror(dest)) { - (void)inflateEnd(&strm); - return Z_ERRNO; - } - } while (strm.avail_out == 0); - - /* done when inflate() says it's done */ - } while (ret != Z_STREAM_END); - - /* clean up and return */ - (void)inflateEnd(&strm); - return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; -} SKRResult PLMImporter::importPlumeCreatorProject(const QUrl& plumeFileName, const QUrl& skribistoFileName) { diff --git a/src/libskribisto-data/src/tasks/sql/plmimporter.h b/src/libskribisto-data/src/tasks/sql/plmimporter.h index c1cb2a781..7dc434297 100755 --- a/src/libskribisto-data/src/tasks/sql/plmimporter.h +++ b/src/libskribisto-data/src/tasks/sql/plmimporter.h @@ -88,8 +88,7 @@ public slots: // used to track old plume id with new skribisto note id QHashm_attendanceConversionHash; - int decompress(FILE *source, - FILE *dest); + }; #endif // PLMIMPORTER_H From 367560203e449b764fd8811fdb18be15798bdc41 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 23 Jun 2021 22:35:05 +0200 Subject: [PATCH 4/8] Navigation Navigation: fix restore --- .../NavigationProjectToolbox/Navigation.qml | 2 -- .../NavigationProjectToolbox.qml | 9 +-------- .../NavigationProjectToolbox/RestoreListView.qml | 11 ++++++++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml index c5f02e2ab..4de73b657 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml @@ -16,7 +16,6 @@ NavigationForm { signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) signal openDocumentInAnotherView(int projectId, int treeItemId) signal openDocumentInNewWindow(int projectId, int treeItemId) - signal restoreDocumentList(int projectId, var treeItemIdList) function setNavigationTreeItemId(projectId, treeItemId){ stackView.get(0, StackView.DontLoad).setCurrentTreeItemId(projectId, treeItemId) @@ -115,7 +114,6 @@ NavigationForm { restoreListView.openDocument.connect(root.openDocument) restoreListView.openDocumentInAnotherView.connect(root.openDocumentInAnotherView) restoreListView.openDocumentInNewWindow.connect(root.openDocumentInNewWindow) - restoreListView.restoreDocumentList.connect(root.restoreDocumentList) restoreListView.goBack.connect(root.popRestoreListView) } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml index 2843d0f8b..5b6695cb8 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml @@ -87,14 +87,7 @@ SkrToolbox { function restoreTreeItemList(projectId, treeItemIdList) { - // restore is difficult to explain : a restored parent will restore its children, even those trashed years before. To avoid that, - // children trashed at the same minute will be checked to allow restore. The older ones will stay unchecked by default. - // All that is done in RestoreView.qml - var i - for (i = 0; i < treeItemIdList.length; i++) { - skrData.treeHub().untrashOnlyOneTreeItem(projectId, - treeItemIdList[i]) - } + //console.log("restored: sheet:", sheetIdList) } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml index 24ee8108f..1ace3042c 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml @@ -22,7 +22,6 @@ RestoreListViewForm { signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) signal openDocumentInAnotherView(int projectId, int treeItemId) signal openDocumentInNewWindow(int projectId, int treeItemId) - signal restoreDocumentList(int projectId, var treeItemIdList) property var trashedChildrenList: [] onTrashedChildrenListChanged: { @@ -100,8 +99,14 @@ RestoreListViewForm { } onTriggered: { var treeItemIdListToBeFinallyRestored = listView.getCheckedTreeItemIdList() - - restoreDocumentList(currentProjectId, treeItemIdListToBeFinallyRestored) + // restore is difficult to explain : a restored parent will restore its children, even those trashed years before. To avoid that, + // children trashed at the same minute will be checked to allow restore. The older ones will stay unchecked by default. + // All that is done in RestoreView.qml + var i + for (i = 0; i < treeItemIdListToBeFinallyRestored.length; i++) { + skrData.treeHub().untrashOnlyOneTreeItem(currentProjectId, + treeItemIdListToBeFinallyRestored[i]) + } //console.log('finally restore list', currentProjectId, treeItemIdListToBeFinallyRestored) } From 8143651e3edd1ce0e247613aa66296b9a1a6738a Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Thu, 24 Jun 2021 15:04:51 +0200 Subject: [PATCH 5/8] Plugin Plugin: migrate PlumeCreatorImporter to a plugin Plugin: create plugin interface for importer --- src/app/src/CMakeLists.txt | 1 + src/app/src/qml.qrc | 2 - src/app/src/qml/Commons/TextContextMenu.qml | 1 + src/app/src/qml/WelcomePage/ImporterPage.qml | 69 +- .../qml/WelcomePage/ImporterPageForm.ui.qml | 15 +- src/app/src/skrimporterinterface.h | 50 ++ src/app/src/skrrootitem.cpp | 101 +++ src/app/src/skrrootitem.h | 5 + src/libskribisto-data/src/CMakeLists.txt | 30 - src/libskribisto-data/src/plmprojecthub.cpp | 15 - src/libskribisto-data/src/plmprojecthub.h | 2 - .../src/tasks/sql/plmimporter.cpp | 728 ------------------ .../src/tasks/sql/plmimporter.h | 37 - src/plugins/CMakeLists.txt | 1 + .../NavigationProjectToolbox/Navigation.qml | 1 - .../plumeCreatorImporter/CMakeLists.txt | 91 +++ .../PlumeCreatorImporter.qml} | 32 +- .../PlumeCreatorImporterForm.ui.qml} | 12 +- src/plugins/plumeCreatorImporter/plugin.cpp | 41 + src/plugins/plumeCreatorImporter/plugin.h | 73 ++ .../plumeCreatorImporter/plugin_info.json | 9 + .../plumeCreatorImporter/plugin_qml.qrc | 6 + .../skrplumecreatorimporter.cpp | 724 +++++++++++++++++ .../skrplumecreatorimporter.h | 55 ++ src/translations/skribisto_en_US.ts | 21 +- 25 files changed, 1268 insertions(+), 854 deletions(-) create mode 100644 src/app/src/skrimporterinterface.h create mode 100755 src/plugins/plumeCreatorImporter/CMakeLists.txt rename src/{app/src/qml/WelcomePage/PlumeImporter.qml => plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporter.qml} (84%) rename src/{app/src/qml/WelcomePage/PlumeImporterForm.ui.qml => plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporterForm.ui.qml} (97%) create mode 100755 src/plugins/plumeCreatorImporter/plugin.cpp create mode 100755 src/plugins/plumeCreatorImporter/plugin.h create mode 100755 src/plugins/plumeCreatorImporter/plugin_info.json create mode 100755 src/plugins/plumeCreatorImporter/plugin_qml.qrc create mode 100644 src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp create mode 100644 src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h diff --git a/src/app/src/CMakeLists.txt b/src/app/src/CMakeLists.txt index 6754a4a78..ed77c1abc 100755 --- a/src/app/src/CMakeLists.txt +++ b/src/app/src/CMakeLists.txt @@ -109,6 +109,7 @@ set(app_SRCS skrshortcutmanager.h skrshortcutmanager.cpp skrprojectpageinterface.h + skrimporterinterface.h ) set(QML qml.qrc diff --git a/src/app/src/qml.qrc b/src/app/src/qml.qrc index 18ed5f48c..c13025f8f 100755 --- a/src/app/src/qml.qrc +++ b/src/app/src/qml.qrc @@ -83,9 +83,7 @@ qml/WelcomePage/About.qml qml/WelcomePage/ImporterPage.qml qml/WelcomePage/FileMenuPageForm.ui.qml - qml/WelcomePage/PlumeImporterForm.ui.qml qml/WelcomePage/ProjectPageForm.ui.qml - qml/WelcomePage/PlumeImporter.qml qml/WelcomePage/ImporterPageForm.ui.qml qml/Commons/NewItemPopup.qml qml/Commons/NewItemPopupForm.ui.qml diff --git a/src/app/src/qml/Commons/TextContextMenu.qml b/src/app/src/qml/Commons/TextContextMenu.qml index a3173c2c7..8a071302c 100755 --- a/src/app/src/qml/Commons/TextContextMenu.qml +++ b/src/app/src/qml/Commons/TextContextMenu.qml @@ -124,6 +124,7 @@ TextContextMenuForm { ColumnLayout{ anchors.fill: parent + spacing: 20 GridLayout{ columns: 4 diff --git a/src/app/src/qml/WelcomePage/ImporterPage.qml b/src/app/src/qml/WelcomePage/ImporterPage.qml index 0b7c16f36..f006ed531 100755 --- a/src/app/src/qml/WelcomePage/ImporterPage.qml +++ b/src/app/src/qml/WelcomePage/ImporterPage.qml @@ -1,28 +1,73 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 +import "../Items" ImporterPageForm { signal closeCalled() - importFromPlumeToolButton.icon.source: "qrc:/pics/skribisto.svg" - importFromPlumeToolButton.icon.color: "transparent" - importFromPlumeToolButton.onClicked: { - var item = stackView.push("PlumeImporter.qml", StackView.Immediate) - item.closeCalled.connect(closeCalled) - item.forceActiveFocus() - item.onGoBackButtonClicked.connect(goBackToImporterMainPage) + function goBackToImporterMainPage(){ + stackView.pop() } +// onActiveFocusChanged: { +// if (activeFocus) { +// importFromPlumeToolButton.forceActiveFocus() +// } +// } - function goBackToImporterMainPage(){ - stackView.pop() +Component.onCompleted: { + populateImporterButtonModel() +} + + //-------------------------------------------------------------- + //----- Top toolbar----------------------------------------------- + //-------------------------------------------------------------- + + + ListModel { + id: importerButtonModel + } + + + function populateImporterButtonModel(){ + importerButtonModel.clear() + + var list = skrRootItem.findImporterNames() + + for(var i in list){ + var name = list[i] + var iconSource = skrRootItem.findImporterIconSource(name) + var buttonText = skrRootItem.findImporterButtonText(name) + var qmlUrl = skrRootItem.findImporterUrl(name) + + importerButtonModel.append({ "name": name, "qmlUrl": qmlUrl, "iconSource": iconSource, "buttonText": buttonText}) + } } - onActiveFocusChanged: { - if (activeFocus) { - importFromPlumeToolButton.forceActiveFocus() + + importerButtonRepeater.model: importerButtonModel + + importerButtonRepeater.delegate: SkrButton { + property string name: model.name + text: model.buttonText + + icon.source: model.iconSource + icon.color: "transparent" + icon.height: 90 + icon.width: 90 + + + onClicked: { + var item = stackView.push(model.qmlUrl, StackView.Immediate) + item.closeCalled.connect(closeCalled) + item.forceActiveFocus() + + item.onGoBackButtonClicked.connect(goBackToImporterMainPage) + } + + } } diff --git a/src/app/src/qml/WelcomePage/ImporterPageForm.ui.qml b/src/app/src/qml/WelcomePage/ImporterPageForm.ui.qml index 7dbf62c6c..b8c30b415 100755 --- a/src/app/src/qml/WelcomePage/ImporterPageForm.ui.qml +++ b/src/app/src/qml/WelcomePage/ImporterPageForm.ui.qml @@ -6,7 +6,8 @@ import "../Commons" import ".." Item { - property alias importFromPlumeToolButton: importFromPlumeToolButton + property alias importerButtonRepeater: importerButtonRepeater + property alias stackView: stackView StackView { @@ -35,20 +36,14 @@ Item { } } - - - SkrButton { - id: importFromPlumeToolButton - text: qsTr("Import from Plume Creator project") - - icon.height: 90 - icon.width: 90 - + Repeater { + id: importerButtonRepeater Layout.minimumHeight: 100 Layout.minimumWidth: 200 Layout.fillWidth: true Layout.maximumWidth: 500 Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + } Item { diff --git a/src/app/src/skrimporterinterface.h b/src/app/src/skrimporterinterface.h new file mode 100644 index 000000000..4fa702685 --- /dev/null +++ b/src/app/src/skrimporterinterface.h @@ -0,0 +1,50 @@ +/*************************************************************************** +* Copyright (C) 2021 by Cyril Jacquet * +* cyril.jacquet@skribisto.eu * +* * +* Filename: skrimporterinterface.h +* * +* This file is part of Skribisto. * +* * +* Skribisto 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, or * +* (at your option) any later version. * +* * +* Skribisto 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 Skribisto. If not, see . * +***************************************************************************/ + +#ifndef SKRIMPORTERINTERFACE_H +#define SKRIMPORTERINTERFACE_H + +#include +#include "skrresult.h" +#include "skrcoreinterface.h" + + +class SKRImporterInterface : public SKRCoreInterface { +public: + + virtual ~SKRImporterInterface() {} + + + virtual QString iconSource() const = 0; + virtual QString buttonText() const = 0; + virtual QString qmlUrl() const = 0; + virtual int weight() const { + return 500; + } +}; + +#define SKRImporterInterface_iid "com.skribisto.ImporterInterface/1.0" + + +Q_DECLARE_INTERFACE(SKRImporterInterface, SKRImporterInterface_iid) + +#endif // SKRIMPORTERINTERFACE_H diff --git a/src/app/src/skrrootitem.cpp b/src/app/src/skrrootitem.cpp index c75637778..989656d43 100755 --- a/src/app/src/skrrootitem.cpp +++ b/src/app/src/skrrootitem.cpp @@ -1,6 +1,7 @@ #include "skrrootitem.h" #include "plmutils.h" #include "skrdata.h" +#include "skrimporterinterface.h" #include #include #include @@ -10,6 +11,8 @@ SKRRootItem::SKRRootItem(QObject *parent) : QObject(parent) { + skrdata->pluginHub()->addPluginType(); + skribistoTranslator = new QTranslator(this); qtTranslator = new QTranslator(this); } @@ -325,3 +328,101 @@ void SKRRootItem::removeFile(const QString& fileName) { file.remove(); } + +// -------------------------------------------------------------------------------------- +// ------------Import +// -------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- + +QStringList SKRRootItem::findImporterNames() const +{ + QList pluginList = + skrdata->pluginHub()->pluginsByType(); + + // reorder by weight, lightest is top, heavier is last + + std::sort(pluginList.begin(), pluginList.end(), + [](SKRImporterInterface *plugin1, SKRImporterInterface + *plugin2) -> bool { + return plugin1->weight() < plugin2->weight(); + } + ); + + QStringList list; + + for (SKRImporterInterface *plugin: qAsConst(pluginList)) { + list << plugin->name(); + } + + + return list; +} + +// --------------------------------------------------------------------------------- + +QString SKRRootItem::findImporterIconSource(const QString& name) const +{ + QList pluginList = + skrdata->pluginHub()->pluginsByType(); + + + QString icon; + + for (SKRImporterInterface *plugin: qAsConst(pluginList)) { + if (plugin->name() == name) { + icon = plugin->iconSource(); + break; + } + } + + + return icon; +} + +// --------------------------------------------------------------------------- + +QString SKRRootItem::findImporterButtonText(const QString& name) const +{ + QList pluginList = + skrdata->pluginHub()->pluginsByType(); + + + QString text; + + for (SKRImporterInterface *plugin: qAsConst(pluginList)) { + if (plugin->name() == name) { + text = plugin->buttonText(); + break; + } + } + + + return text; +} + +// --------------------------------------------------------------------------------- + + +QString SKRRootItem::findImporterUrl(const QString& name) const +{ + QList pluginList = skrdata->pluginHub()->pluginsByType(); + + // reorder by weight, lightest is top, heavier is last + + std::sort(pluginList.begin(), + pluginList.end(), + [](SKRImporterInterface *plugin1, SKRImporterInterface *plugin2) -> bool { + return plugin1->weight() < plugin2->weight(); + } + ); + + QString url; + + for (SKRImporterInterface *plugin: qAsConst(pluginList)) { + if (plugin->name() == name) { + url = plugin->qmlUrl(); + } + } + + return url; +} diff --git a/src/app/src/skrrootitem.h b/src/app/src/skrrootitem.h index ef200bacc..bfa62e4ec 100755 --- a/src/app/src/skrrootitem.h +++ b/src/app/src/skrrootitem.h @@ -65,6 +65,11 @@ class SKRRootItem : public QObject { Q_INVOKABLE QString getNativeCountryNameFromLocale(const QString& name) const; Q_INVOKABLE QString getNativeLanguageNameFromLocale(const QString& name) const; + Q_INVOKABLE QStringList findImporterNames() const; + Q_INVOKABLE QString findImporterIconSource(const QString& name) const; + Q_INVOKABLE QString findImporterButtonText(const QString& name) const; + Q_INVOKABLE QString findImporterUrl(const QString& name) const; + signals: void currentTranslationLanguageCodeChanged(const QString& langCode); diff --git a/src/libskribisto-data/src/CMakeLists.txt b/src/libskribisto-data/src/CMakeLists.txt index 37c362d51..6c2befce7 100755 --- a/src/libskribisto-data/src/CMakeLists.txt +++ b/src/libskribisto-data/src/CMakeLists.txt @@ -108,36 +108,6 @@ target_link_libraries(skribisto-data PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_ -#-------------- QUAZIP -------------------- - - - - if(${SKR_DEV}) - - message("-------------- SKR_DEV is ON ---------------------") - set(EXTERNAL_INSTALL_LOCATION ${CMAKE_SOURCE_DIR}/../build_skribisto_Release/3rdparty) - - set(QUAZIP_QT_MAJOR_VERSION ${QT_MAJOR_VERSION}) - - if(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) - set(QUAZIP_LIB_DIR ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) - endif(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) - if(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) - set(QUAZIP_LIB_DIR ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) - endif(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) - - set(QuaZip-Qt${QT_VERSION_MAJOR}_DIR ${QUAZIP_LIB_DIR}/cmake/QuaZip-Qt${QT_VERSION_MAJOR}-1.1) - set(ZLIB_ROOT ${EXTERNAL_INSTALL_LOCATION}/zlib) - message("-------------- QuaZip-Qt${QT_VERSION_MAJOR}_DIR = ${QuaZip-Qt${QT_VERSION_MAJOR}_DIR}---------------------") - endif(${SKR_DEV}) - - - - -find_package(QuaZip-Qt${QT_VERSION_MAJOR} REQUIRED) -target_link_libraries(skribisto-data PRIVATE QuaZip::QuaZip) -target_include_directories(skribisto-data SYSTEM PUBLIC ${HUNSPELL_INCLUDE_DIRS}) - #--------------------------------- diff --git a/src/libskribisto-data/src/plmprojecthub.cpp b/src/libskribisto-data/src/plmprojecthub.cpp index bfaf38e65..5884ad405 100755 --- a/src/libskribisto-data/src/plmprojecthub.cpp +++ b/src/libskribisto-data/src/plmprojecthub.cpp @@ -650,21 +650,6 @@ QString PLMProjectHub::getProjectUniqueId(int projectId) const // ---------------------------------------------------------------------------- -SKRResult PLMProjectHub::importPlumeCreatorProject(const QUrl& plumeURL, const QUrl& skribistoFileURL) -{ - PLMImporter importer; - - SKRResult result = importer.importPlumeCreatorProject(plumeURL, skribistoFileURL); - - IFKO(result) { - emit errorSent(result); - } - - return result; -} - -// ---------------------------------------------------------------------------- - SKRResult PLMProjectHub::set(int projectId, const QString & fieldName, const QVariant& value, diff --git a/src/libskribisto-data/src/plmprojecthub.h b/src/libskribisto-data/src/plmprojecthub.h index e6b1594b5..3a1fa5497 100755 --- a/src/libskribisto-data/src/plmprojecthub.h +++ b/src/libskribisto-data/src/plmprojecthub.h @@ -91,8 +91,6 @@ class EXPORT PLMProjectHub : public QObject { Q_INVOKABLE bool isThisProjectABackup(int projectId); QString getProjectType(int projectId) const; - Q_INVOKABLE SKRResult importPlumeCreatorProject(const QUrl& plumeURL, - const QUrl& skribistoFileURL); SKRResult set(int projectId, const QString & fieldName, diff --git a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp index 42a9a2cdd..806f85bf8 100755 --- a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp +++ b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp @@ -25,24 +25,6 @@ #include "skrdata.h" #include "skrsqltools.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// #include PLMImporter::PLMImporter(QObject *parent) : QObject(parent) @@ -289,716 +271,6 @@ QSqlDatabase PLMImporter::createEmptySQLiteProject(int projectId, SKRResult& res return sqlDb; } -// ----------------------------------------------------------------------------------------------- - - -SKRResult PLMImporter::importPlumeCreatorProject(const QUrl& plumeFileName, const QUrl& skribistoFileName) -{ - QString sqlFile = ":/sql/sqlite_project_1_6.sql"; - SKRResult result = skrdata->projectHub()->createSilentlyNewSpecificEmptyProject(skribistoFileName, sqlFile); - - int projectId = -2; - - IFOK(result) { - projectId = result.getData("projectId", -2).toInt(); - } - - if (projectId == -2) { - result = SKRResult(SKRResult::Critical, this, "plume_cant_create_empty_project"); - return result; - } - - // create temp file - QTemporaryDir tempDir; - - tempDir.setAutoRemove(true); - - if (!tempDir.isValid()) { - result = SKRResult(SKRResult::Critical, this, "plume_no_temp_dir"); - return result; - } - - QString tempDirPath = tempDir.path(); - - // extract zip - - JlCompress::extractDir(plumeFileName.toLocalFile(), tempDirPath); - - // QFile qf(plumeFileName.toLocalFile()); - // qf.open(QIODevice::ReadOnly); - // int fd = qf.handle(); - // FILE *plumeFile = fdopen(dup(fd), "rb"); - - - // // QFile tf(plumeFileName.toLocalFile()); - // // tf.open(QIODevice::ReadOnly); - // FILE *fp; - // QByteArray ba = tempDirPath.toLocal8Bit(); - // const char *c_str2 = ba.data(); - // fp = fopen(c_str2, "w"); - - // // int fd2 = tf.handle(); - // // FILE *t = fdopen(dup(fd2), "rb"); - - // decompress(plumeFile, fp); - // fclose(plumeFile); // correct - // fclose(fp); - - // ----------- create text folder--------------------------------------- - - - result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "text_folder_creation"); - return result; - } - int textFolderId = result.getData("treeItemId", -2).toInt(); - - IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, textFolderId, 1000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, textFolderId, "text")); - - - // ----------- create attend folder--------------------------------------- - - - result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "attend_folder_creation"); - return result; - } - int attendFolderId = result.getData("treeItemId", -2).toInt(); - - IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, attendFolderId, 80000000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, attendFolderId, "attendance")); - - - // ----------- create note folder--------------------------------------- - - - result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "note_creation"); - return result; - } - int noteFolderId = result.getData("treeItemId", -2).toInt(); - - IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, noteFolderId, 90000000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteFolderId, "note")); - - - // ----------------------------- read - // attend--------------------------------------- - - QFileInfo attendFileInfo(tempDirPath + "/attendance"); - - if (!attendFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_attend_file"); - return result; - } - - - QFile attendFile(attendFileInfo.absoluteFilePath()); - - if (!attendFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_attend_file"); - return result; - } - - - QByteArray attendLines = attendFile.readAll(); - - QXmlStreamReader attendXml(attendLines); - - - m_attendanceConversionHash.clear(); - - - while (attendXml.readNextStartElement() && attendXml.name().toString() == "plume-attendance") { - QStringList rolesNames = attendXml.attributes().value("rolesNames").toString().split("--", Qt::SkipEmptyParts); - QStringList levelsNames = - attendXml.attributes().value("levelsNames").toString().split("--", Qt::SkipEmptyParts); - - QStringList box_1Names = attendXml.attributes().value("box_1").toString().split("--", Qt::SkipEmptyParts); - QStringList box_2Names = attendXml.attributes().value("box_2").toString().split("--", Qt::SkipEmptyParts); - QStringList box_3Names = attendXml.attributes().value("box_3").toString().split("--", Qt::SkipEmptyParts); - - QStringList spinBox_1Names = - attendXml.attributes().value("spinBox_1").toString().split("--", Qt::SkipEmptyParts); - - - while (attendXml.readNextStartElement() && attendXml.name().toString() == "group") { - int attendNumber = attendXml.attributes().value("number").toInt(); - QString groupName = attendXml.attributes().value("name").toString(); - result = this->createNote(projectId, 1, attendNumber, groupName, tempDirPath + "/attend/A", attendFolderId); - IFOK(result) { - int groupNoteId = result.getData("treeItemId", -2).toInt(); - - m_attendanceConversionHash.insert(attendNumber, groupNoteId); - - result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_1", box_1Names); - result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_2", box_2Names); - result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_3", box_3Names); - result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "spinBox_1", spinBox_1Names); - } - - - while (attendXml.readNextStartElement() && attendXml.name().toString() == "obj") { - int attendNumber = attendXml.attributes().value("number").toInt(); - QString objName = attendXml.attributes().value("name").toString(); - result = - this->createNote(projectId, 2, attendNumber, objName, tempDirPath + "/attend/A", attendFolderId); - IFOK(result) { - int objNoteId = result.getData("treeItemId", -2).toInt(); - - m_attendanceConversionHash.insert(attendNumber, objNoteId); - - - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_1", box_1Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_2", box_2Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_3", box_3Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "spinBox_1", spinBox_1Names); - - QString objQuickDetail = attendXml.attributes().value("quickDetails").toString(); - - skrdata->treePropertyHub()->setProperty(projectId, objNoteId, "label", objQuickDetail); - } - - attendXml.readElementText(); - } - } - } - - - // ----------------------------- read - // tree--------------------------------------- - - - QFileInfo treeFileInfo(tempDirPath + "/tree"); - - if (!treeFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_tree_file"); - result.addData("filePath", treeFileInfo.absoluteFilePath()); - return result; - } - - - QFile treeFile(treeFileInfo.absoluteFilePath()); - - if (!treeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_tree_file"); - result.addData("filePath", treeFileInfo.absoluteFilePath()); - return result; - } - - - QByteArray lines = treeFile.readAll(); - - QXmlStreamReader xml(lines); - - - while (xml.readNextStartElement() && xml.name().toString() == "plume-tree") { - // pick project name - QString projectName = xml.attributes().value("projectName").toString(); - IFOKDO(result, skrdata->projectHub()->setProjectName(projectId, projectName)); - - - while (xml.readNextStartElement()) { - if (xml.name().toString() == "trash") { - xml.skipCurrentElement(); - continue; - } - - if (xml.name().toString() == "book") { - IFOKDO(result, - this->readXMLRecursivelyAndCreatePaper(projectId, 1, &xml, tempDirPath, textFolderId, - noteFolderId)); - } - } - - while (xml.readNext() != QXmlStreamReader::EndDocument) {} - } - - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "error_while_exploiting_tree"); - return result; - } - - if (xml.hasError() && (xml.error() != QXmlStreamReader::PrematureEndOfDocumentError)) { - result = SKRResult(SKRResult::Critical, this, "error_in_tree_xml"); - - result.addData("xmlError", QString("%1\nLine %2, column %3") - .arg(xml.errorString()) - .arg(xml.lineNumber()) - .arg(xml.columnNumber())); - - return result; - } - - - // ----------------------------- user dict------ - // ----------------------------- - - QFileInfo dictFileInfo(tempDirPath + "/dicts/userDict.dict_plume"); - - if (!dictFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_dict_file"); - result.addData("filePath", dictFileInfo.absoluteFilePath()); - return result; - } - - - QFile dictFile(dictFileInfo.absoluteFilePath()); - - if (!dictFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_dict_file"); - result.addData("filePath", dictFileInfo.absoluteFilePath()); - result.addData("fileError", dictFile.error()); - return result; - } - - - QByteArray dictLines = dictFile.readAll(); - - QString dictString = QString::fromUtf8(dictLines); - - QStringList dictWords = dictString.split(";", Qt::SkipEmptyParts); - - skrdata->projectDictHub()->setProjectDictList(projectId, dictWords); - - - // transform texts with children in folders - - IFOKDO(result, transformParentsToFolder(projectId)); - - - // save - - IFOKDO(result, skrdata->projectHub()->saveProject(projectId)); - - QUrl url = skrdata->projectHub()->getPath(projectId); - - IFOKDO(result, skrdata->projectHub()->closeProject(projectId)); - - IFOKDO(result, skrdata->projectHub()->loadProject(url)); - - return result; -} - -// ----------------------------------------------------------- - - -SKRResult PLMImporter::transformParentsToFolder(int projectId) { - SKRResult result; - QSqlDatabase sqlDb = plmProjectManager->project(projectId)->getSqlDb(); - - - // retrieve sorted id with indent list - - QList treeIdList; - QList treeIndentList; - QList treeSortOrderList; - QList typeList; - QSqlQuery query(sqlDb); - QString queryStr = "SELECT l_tree_id, l_indent, l_sort_order, t_type FROM tbl_tree ORDER BY l_sort_order"; - - - query.prepare(queryStr); - - - query.exec(); - - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); - - return result; - } - - while (query.next()) { - treeIdList.append(query.value(0).toInt()); - treeIndentList.append(query.value(1).toInt()); - treeSortOrderList.append(query.value(2).toInt()); - typeList.append(query.value(2).toString()); - } - - // remove project item (the first one) - treeIdList.takeFirst(); - treeIndentList.takeFirst(); - treeSortOrderList.takeFirst(); - typeList.takeFirst(); - - // remove folders - - QList indexesToRemove; - - for (int k = 0; k < typeList.count(); k++) { - if (typeList.at(k) == "TEXT") { - indexesToRemove.append(k); - } - } - - for (int m = indexesToRemove.count() - 1; m >= 0; m++) { - treeIdList.removeAt(m); - treeIndentList.removeAt(m); - treeSortOrderList.removeAt(m); - typeList.removeAt(m); - } - - - // create folders - - sqlDb.transaction(); - - QString treeQueryStr = "INSERT INTO tbl_tree (t_title, l_indent, l_sort_order, t_type, dt_trashed, b_trashed)" - " VALUES (" - " (SELECT t_title FROM tbl_tree WHERE l_tree_id = :treeId)," - " (SELECT l_indent FROM tbl_tree WHERE l_tree_id = :treeId)," - " :newSortOrder," - " 'FOLDER'," - " (SELECT dt_trashed FROM tbl_tree WHERE l_tree_id = :treeId)," - " (SELECT b_trashed FROM tbl_tree WHERE l_tree_id = :treeId)" - ")"; - - - QString incrementIndentQueryStr = "UPDATE tbl_tree SET l_indent = :newIndent WHERE l_tree_id = :treeId"; - - int previousIdIndent = -1; - - for (int i = treeIdList.count() - 1; i >= 0; i--) { - int currentTreeId = treeIdList.at(i); - int currentIndent = treeIndentList.at(i); - - if (i < treeIdList.count() - 1) { - previousIdIndent = treeIndentList.at(i + 1); - } - - - if (previousIdIndent > currentIndent) { - int currentSortOrder = treeSortOrderList.at(i); - - - // create folder - query.prepare(treeQueryStr); - - query.bindValue(":treeId", currentTreeId); - query.bindValue(":newSortOrder", currentSortOrder - 1); - - query.exec(); - - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); - result.addData("treeId", currentTreeId); - result.addData("newSortOrder", currentSortOrder - 1); - sqlDb.rollback(); - - return result; - } - - int newFolderTreeId = query.lastInsertId().toInt(); - - // increment current item's indent - - query.prepare(incrementIndentQueryStr); - - query.bindValue(":treeId", currentTreeId); - query.bindValue(":newIndent", currentIndent + 1); - - query.exec(); - - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); - result.addData("treeId", currentTreeId); - result.addData("sortOrder", currentSortOrder); - result.addData("newIndent", currentIndent + 1); - sqlDb.rollback(); - - return result; - } - } - } - - IFOK(result) { - sqlDb.commit(); - } - - - return result; -} - -// ----------------------------------------------------------- - -SKRResult PLMImporter::readXMLRecursivelyAndCreatePaper(int projectId, - int indent, - QXmlStreamReader *xml, - const QString& tempDirPath, - int textFolderId, int noteFolderId) -{ - SKRResult result; - - if (!xml->readNextStartElement()) { - xml->readElementText(); - return result; - } - else { - if (xml->name().toString() == "separator") { - xml->skipCurrentElement(); - } - else { - IFOKDO(result, - this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); - IFOKDO(result, - readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, - noteFolderId)); - } - } - - while (xml->readNextStartElement()) { - if (xml->name().toString() == "separator") { - xml->skipCurrentElement(); - continue; - } - IFOKDO(result, - this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); - IFOKDO(result, - readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, noteFolderId)); - } - - return result; -} - -SKRResult PLMImporter::createPapersAndAssociations(int projectId, - int indent, - const QXmlStreamReader& xml, - const QString& tempDirPath, - int textFolderId, int noteFolderId) -{ - SKRResult result(this); - - int plumeId = xml.attributes().value("number").toInt(); - QString name = xml.attributes().value("name").toString(); - - // create sheet - - result = skrdata->treeHub()->addChildTreeItem(projectId, textFolderId, "TEXT"); - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "text_creation"); - return result; - } - int sheetId = result.getData("treeItemId", -2).toInt(); - - int textFolderIndent = skrdata->treeHub()->getIndent(projectId, textFolderId); - - IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, textFolderIndent + indent)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, name)); - IFOKDO(result, skrdata->treeHub()->setType(projectId, sheetId, "TEXT")); - - - // - fetch text - - QFileInfo textFileInfo(tempDirPath + "/text/T" + QString::number(plumeId) + ".html"); - - if (!textFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_text_file"); - result.addData("filePath", textFileInfo.absoluteFilePath()); - return result; - } - - - QFile textFile(textFileInfo.absoluteFilePath()); - - if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_text_file"); - result.addData("filePath", textFileInfo.absoluteFilePath()); - return result; - } - - QByteArray textLines = textFile.readAll(); - - - QTextDocument sheetDoc; - - sheetDoc.setHtml(QString::fromUtf8(textLines)); - - IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, sheetId, sheetDoc.toHtml())); - - - IFKO(result) { - return result; - } - - // create note - - - result = this->createNote(projectId, indent, plumeId, name, tempDirPath + "/text/N", noteFolderId); - bool ok; - int noteId = result.getData("treeItemId", -2).toInt(&ok); - - if (!ok) { - result = SKRResult(SKRResult::Critical, this, "bad_conversion_to_int"); - return result; - } - - - result = skrdata->treeHub()->setTreeRelationship(projectId, noteId, sheetId); - - - IFKO(result) { - return result; - } - - // create write synopsis - - - QFileInfo synFileInfo(tempDirPath + "/text/S" + QString::number(plumeId) + ".html"); - - if (!synFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_synopsis_file"); - result.addData("filePath", synFileInfo.absoluteFilePath()); - return result; - } - - - QFile synFile(synFileInfo.absoluteFilePath()); - - if (!synFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_synopsis_file"); - result.addData("filePath", synFileInfo.absoluteFilePath()); - return result; - } - - QByteArray lines = synFile.readAll(); - - - QTextDocument synDoc; - - synDoc.setHtml(QString::fromUtf8(lines)); - IFOKDO(result, skrdata->treeHub()->setSecondaryContent(projectId, sheetId, synDoc.toHtml())); - - - // associate "attendance" notes - - QStringList attendIds = xml.attributes().value("attend").toString().split("-", Qt::SkipEmptyParts); - - for (const QString& attendIdString : qAsConst(attendIds)) { - int attendId = attendIdString.toInt(); - - if (attendId == 0) { - continue; - } - - int skrNoteId = m_attendanceConversionHash.value(attendId); - - // associate : - result = skrdata->treeHub()->setTreeRelationship(projectId, skrNoteId, sheetId); - } - - IFKO(result) { - return result; - } - - - return result; -} - -// ----------------------------------------------------------------------------------------------- - -SKRResult PLMImporter::createNote(int projectId, int indent, int plumeId, const QString& name, - const QString& tempDirPath, int parentFolderId) -{ - SKRResult result(this); - - - result = skrdata->treeHub()->addChildTreeItem(projectId, parentFolderId, "TEXT"); - IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "note_creation"); - return result; - } - int noteId = result.getData("treeItemId", -2).toInt(); - - - int parentFolderIndent = skrdata->treeHub()->getIndent(projectId, parentFolderId); - - IFOKDO(result, skrdata->treeHub()->setIndent(projectId, noteId, parentFolderIndent + indent)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteId, name)); - IFOKDO(result, skrdata->treeHub()->setType(projectId, noteId, "TEXT")); - - - // fetch text - - QFileInfo attendFileInfo(tempDirPath + QString::number(plumeId) + ".html"); - - if (!attendFileInfo.exists()) { - result = SKRResult(SKRResult::Critical, this, "no_attend_file"); - result.addData("filePath", attendFileInfo.absoluteFilePath()); - return result; - } - - - QFile attendFile(attendFileInfo.absoluteFilePath()); - - if (!attendFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - result = SKRResult(SKRResult::Critical, this, "cant_open_attend_file"); - result.addData("filePath", attendFileInfo.absoluteFilePath()); - return result; - } - - QByteArray lines = attendFile.readAll(); - - - QTextDocument noteDoc; - - noteDoc.setHtml(QString::fromUtf8(lines)); - IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, noteId, noteDoc.toHtml())); - - - return result; -} - -// ----------------------------------------------------------------------------------------------- - -SKRResult PLMImporter::createTagsFromAttend(int projectId, - int noteId, - const QXmlStreamReader& xml, - const QString & attributeName, - const QStringList & values) -{ - SKRResult result(this); - - if (values.isEmpty()) { - return result; // return successfully - } - - - bool ok; - int index = xml.attributes().value(attributeName).toInt(&ok); - - if (!ok) { - result = SKRResult(SKRResult::Critical, this, "conversion_to_int"); - return result; - } - - if (values.count() <= index) { - return result; // return successfully - } - - - IFOK(result) { - result = skrdata->tagHub()->addTag(projectId, values.at(index)); - IFOK(result) { - result = skrdata->tagHub()->setTagRelationship(projectId, - noteId, - result.getData("tagId", -2).toInt()); - } - } - - return result; -} - // QSqlDatabase PLMImporter::copySQLiteDbToMemory(QSqlDatabase sourceSqlDb, int // projectId, SKRResult &result) // { diff --git a/src/libskribisto-data/src/tasks/sql/plmimporter.h b/src/libskribisto-data/src/tasks/sql/plmimporter.h index 7dc434297..cb53f3342 100755 --- a/src/libskribisto-data/src/tasks/sql/plmimporter.h +++ b/src/libskribisto-data/src/tasks/sql/plmimporter.h @@ -22,11 +22,8 @@ #ifndef PLMIMPORTER_H #define PLMIMPORTER_H -#include #include #include -#include -#include #include "skrresult.h" @@ -45,9 +42,6 @@ class PLMImporter : public QObject { SKRResult & result, const QString& sqlFile = ""); - SKRResult importPlumeCreatorProject(const QUrl& plumeFileName, - const QUrl& skribistoFileName); - signals: public slots: @@ -56,39 +50,8 @@ public slots: // QSqlDatabase copySQLiteDbToMemory(QSqlDatabase sourceSqlDb, int // projectId, SKRResult &result); - SKRResult transformParentsToFolder(int projectId); - - SKRResult createPapersAndAssociations(int projectId, - int indent, - const QXmlStreamReader& xml, - const QString & tempDirPath, - int textFolderId, - int noteFolderId); - SKRResult createNote(int projectId, - int indent, - int plumeId, - const QString& name, - const QString& tempDirPath, - int parentFolderId); - - SKRResult createTagsFromAttend(int projectId, - int noteId, - const QXmlStreamReader& xml, - const QString & attributeName, - const QStringList & values); - - SKRResult readXMLRecursivelyAndCreatePaper(int projectId, - int indent, - QXmlStreamReader *xml, - const QString & tempDirPath, - int textFolderId, - int noteFolderId); private: - - // used to track old plume id with new skribisto note id - QHashm_attendanceConversionHash; - }; #endif // PLMIMPORTER_H diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index aa61dca61..d56b5d61c 100755 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(themePage) add_subdirectory(writingGamesPageToolbox) add_subdirectory(favoritesProjectToolbox) add_subdirectory(navigationProjectToolbox) +add_subdirectory(plumeCreatorImporter) #add_subdirectory(welcomewindow) #add_subdirectory(writewindow) diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml index 4de73b657..c8aa1b07f 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml @@ -134,7 +134,6 @@ NavigationForm { } function popRestoreListView() { - //console.log("popRestoreListView") stackView.pop() root.restoreListViewProxyModel.clearCheckedList() } diff --git a/src/plugins/plumeCreatorImporter/CMakeLists.txt b/src/plugins/plumeCreatorImporter/CMakeLists.txt new file mode 100755 index 000000000..db6633b13 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.5.0) + + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -pedantic -Wno-deprecated-declarations -fstrict-aliasing -pedantic-errors -Wno-unused-variable") +# +# Populate a CMake variable with the sources + +set(PROJECT_NAME "skribisto-plugin-plumeCreatorImporter") + +project(${PROJECT_NAME} LANGUAGES CXX VERSION ${VERSION} ) +add_definitions(-DSKRIBISTO_PLUGIN_LIBRARY) +# Find the QtWidgets library +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Quick Svg QuickControls2 CONFIG REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Quick Svg QuickControls2 CONFIG REQUIRED) + + + +set(SRCS + plugin_info.json + plugin.cpp + plugin.h + skrplumecreatorimporter.h + skrplumecreatorimporter.cpp +) + +set(QRC + plugin_qml.qrc + ) + +qt_add_resources(RESOURCES ${QRC}) + +# Tell CMake to create the executable +add_library(${PROJECT_NAME} SHARED ${SRCS} ${RESOURCES}) +add_dependencies(${PROJECT_NAME} skribisto-data) +add_dependencies(${PROJECT_NAME} skribisto) + +target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::QuickControls2) +target_link_libraries(${PROJECT_NAME} PUBLIC skribisto-data) + + +include_directories("${CMAKE_SOURCE_DIR}/src/app/src/") +include_directories("${CMAKE_SOURCE_DIR}/src/app/src/qml/") +include_directories("${CMAKE_SOURCE_DIR}/src/libskribisto-data/src/") + + + + +#-------------- QUAZIP -------------------- + + + + if(${SKR_DEV}) + + message("-------------- SKR_DEV is ON ---------------------") + set(EXTERNAL_INSTALL_LOCATION ${CMAKE_SOURCE_DIR}/../build_skribisto_Release/3rdparty) + + set(QUAZIP_QT_MAJOR_VERSION ${QT_MAJOR_VERSION}) + + if(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) + set(QUAZIP_LIB_DIR ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) + endif(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib) + if(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) + set(QUAZIP_LIB_DIR ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) + endif(EXISTS ${EXTERNAL_INSTALL_LOCATION}/quazip/lib64) + + set(QuaZip-Qt${QT_VERSION_MAJOR}_DIR ${QUAZIP_LIB_DIR}/cmake/QuaZip-Qt${QT_VERSION_MAJOR}-1.1) + set(ZLIB_ROOT ${EXTERNAL_INSTALL_LOCATION}/zlib) + message("-------------- QuaZip-Qt${QT_VERSION_MAJOR}_DIR = ${QuaZip-Qt${QT_VERSION_MAJOR}_DIR}---------------------") + endif(${SKR_DEV}) + + +#--------------------------------- + +find_package(QuaZip-Qt${QT_VERSION_MAJOR} REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE QuaZip::QuaZip) +target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${QuaZip_INCLUDE_DIRS}) + + + + + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/plugins/skribisto") +message("----------------PLUGIN_INSTALL_DIR---------------") +message("${PLUGIN_INSTALL_DIR}") +endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "bin/plugins") +endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/src/app/src/qml/WelcomePage/PlumeImporter.qml b/src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporter.qml similarity index 84% rename from src/app/src/qml/WelcomePage/PlumeImporter.qml rename to src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporter.qml index d7beff5db..1e696a117 100755 --- a/src/app/src/qml/WelcomePage/PlumeImporter.qml +++ b/src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporter.qml @@ -1,19 +1,34 @@ import QtQuick 2.15 +import QtQml 2.15 import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 import Qt.labs.platform 1.1 as LabPlatform -import QtQml 2.15 -import eu.skribisto.result 1.0 -import "../Items" -import "../Commons" -import ".." +import eu.skribisto.plumecreatorimporter 1.0 + +import "../../Items" +import "../../Commons" +import "../.." + +PlumeCreatorImporterForm { + id: root -PlumeImporterForm { property string plumeFileName: "" property string targetFileName: "" signal closeCalled() + + + + SKRPlumeCreatorImporter{ + id: plumeCreatorImporter + } + + + + + + + goBackToolButton.icon.source: "qrc:///icons/backup/go-previous.svg" signal goBackButtonClicked() goBackToolButton.onClicked: goBackButtonClicked() @@ -50,7 +65,7 @@ PlumeImporterForm { if(plumeProjectFileTextField.text.length !== 0){ - skrData.projectHub().importPlumeCreatorProject(plumeFileName, targetFileName) + plumeCreatorImporter.importPlumeCreatorProject(plumeFileName, targetFileName) closeCalled() } } @@ -64,4 +79,5 @@ PlumeImporterForm { selectPlumeProjectFileToolButton.forceActiveFocus() } } + } diff --git a/src/app/src/qml/WelcomePage/PlumeImporterForm.ui.qml b/src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporterForm.ui.qml similarity index 97% rename from src/app/src/qml/WelcomePage/PlumeImporterForm.ui.qml rename to src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporterForm.ui.qml index 878c93f37..40a7a4dbd 100755 --- a/src/app/src/qml/WelcomePage/PlumeImporterForm.ui.qml +++ b/src/plugins/plumeCreatorImporter/PlumeCreatorImporter/PlumeCreatorImporterForm.ui.qml @@ -1,9 +1,11 @@ import QtQuick 2.15 -import QtQuick.Controls 2.15 +import QtQml 2.15 import QtQuick.Layouts 1.15 -import "../Items" -import "../Commons" -import ".." +import QtQuick.Controls 2.15 + +import "../../Items" +import "../../Commons" +import "../.." Item { property alias goBackToolButton: goBackToolButton @@ -13,6 +15,7 @@ Item { property alias plumeProjectFileTextField: plumeProjectFileTextField readonly property int columnWidth: 550 + clip: true ColumnLayout { id: columnLayout6 anchors.fill: parent @@ -104,3 +107,4 @@ Item { } } + diff --git a/src/plugins/plumeCreatorImporter/plugin.cpp b/src/plugins/plumeCreatorImporter/plugin.cpp new file mode 100755 index 000000000..2d8ac3cce --- /dev/null +++ b/src/plugins/plumeCreatorImporter/plugin.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** +* Copyright (C) 2021 by Cyril Jacquet * +* cyril.jacquet@skribisto.eu * +* * +* Filename: plugin.cpp +* * +* This file is part of Skribisto. * +* * +* Skribisto 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, or * +* (at your option) any later version. * +* * +* Skribisto 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 Skribisto. If not, see . * +***************************************************************************/ +#include "plugin.h" +#include "skrdata.h" +#include "skrplumecreatorimporter.h" +#include + +Plugin::Plugin(QObject *parent) : QObject(parent) +{ + qmlRegisterType("eu.skribisto.plumecreatorimporter", + 1, + 0, + "SKRPlumeCreatorImporter"); +} + +// --------------------------------------------------- + +Plugin::~Plugin() +{} + + +// --------------------------------------------------- diff --git a/src/plugins/plumeCreatorImporter/plugin.h b/src/plugins/plumeCreatorImporter/plugin.h new file mode 100755 index 000000000..3c013e1a5 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/plugin.h @@ -0,0 +1,73 @@ +/*************************************************************************** +* Copyright (C) 2021 by Cyril Jacquet * +* cyril.jacquet@skribisto.eu * +* * +* Filename: plugin.h +* * +* This file is part of Skribisto. * +* * +* Skribisto 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, or * +* (at your option) any later version. * +* * +* Skribisto 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 Skribisto. If not, see . * +***************************************************************************/ +#ifndef PLUMECREATORIMPORTER_H +#define PLUMECREATORIMPORTER_H + +#include +#include "skrimporterinterface.h" + +class Plugin : public QObject, + public SKRImporterInterface { + Q_OBJECT + Q_PLUGIN_METADATA( + IID "eu.skribisto.PlumeCreatorImporterPlugin/1.0" FILE + "plugin_info.json") + Q_INTERFACES(SKRImporterInterface) + +public: + + explicit Plugin(QObject *parent = nullptr); + ~Plugin(); + QString name() const override { + return "PlumeCreatorImporter"; + } + + QString displayedName() const override { + return tr("Plume Creator importer"); + } + + QString use() const override { + return tr("Import a project from Plume Creator"); + } + + QString iconSource() const override { + return "qrc:/pics/skribisto.svg"; + } + + QString buttonText() const override { + return tr("Import a project from Plume Creator "); + } + + QString qmlUrl() const override { + return "qrc:///qml/plugins/PlumeCreatorImporter/PlumeCreatorImporter.qml"; + } + + int weight() const override { + return 500; + } + +signals: + +private: +}; + +#endif // PLUGIN_H diff --git a/src/plugins/plumeCreatorImporter/plugin_info.json b/src/plugins/plumeCreatorImporter/plugin_info.json new file mode 100755 index 000000000..1bc11d301 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/plugin_info.json @@ -0,0 +1,9 @@ +{ + "type" : "importer", + "shortname" : "PlumeCreatorImporter", + "longname" : "Plume Creator Importer", + "version" : "1.0", + "mandatory" : true, + "activatedByDefault" : true, + "dependencies" : [] +} diff --git a/src/plugins/plumeCreatorImporter/plugin_qml.qrc b/src/plugins/plumeCreatorImporter/plugin_qml.qrc new file mode 100755 index 000000000..71e05c2eb --- /dev/null +++ b/src/plugins/plumeCreatorImporter/plugin_qml.qrc @@ -0,0 +1,6 @@ + + + PlumeCreatorImporter/PlumeCreatorImporter.qml + PlumeCreatorImporter/PlumeCreatorImporterForm.ui.qml + + diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp new file mode 100644 index 000000000..767edf80d --- /dev/null +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp @@ -0,0 +1,724 @@ +#include "skrplumecreatorimporter.h" +#include "skrdata.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +SKRPlumeCreatorImporter::SKRPlumeCreatorImporter(QObject *parent) : QObject(parent) +{} + +// ----------------------------------------------------------------------------------------------- + + +SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFileName, const QUrl& skribistoFileName) +{ + QString sqlFile = ":/sql/sqlite_project_1_6.sql"; + SKRResult result = skrdata->projectHub()->createSilentlyNewSpecificEmptyProject(skribistoFileName, sqlFile); + + int projectId = -2; + + IFOK(result) { + projectId = result.getData("projectId", -2).toInt(); + } + + if (projectId == -2) { + result = SKRResult(SKRResult::Critical, this, "plume_cant_create_empty_project"); + return result; + } + + // create temp file + QTemporaryDir tempDir; + + tempDir.setAutoRemove(true); + + if (!tempDir.isValid()) { + result = SKRResult(SKRResult::Critical, this, "plume_no_temp_dir"); + return result; + } + + QString tempDirPath = tempDir.path(); + + // extract zip + + JlCompress::extractDir(plumeFileName.toLocalFile(), tempDirPath); + + // QFile qf(plumeFileName.toLocalFile()); + // qf.open(QIODevice::ReadOnly); + // int fd = qf.handle(); + // FILE *plumeFile = fdopen(dup(fd), "rb"); + + + // // QFile tf(plumeFileName.toLocalFile()); + // // tf.open(QIODevice::ReadOnly); + // FILE *fp; + // QByteArray ba = tempDirPath.toLocal8Bit(); + // const char *c_str2 = ba.data(); + // fp = fopen(c_str2, "w"); + + // // int fd2 = tf.handle(); + // // FILE *t = fdopen(dup(fd2), "rb"); + + // decompress(plumeFile, fp); + // fclose(plumeFile); // correct + // fclose(fp); + + // ----------- create text folder--------------------------------------- + + + result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_folder_creation"); + return result; + } + int textFolderId = result.getData("treeItemId", -2).toInt(); + + IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, textFolderId, 1000)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, textFolderId, "text")); + + + // ----------- create attend folder--------------------------------------- + + + result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "attend_folder_creation"); + return result; + } + int attendFolderId = result.getData("treeItemId", -2).toInt(); + + IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, attendFolderId, 80000000)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, attendFolderId, "attendance")); + + + // ----------- create note folder--------------------------------------- + + + result = skrdata->treeHub()->addChildTreeItem(projectId, 0, "FOLDER"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "note_creation"); + return result; + } + int noteFolderId = result.getData("treeItemId", -2).toInt(); + + IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, noteFolderId, 90000000)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteFolderId, "note")); + + + // ----------------------------- read + // attend--------------------------------------- + + QFileInfo attendFileInfo(tempDirPath + "/attendance"); + + if (!attendFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_attend_file"); + return result; + } + + + QFile attendFile(attendFileInfo.absoluteFilePath()); + + if (!attendFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_attend_file"); + return result; + } + + + QByteArray attendLines = attendFile.readAll(); + + QXmlStreamReader attendXml(attendLines); + + + m_attendanceConversionHash.clear(); + + + while (attendXml.readNextStartElement() && attendXml.name().toString() == "plume-attendance") { + QStringList rolesNames = attendXml.attributes().value("rolesNames").toString().split("--", Qt::SkipEmptyParts); + QStringList levelsNames = + attendXml.attributes().value("levelsNames").toString().split("--", Qt::SkipEmptyParts); + + QStringList box_1Names = attendXml.attributes().value("box_1").toString().split("--", Qt::SkipEmptyParts); + QStringList box_2Names = attendXml.attributes().value("box_2").toString().split("--", Qt::SkipEmptyParts); + QStringList box_3Names = attendXml.attributes().value("box_3").toString().split("--", Qt::SkipEmptyParts); + + QStringList spinBox_1Names = + attendXml.attributes().value("spinBox_1").toString().split("--", Qt::SkipEmptyParts); + + + while (attendXml.readNextStartElement() && attendXml.name().toString() == "group") { + int attendNumber = attendXml.attributes().value("number").toInt(); + QString groupName = attendXml.attributes().value("name").toString(); + result = this->createNote(projectId, 1, attendNumber, groupName, tempDirPath + "/attend/A", attendFolderId); + IFOK(result) { + int groupNoteId = result.getData("treeItemId", -2).toInt(); + + m_attendanceConversionHash.insert(attendNumber, groupNoteId); + + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_1", box_1Names); + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_2", box_2Names); + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_3", box_3Names); + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "spinBox_1", spinBox_1Names); + } + + + while (attendXml.readNextStartElement() && attendXml.name().toString() == "obj") { + int attendNumber = attendXml.attributes().value("number").toInt(); + QString objName = attendXml.attributes().value("name").toString(); + result = + this->createNote(projectId, 2, attendNumber, objName, tempDirPath + "/attend/A", attendFolderId); + IFOK(result) { + int objNoteId = result.getData("treeItemId", -2).toInt(); + + m_attendanceConversionHash.insert(attendNumber, objNoteId); + + + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_1", box_1Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_2", box_2Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_3", box_3Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "spinBox_1", spinBox_1Names); + + QString objQuickDetail = attendXml.attributes().value("quickDetails").toString(); + + skrdata->treePropertyHub()->setProperty(projectId, objNoteId, "label", objQuickDetail); + } + + attendXml.readElementText(); + } + } + } + + + // ----------------------------- read + // tree--------------------------------------- + + + QFileInfo treeFileInfo(tempDirPath + "/tree"); + + if (!treeFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_tree_file"); + result.addData("filePath", treeFileInfo.absoluteFilePath()); + return result; + } + + + QFile treeFile(treeFileInfo.absoluteFilePath()); + + if (!treeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_tree_file"); + result.addData("filePath", treeFileInfo.absoluteFilePath()); + return result; + } + + + QByteArray lines = treeFile.readAll(); + + QXmlStreamReader xml(lines); + + + while (xml.readNextStartElement() && xml.name().toString() == "plume-tree") { + // pick project name + QString projectName = xml.attributes().value("projectName").toString(); + IFOKDO(result, skrdata->projectHub()->setProjectName(projectId, projectName)); + + + while (xml.readNextStartElement()) { + if (xml.name().toString() == "trash") { + xml.skipCurrentElement(); + continue; + } + + if (xml.name().toString() == "book") { + IFOKDO(result, + this->readXMLRecursivelyAndCreatePaper(projectId, 1, &xml, tempDirPath, textFolderId, + noteFolderId)); + } + } + + while (xml.readNext() != QXmlStreamReader::EndDocument) {} + } + + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "error_while_exploiting_tree"); + return result; + } + + if (xml.hasError() && (xml.error() != QXmlStreamReader::PrematureEndOfDocumentError)) { + result = SKRResult(SKRResult::Critical, this, "error_in_tree_xml"); + + result.addData("xmlError", QString("%1\nLine %2, column %3") + .arg(xml.errorString()) + .arg(xml.lineNumber()) + .arg(xml.columnNumber())); + + return result; + } + + + // ----------------------------- user dict------ + // ----------------------------- + + QFileInfo dictFileInfo(tempDirPath + "/dicts/userDict.dict_plume"); + + if (!dictFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_dict_file"); + result.addData("filePath", dictFileInfo.absoluteFilePath()); + return result; + } + + + QFile dictFile(dictFileInfo.absoluteFilePath()); + + if (!dictFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_dict_file"); + result.addData("filePath", dictFileInfo.absoluteFilePath()); + result.addData("fileError", dictFile.error()); + return result; + } + + + QByteArray dictLines = dictFile.readAll(); + + QString dictString = QString::fromUtf8(dictLines); + + QStringList dictWords = dictString.split(";", Qt::SkipEmptyParts); + + skrdata->projectDictHub()->setProjectDictList(projectId, dictWords); + + + // transform texts with children in folders + + IFOKDO(result, transformParentsToFolder(projectId)); + + + // save + + IFOKDO(result, skrdata->projectHub()->saveProject(projectId)); + + QUrl url = skrdata->projectHub()->getPath(projectId); + + IFOKDO(result, skrdata->projectHub()->closeProject(projectId)); + + IFOKDO(result, skrdata->projectHub()->loadProject(url)); + + return result; +} + +// ----------------------------------------------------------- + + +SKRResult SKRPlumeCreatorImporter::transformParentsToFolder(int projectId) { + SKRResult result; + QSqlDatabase sqlDb = plmProjectManager->project(projectId)->getSqlDb(); + + + // retrieve sorted id with indent list + + QList treeIdList; + QList treeIndentList; + QList treeSortOrderList; + QList typeList; + QSqlQuery query(sqlDb); + QString queryStr = "SELECT l_tree_id, l_indent, l_sort_order, t_type FROM tbl_tree ORDER BY l_sort_order"; + + + query.prepare(queryStr); + + + query.exec(); + + if (query.lastError().isValid()) { + result = SKRResult(SKRResult::Critical, this, "sql_error"); + result.addData("SQLError", query.lastError().text()); + result.addData("SQL string", queryStr); + + return result; + } + + while (query.next()) { + treeIdList.append(query.value(0).toInt()); + treeIndentList.append(query.value(1).toInt()); + treeSortOrderList.append(query.value(2).toInt()); + typeList.append(query.value(2).toString()); + } + + // remove project item (the first one) + treeIdList.takeFirst(); + treeIndentList.takeFirst(); + treeSortOrderList.takeFirst(); + typeList.takeFirst(); + + // remove folders + + QList indexesToRemove; + + for (int k = 0; k < typeList.count(); k++) { + if (typeList.at(k) == "TEXT") { + indexesToRemove.append(k); + } + } + + for (int m = indexesToRemove.count() - 1; m >= 0; m++) { + treeIdList.removeAt(m); + treeIndentList.removeAt(m); + treeSortOrderList.removeAt(m); + typeList.removeAt(m); + } + + + // create folders + + sqlDb.transaction(); + + QString treeQueryStr = "INSERT INTO tbl_tree (t_title, l_indent, l_sort_order, t_type, dt_trashed, b_trashed)" + " VALUES (" + " (SELECT t_title FROM tbl_tree WHERE l_tree_id = :treeId)," + " (SELECT l_indent FROM tbl_tree WHERE l_tree_id = :treeId)," + " :newSortOrder," + " 'FOLDER'," + " (SELECT dt_trashed FROM tbl_tree WHERE l_tree_id = :treeId)," + " (SELECT b_trashed FROM tbl_tree WHERE l_tree_id = :treeId)" + ")"; + + + QString incrementIndentQueryStr = "UPDATE tbl_tree SET l_indent = :newIndent WHERE l_tree_id = :treeId"; + + int previousIdIndent = -1; + + for (int i = treeIdList.count() - 1; i >= 0; i--) { + int currentTreeId = treeIdList.at(i); + int currentIndent = treeIndentList.at(i); + + if (i < treeIdList.count() - 1) { + previousIdIndent = treeIndentList.at(i + 1); + } + + + if (previousIdIndent > currentIndent) { + int currentSortOrder = treeSortOrderList.at(i); + + + // create folder + query.prepare(treeQueryStr); + + query.bindValue(":treeId", currentTreeId); + query.bindValue(":newSortOrder", currentSortOrder - 1); + + query.exec(); + + if (query.lastError().isValid()) { + result = SKRResult(SKRResult::Critical, this, "sql_error"); + result.addData("SQLError", query.lastError().text()); + result.addData("SQL string", queryStr); + result.addData("treeId", currentTreeId); + result.addData("newSortOrder", currentSortOrder - 1); + sqlDb.rollback(); + + return result; + } + + int newFolderTreeId = query.lastInsertId().toInt(); + + // increment current item's indent + + query.prepare(incrementIndentQueryStr); + + query.bindValue(":treeId", currentTreeId); + query.bindValue(":newIndent", currentIndent + 1); + + query.exec(); + + if (query.lastError().isValid()) { + result = SKRResult(SKRResult::Critical, this, "sql_error"); + result.addData("SQLError", query.lastError().text()); + result.addData("SQL string", queryStr); + result.addData("treeId", currentTreeId); + result.addData("sortOrder", currentSortOrder); + result.addData("newIndent", currentIndent + 1); + sqlDb.rollback(); + + return result; + } + } + } + + IFOK(result) { + sqlDb.commit(); + } + + + return result; +} + +// ----------------------------------------------------------- + +SKRResult SKRPlumeCreatorImporter::readXMLRecursivelyAndCreatePaper(int projectId, + int indent, + QXmlStreamReader *xml, + const QString& tempDirPath, + int textFolderId, int noteFolderId) +{ + SKRResult result; + + if (!xml->readNextStartElement()) { + xml->readElementText(); + return result; + } + else { + if (xml->name().toString() == "separator") { + xml->skipCurrentElement(); + } + else { + IFOKDO(result, + this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); + IFOKDO(result, + readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, + noteFolderId)); + } + } + + while (xml->readNextStartElement()) { + if (xml->name().toString() == "separator") { + xml->skipCurrentElement(); + continue; + } + IFOKDO(result, + this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); + IFOKDO(result, + readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, noteFolderId)); + } + + return result; +} + +SKRResult SKRPlumeCreatorImporter::createPapersAndAssociations(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString& tempDirPath, + int textFolderId, int noteFolderId) +{ + SKRResult result(this); + + int plumeId = xml.attributes().value("number").toInt(); + QString name = xml.attributes().value("name").toString(); + + // create sheet + + result = skrdata->treeHub()->addChildTreeItem(projectId, textFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_creation"); + return result; + } + int sheetId = result.getData("treeItemId", -2).toInt(); + + int textFolderIndent = skrdata->treeHub()->getIndent(projectId, textFolderId); + + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, textFolderIndent + indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, name)); + IFOKDO(result, skrdata->treeHub()->setType(projectId, sheetId, "TEXT")); + + + // - fetch text + + QFileInfo textFileInfo(tempDirPath + "/text/T" + QString::number(plumeId) + ".html"); + + if (!textFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; + } + + + QFile textFile(textFileInfo.absoluteFilePath()); + + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; + } + + QByteArray textLines = textFile.readAll(); + + + QTextDocument sheetDoc; + + sheetDoc.setHtml(QString::fromUtf8(textLines)); + + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, sheetId, sheetDoc.toHtml())); + + + IFKO(result) { + return result; + } + + // create note + + + result = this->createNote(projectId, indent, plumeId, name, tempDirPath + "/text/N", noteFolderId); + bool ok; + int noteId = result.getData("treeItemId", -2).toInt(&ok); + + if (!ok) { + result = SKRResult(SKRResult::Critical, this, "bad_conversion_to_int"); + return result; + } + + + result = skrdata->treeHub()->setTreeRelationship(projectId, noteId, sheetId); + + + IFKO(result) { + return result; + } + + // create write synopsis + + + QFileInfo synFileInfo(tempDirPath + "/text/S" + QString::number(plumeId) + ".html"); + + if (!synFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_synopsis_file"); + result.addData("filePath", synFileInfo.absoluteFilePath()); + return result; + } + + + QFile synFile(synFileInfo.absoluteFilePath()); + + if (!synFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_synopsis_file"); + result.addData("filePath", synFileInfo.absoluteFilePath()); + return result; + } + + QByteArray lines = synFile.readAll(); + + + QTextDocument synDoc; + + synDoc.setHtml(QString::fromUtf8(lines)); + IFOKDO(result, skrdata->treeHub()->setSecondaryContent(projectId, sheetId, synDoc.toHtml())); + + + // associate "attendance" notes + + QStringList attendIds = xml.attributes().value("attend").toString().split("-", Qt::SkipEmptyParts); + + for (const QString& attendIdString : qAsConst(attendIds)) { + int attendId = attendIdString.toInt(); + + if (attendId == 0) { + continue; + } + + int skrNoteId = m_attendanceConversionHash.value(attendId); + + // associate : + result = skrdata->treeHub()->setTreeRelationship(projectId, skrNoteId, sheetId); + } + + IFKO(result) { + return result; + } + + + return result; +} + +// ----------------------------------------------------------------------------------------------- + +SKRResult SKRPlumeCreatorImporter::createNote(int projectId, int indent, int plumeId, const QString& name, + const QString& tempDirPath, int parentFolderId) +{ + SKRResult result(this); + + + result = skrdata->treeHub()->addChildTreeItem(projectId, parentFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "note_creation"); + return result; + } + int noteId = result.getData("treeItemId", -2).toInt(); + + + int parentFolderIndent = skrdata->treeHub()->getIndent(projectId, parentFolderId); + + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, noteId, parentFolderIndent + indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteId, name)); + IFOKDO(result, skrdata->treeHub()->setType(projectId, noteId, "TEXT")); + + + // fetch text + + QFileInfo attendFileInfo(tempDirPath + QString::number(plumeId) + ".html"); + + if (!attendFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_attend_file"); + result.addData("filePath", attendFileInfo.absoluteFilePath()); + return result; + } + + + QFile attendFile(attendFileInfo.absoluteFilePath()); + + if (!attendFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_attend_file"); + result.addData("filePath", attendFileInfo.absoluteFilePath()); + return result; + } + + QByteArray lines = attendFile.readAll(); + + + QTextDocument noteDoc; + + noteDoc.setHtml(QString::fromUtf8(lines)); + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, noteId, noteDoc.toHtml())); + + + return result; +} + +// ----------------------------------------------------------------------------------------------- + +SKRResult SKRPlumeCreatorImporter::createTagsFromAttend(int projectId, + int noteId, + const QXmlStreamReader& xml, + const QString & attributeName, + const QStringList & values) +{ + SKRResult result(this); + + if (values.isEmpty()) { + return result; // return successfully + } + + + bool ok; + int index = xml.attributes().value(attributeName).toInt(&ok); + + if (!ok) { + result = SKRResult(SKRResult::Critical, this, "conversion_to_int"); + return result; + } + + if (values.count() <= index) { + return result; // return successfully + } + + + IFOK(result) { + result = skrdata->tagHub()->addTag(projectId, values.at(index)); + IFOK(result) { + result = skrdata->tagHub()->setTagRelationship(projectId, + noteId, + result.getData("tagId", -2).toInt()); + } + } + + return result; +} diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h new file mode 100644 index 000000000..b74347653 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h @@ -0,0 +1,55 @@ +#ifndef SKRPLUMECREATORIMPORTER_H +#define SKRPLUMECREATORIMPORTER_H + +#include +#include +#include +#include "skrresult.h" + +class SKRPlumeCreatorImporter : public QObject { + Q_OBJECT + +public: + + explicit SKRPlumeCreatorImporter(QObject *parent = nullptr); + + Q_INVOKABLE SKRResult importPlumeCreatorProject(const QUrl& plumeFileName, + const QUrl& skribistoFileName); + +signals: + +private: + + // QSqlDatabase copySQLiteDbToMemory(QSqlDatabase sourceSqlDb, int + // projectId, SKRResult &result); + SKRResult transformParentsToFolder(int projectId); + + SKRResult createPapersAndAssociations(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int textFolderId, + int noteFolderId); + SKRResult createNote(int projectId, + int indent, + int plumeId, + const QString& name, + const QString& tempDirPath, + int parentFolderId); + + SKRResult createTagsFromAttend(int projectId, + int noteId, + const QXmlStreamReader& xml, + const QString & attributeName, + const QStringList & values); + + SKRResult readXMLRecursivelyAndCreatePaper(int projectId, + int indent, + QXmlStreamReader *xml, + const QString & tempDirPath, + int textFolderId, + int noteFolderId); + + QHashm_attendanceConversionHash; +}; +#endif // SKRPLUMECREATORIMPORTER_H diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index a7b772591..67b6b0244 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -795,10 +795,6 @@ <h2>Import</h2> - - Import from Plume Creator project - - ListItemAttributes @@ -1390,6 +1386,21 @@ + + Plugin + + Plume Creator importer + + + + Import a project from Plume Creator + + + + Import a project from Plume Creator + + + PluginPage @@ -1417,7 +1428,7 @@ - PlumeImporterForm.ui + PlumeCreatorImporterForm.ui Go back From a03ecb13cb7807239fcc1b9759dc102fdc55abd6 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Thu, 24 Jun 2021 18:46:46 +0200 Subject: [PATCH 6/8] Windows support Windows support: fix packaging.ps1 --- cmake/Superbuild/CMakeLists.txt | 2 +- package/windows/packaging.ps1 | 5 +---- src/libskribisto-data/src/CMakeLists.txt | 6 +++--- src/plugins/plumeCreatorImporter/CMakeLists.txt | 6 +++--- .../plumeCreatorImporter/skrplumecreatorimporter.cpp | 7 +++---- src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h | 1 + src/translations/CMakeLists.txt | 6 +++--- src/translations/skribisto_en_US.ts | 2 +- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/cmake/Superbuild/CMakeLists.txt b/cmake/Superbuild/CMakeLists.txt index ab87a74db..ba6b7b6eb 100644 --- a/cmake/Superbuild/CMakeLists.txt +++ b/cmake/Superbuild/CMakeLists.txt @@ -20,7 +20,7 @@ ExternalProject_Add(zlib ExternalProject_Add(quazip GIT_REPOSITORY https://github.com/jacquetc/quazip.git - GIT_TAG de8125cf8cf8422dae5df87ed6b5be9305219594 + GIT_TAG v1.1 CMAKE_ARGS -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} diff --git a/package/windows/packaging.ps1 b/package/windows/packaging.ps1 index 37cd23e31..d0f58fa0b 100644 --- a/package/windows/packaging.ps1 +++ b/package/windows/packaging.ps1 @@ -2,9 +2,9 @@ $env:Path += ";C:\Qt\5.15.2\mingw81_64\bin;C:\Qt\Tools\mingw810_64\bin;C:\Qt\Tools\CMake_64\bin" +# clean Remove-Item -Recurse -Force -Path ../../../build_skribisto_Release mkdir ../../../build_skribisto_Release -mkdir ../../../build_skribisto_Release/package # prepare cmake.exe -B../../../build_skribisto_Release -GNinja ` @@ -21,9 +21,6 @@ cmake.exe -B../../../build_skribisto_Release -GNinja ` cd ../../../build_skribisto_Release C:/Qt/Tools/Ninja/ninja.exe -# clean -Remove-Item -Recurse -Path package - # copy mkdir package mkdir package/plugins diff --git a/src/libskribisto-data/src/CMakeLists.txt b/src/libskribisto-data/src/CMakeLists.txt index 6c2befce7..61449cf72 100755 --- a/src/libskribisto-data/src/CMakeLists.txt +++ b/src/libskribisto-data/src/CMakeLists.txt @@ -9,8 +9,8 @@ cmake_minimum_required(VERSION 3.5.0) project(skribisto-data LANGUAGES CXX VERSION ${VERSION} ) add_definitions(-DSKRIBISTO_DATA_LIBRARY) # Find the QtWidgets library -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql Xml CONFIG REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql Xml CONFIG REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Sql Xml CONFIG REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Sql Xml CONFIG REQUIRED) @@ -104,7 +104,7 @@ qt_add_resources(RESOURCES ${QRC}) # Tell CMake to create the executable add_library(skribisto-data SHARED ${data_SRCS} ${data_HDR} ${RESOURCES}) -target_link_libraries(skribisto-data PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Xml) +target_link_libraries(skribisto-data PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::Xml) diff --git a/src/plugins/plumeCreatorImporter/CMakeLists.txt b/src/plugins/plumeCreatorImporter/CMakeLists.txt index db6633b13..a8a9aee84 100755 --- a/src/plugins/plumeCreatorImporter/CMakeLists.txt +++ b/src/plugins/plumeCreatorImporter/CMakeLists.txt @@ -11,8 +11,8 @@ set(PROJECT_NAME "skribisto-plugin-plumeCreatorImporter") project(${PROJECT_NAME} LANGUAGES CXX VERSION ${VERSION} ) add_definitions(-DSKRIBISTO_PLUGIN_LIBRARY) # Find the QtWidgets library -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Quick Svg QuickControls2 CONFIG REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Quick Svg QuickControls2 CONFIG REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Quick Svg Sql QuickControls2 CONFIG REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Quick Svg Sql QuickControls2 CONFIG REQUIRED) @@ -35,7 +35,7 @@ add_library(${PROJECT_NAME} SHARED ${SRCS} ${RESOURCES}) add_dependencies(${PROJECT_NAME} skribisto-data) add_dependencies(${PROJECT_NAME} skribisto) -target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::QuickControls2) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Sql Qt${QT_VERSION_MAJOR}::QuickControls2) target_link_libraries(${PROJECT_NAME} PUBLIC skribisto-data) diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp index 767edf80d..8b0ede2e4 100644 --- a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp @@ -1,13 +1,12 @@ #include "skrplumecreatorimporter.h" #include "skrdata.h" -#include -#include -#include -#include #include #include #include #include +#include +#include +#include SKRPlumeCreatorImporter::SKRPlumeCreatorImporter(QObject *parent) : QObject(parent) diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h index b74347653..2b03c5633 100644 --- a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "skrresult.h" class SKRPlumeCreatorImporter : public QObject { diff --git a/src/translations/CMakeLists.txt b/src/translations/CMakeLists.txt index b1c957d5b..50b9e8fcc 100644 --- a/src/translations/CMakeLists.txt +++ b/src/translations/CMakeLists.txt @@ -19,7 +19,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lupdate") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lupdate.exe") + set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lupdate.exe") endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") message(${LUPDATE_EXECUTABLE}) @@ -37,7 +37,7 @@ add_custom_target("update_ts_en_US" ALL set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lrelease") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lrelease.exe") + set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lrelease.exe") endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") message(${LRELEASE_EXECUTABLE}) @@ -48,7 +48,7 @@ foreach(TS_FILEPATH ${TS_FILES}) string(REGEX MATCH "\\_([0-9a-zA-Z_]*)\\." LANGSTR ${TS_FILENAME}) string(REPLACE "." "" LANGSTR ${LANGSTR}) set(QM_FILENAME "skribisto${LANGSTR}.qm") - add_custom_target("release_ts_${LANGSTR}" ALL + add_custom_target("release_ts${LANGSTR}" ALL COMMAND ${LRELEASE_EXECUTABLE} ${TS_FILEPATH} -silent -qm ${QM_FILENAME} DEPENDS ${TS_FILEPATH} ) diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index 67b6b0244..b41b5b357 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -1764,7 +1764,7 @@ S: - the capital letters are shortcuts, mark one letter similarly in your translation and translate the standalone letter accordingly later + translate this letter according to the shortcut * letter y)es From 05f7d90c51f654c7bd9ffa4c7663726134d45e44 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Thu, 24 Jun 2021 19:16:39 +0200 Subject: [PATCH 7/8] Update NEWS.yml --- NEWS.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS.yml b/NEWS.yml index 2a0367f09..075bd5409 100644 --- a/NEWS.yml +++ b/NEWS.yml @@ -45,3 +45,15 @@ Description: - "Breadcrumb: adapt to small size" - "Navigation: fix Sort Alphabetically action" - "Navigation: Fix cut, copy, paste" +--- +Version: 1.9.26 +Date: 2021-06-24 +Type: stable +Description: +- "Plugin: create plugin interface for importer" +- "Navigation: fix restore" +- "Icons: fix some icons not following the theme" +- "Distraction free: header bar can now hide" +- "Writing games: forbid Ctrl+Z" + + From 2acd8b9e72f9871c84d3fe98f938afb0e00c37b0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 24 Jun 2021 17:17:05 +0000 Subject: [PATCH 8/8] releases in AppData generated --- eu.skribisto.skribisto.appdata.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/eu.skribisto.skribisto.appdata.xml b/eu.skribisto.skribisto.appdata.xml index 97b129d21..7e8da2757 100644 --- a/eu.skribisto.skribisto.appdata.xml +++ b/eu.skribisto.skribisto.appdata.xml @@ -28,6 +28,17 @@ + + +
    +
  • Plugin: create plugin interface for importer
  • +
  • Navigation: fix restore
  • +
  • Icons: fix some icons not following the theme
  • +
  • Distraction free: header bar can now hide
  • +
  • Writing games: forbid Ctrl+Z
  • +
+
+