From 1ae3987fabb555b9c91fe85e3b9d219fd6b60453 Mon Sep 17 00:00:00 2001 From: shufeng2012 <277665039@qq.com> Date: Sat, 15 Aug 2026 19:40:17 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=9A=20=E7=A7=BB=E5=8A=A8=E6=BA=90?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entry/src/main/cpp/src/CMakeLists.txt | 174 + entry/src/main/cpp/src/config-debug.h.in | 69 + entry/src/main/cpp/src/config-dev.h.in | 50 + entry/src/main/cpp/src/config-features.h.in | 30 + entry/src/main/cpp/src/config-git.h.in | 40 + entry/src/main/cpp/src/config-paths.h.in | 22 + entry/src/main/cpp/src/config.h.in | 57 + entry/src/main/cpp/src/core/CMakeLists.txt | 20 + .../main/cpp/src/core/audio/AudioPlayer.cpp | 85 + .../src/main/cpp/src/core/audio/AudioPlayer.h | 56 + .../src/main/cpp/src/core/audio/AudioQueue.h | 147 + .../main/cpp/src/core/audio/AudioRecorder.cpp | 32 + .../main/cpp/src/core/audio/AudioRecorder.h | 44 + .../main/cpp/src/core/audio/DeviceInfo.cpp | 15 + .../src/main/cpp/src/core/audio/DeviceInfo.h | 37 + .../cpp/src/core/audio/PortAudioConsumer.cpp | 162 + .../cpp/src/core/audio/PortAudioConsumer.h | 46 + .../cpp/src/core/audio/PortAudioProducer.cpp | 127 + .../cpp/src/core/audio/PortAudioProducer.h | 52 + .../src/main/cpp/src/core/audio/SNDFileCpp.h | 61 + .../cpp/src/core/audio/VorbisConsumer.cpp | 80 + .../main/cpp/src/core/audio/VorbisConsumer.h | 39 + .../cpp/src/core/audio/VorbisProducer.cpp | 84 + .../main/cpp/src/core/audio/VorbisProducer.h | 37 + .../cpp/src/core/control/AudioController.cpp | 132 + .../cpp/src/core/control/AudioController.h | 72 + .../cpp/src/core/control/ClipboardHandler.cpp | 323 ++ .../cpp/src/core/control/ClipboardHandler.h | 73 + .../src/core/control/CompassController.cpp | 125 + .../cpp/src/core/control/CompassController.h | 136 + .../src/main/cpp/src/core/control/Control.cpp | 2612 +++++++++++ entry/src/main/cpp/src/core/control/Control.h | 569 +++ .../cpp/src/core/control/CrashHandler.cpp | 83 + .../main/cpp/src/core/control/CrashHandler.h | 17 + .../cpp/src/core/control/DeviceListHelper.cpp | 124 + .../cpp/src/core/control/DeviceListHelper.h | 46 + .../cpp/src/core/control/ExportHelper.cpp | 110 + .../main/cpp/src/core/control/ExportHelper.h | 62 + .../core/control/GeometryToolController.cpp | 127 + .../src/core/control/GeometryToolController.h | 103 + .../cpp/src/core/control/LatexController.cpp | 366 ++ .../cpp/src/core/control/LatexController.h | 230 + .../PageBackgroundChangeController.cpp | 380 ++ .../control/PageBackgroundChangeController.h | 103 + .../main/cpp/src/core/control/PdfCache.cpp | 164 + .../src/main/cpp/src/core/control/PdfCache.h | 93 + .../cpp/src/core/control/PrintHandler.cpp | 128 + .../main/cpp/src/core/control/PrintHandler.h | 22 + .../cpp/src/core/control/RecentManager.cpp | 150 + .../main/cpp/src/core/control/RecentManager.h | 70 + .../cpp/src/core/control/ScrollHandler.cpp | 125 + .../main/cpp/src/core/control/ScrollHandler.h | 58 + .../cpp/src/core/control/SearchControl.cpp | 73 + .../main/cpp/src/core/control/SearchControl.h | 52 + .../src/core/control/SetsquareController.cpp | 120 + .../src/core/control/SetsquareController.h | 130 + entry/src/main/cpp/src/core/control/Tool.cpp | 38 + entry/src/main/cpp/src/core/control/Tool.h | 77 + .../main/cpp/src/core/control/ToolBase.cpp | 77 + .../src/main/cpp/src/core/control/ToolBase.h | 140 + .../main/cpp/src/core/control/ToolEnums.cpp | 142 + .../src/main/cpp/src/core/control/ToolEnums.h | 200 + .../main/cpp/src/core/control/ToolHandler.cpp | 699 +++ .../main/cpp/src/core/control/ToolHandler.h | 417 ++ .../src/core/control/UndoRedoController.cpp | 89 + .../cpp/src/core/control/UndoRedoController.h | 48 + .../main/cpp/src/core/control/XournalMain.cpp | 705 +++ .../main/cpp/src/core/control/XournalMain.h | 20 + .../core/control/actions/ActionDatabase.cpp | 209 + .../src/core/control/actions/ActionDatabase.h | 120 + .../core/control/actions/ActionProperties.h | 1100 +++++ .../cpp/src/core/control/actions/ActionRef.h | 15 + .../cpp/src/core/control/jobs/AutosaveJob.cpp | 79 + .../cpp/src/core/control/jobs/AutosaveJob.h | 36 + .../src/core/control/jobs/BaseExportJob.cpp | 104 + .../cpp/src/core/control/jobs/BaseExportJob.h | 74 + .../cpp/src/core/control/jobs/BlockingJob.cpp | 25 + .../cpp/src/core/control/jobs/BlockingJob.h | 34 + .../src/core/control/jobs/CustomExportJob.cpp | 158 + .../src/core/control/jobs/CustomExportJob.h | 91 + .../cpp/src/core/control/jobs/ImageExport.cpp | 260 ++ .../cpp/src/core/control/jobs/ImageExport.h | 196 + .../main/cpp/src/core/control/jobs/Job.cpp | 55 + .../src/main/cpp/src/core/control/jobs/Job.h | 99 + .../src/core/control/jobs/PdfExportJob.cpp | 42 + .../cpp/src/core/control/jobs/PdfExportJob.h | 32 + .../cpp/src/core/control/jobs/PreviewJob.cpp | 132 + .../cpp/src/core/control/jobs/PreviewJob.h | 61 + .../src/core/control/jobs/ProgressListener.h | 28 + .../cpp/src/core/control/jobs/RenderJob.cpp | 120 + .../cpp/src/core/control/jobs/RenderJob.h | 50 + .../cpp/src/core/control/jobs/SaveJob.cpp | 156 + .../main/cpp/src/core/control/jobs/SaveJob.h | 43 + .../cpp/src/core/control/jobs/Scheduler.cpp | 212 + .../cpp/src/core/control/jobs/Scheduler.h | 147 + .../core/control/jobs/XournalScheduler.cpp | 119 + .../src/core/control/jobs/XournalScheduler.h | 58 + .../src/core/control/latex/LatexGenerator.cpp | 95 + .../src/core/control/latex/LatexGenerator.h | 55 + .../core/control/layer/LayerController.cpp | 453 ++ .../src/core/control/layer/LayerController.h | 121 + .../core/control/layer/LayerCtrlListener.cpp | 19 + .../core/control/layer/LayerCtrlListener.h | 31 + .../core/control/pagetype/PageTypeHandler.cpp | 166 + .../core/control/pagetype/PageTypeHandler.h | 51 + .../core/control/settings/ButtonConfig.cpp | 103 + .../src/core/control/settings/ButtonConfig.h | 81 + .../core/control/settings/LatexSettings.cpp | 1 + .../src/core/control/settings/LatexSettings.h | 51 + .../core/control/settings/MetadataManager.cpp | 239 ++ .../core/control/settings/MetadataManager.h | 86 + .../control/settings/PageTemplateSettings.cpp | 119 + .../control/settings/PageTemplateSettings.h | 81 + .../core/control/settings/RecolorParameters.h | 16 + .../src/core/control/settings/Settings.cpp | 2697 ++++++++++++ .../cpp/src/core/control/settings/Settings.h | 1186 +++++ .../core/control/settings/SettingsEnums.cpp | 77 + .../src/core/control/settings/SettingsEnums.h | 205 + .../src/core/control/settings/ViewModes.cpp | 35 + .../cpp/src/core/control/settings/ViewModes.h | 48 + .../shaperecognizer/CircleRecognizer.cpp | 77 + .../shaperecognizer/CircleRecognizer.h | 29 + .../core/control/shaperecognizer/Inertia.cpp | 87 + .../core/control/shaperecognizer/Inertia.h | 48 + .../control/shaperecognizer/RecoSegment.cpp | 48 + .../control/shaperecognizer/RecoSegment.h | 41 + .../shaperecognizer/ShapeRecognizer.cpp | 449 ++ .../control/shaperecognizer/ShapeRecognizer.h | 50 + .../shaperecognizer/ShapeRecognizerConfig.h | 34 + .../src/core/control/tools/ArrowHandler.cpp | 87 + .../cpp/src/core/control/tools/ArrowHandler.h | 32 + .../core/control/tools/BaseShapeHandler.cpp | 180 + .../src/core/control/tools/BaseShapeHandler.h | 116 + .../control/tools/CoordinateSystemHandler.cpp | 65 + .../control/tools/CoordinateSystemHandler.h | 30 + .../core/control/tools/CursorSelectionType.h | 32 + .../src/core/control/tools/EditSelection.cpp | 1262 ++++++ .../src/core/control/tools/EditSelection.h | 470 ++ .../control/tools/EditSelectionContents.cpp | 582 +++ .../control/tools/EditSelectionContents.h | 232 + .../src/core/control/tools/EllipseHandler.cpp | 106 + .../src/core/control/tools/EllipseHandler.h | 31 + .../src/core/control/tools/EraseHandler.cpp | 145 + .../cpp/src/core/control/tools/EraseHandler.h | 58 + .../src/core/control/tools/ImageHandler.cpp | 153 + .../cpp/src/core/control/tools/ImageHandler.h | 52 + .../core/control/tools/ImageSizeSelection.cpp | 36 + .../core/control/tools/ImageSizeSelection.h | 42 + .../src/core/control/tools/InputHandler.cpp | 62 + .../cpp/src/core/control/tools/InputHandler.h | 108 + .../control/tools/LaserPointerHandler.cpp | 99 + .../core/control/tools/LaserPointerHandler.h | 71 + .../core/control/tools/PdfElemSelection.cpp | 139 + .../src/core/control/tools/PdfElemSelection.h | 109 + .../core/control/tools/RectangleHandler.cpp | 59 + .../src/core/control/tools/RectangleHandler.h | 31 + .../src/core/control/tools/RulerHandler.cpp | 19 + .../cpp/src/core/control/tools/RulerHandler.h | 31 + .../cpp/src/core/control/tools/Selector.cpp | 199 + .../cpp/src/core/control/tools/Selector.h | 98 + .../control/tools/SnapToGridInputHandler.cpp | 154 + .../control/tools/SnapToGridInputHandler.h | 115 + .../src/core/control/tools/SplineHandler.cpp | 406 ++ .../src/core/control/tools/SplineHandler.h | 129 + .../src/core/control/tools/StrokeHandler.cpp | 301 ++ .../src/core/control/tools/StrokeHandler.h | 108 + .../core/control/tools/StrokeStabilizer.cpp | 405 ++ .../src/core/control/tools/StrokeStabilizer.h | 586 +++ .../core/control/tools/StrokeStabilizerEnum.h | 27 + .../cpp/src/core/control/tools/TextEditor.cpp | 1112 +++++ .../cpp/src/core/control/tools/TextEditor.h | 214 + .../control/tools/TextEditorKeyBindings.h | 150 + .../control/tools/VerticalToolHandler.cpp | 143 + .../core/control/tools/VerticalToolHandler.h | 135 + .../cpp/src/core/control/xml/Attribute.cpp | 9 + .../main/cpp/src/core/control/xml/Attribute.h | 30 + .../core/control/xml/DoubleArrayAttribute.cpp | 32 + .../core/control/xml/DoubleArrayAttribute.h | 31 + .../src/core/control/xml/DoubleAttribute.cpp | 20 + .../src/core/control/xml/DoubleAttribute.h | 29 + .../cpp/src/core/control/xml/IntAttribute.cpp | 18 + .../cpp/src/core/control/xml/IntAttribute.h | 28 + .../src/core/control/xml/SizeTAttribute.cpp | 18 + .../cpp/src/core/control/xml/SizeTAttribute.h | 30 + .../src/core/control/xml/TextAttribute.cpp | 25 + .../cpp/src/core/control/xml/TextAttribute.h | 30 + .../cpp/src/core/control/xml/XmlAudioNode.cpp | 13 + .../cpp/src/core/control/xml/XmlAudioNode.h | 32 + .../cpp/src/core/control/xml/XmlImageNode.cpp | 65 + .../cpp/src/core/control/xml/XmlImageNode.h | 38 + .../main/cpp/src/core/control/xml/XmlNode.cpp | 95 + .../main/cpp/src/core/control/xml/XmlNode.h | 52 + .../cpp/src/core/control/xml/XmlPointNode.cpp | 34 + .../cpp/src/core/control/xml/XmlPointNode.h | 32 + .../cpp/src/core/control/xml/XmlTexNode.cpp | 27 + .../cpp/src/core/control/xml/XmlTexNode.h | 33 + .../cpp/src/core/control/xml/XmlTextNode.cpp | 30 + .../cpp/src/core/control/xml/XmlTextNode.h | 33 + .../src/core/control/xojfile/LoadHandler.cpp | 1188 +++++ .../src/core/control/xojfile/LoadHandler.h | 176 + .../control/xojfile/LoadHandlerHelper.cpp | 211 + .../core/control/xojfile/LoadHandlerHelper.h | 30 + .../src/core/control/xojfile/SaveHandler.cpp | 379 ++ .../src/core/control/xojfile/SaveHandler.h | 85 + .../core/control/xojfile/XojExportHandler.cpp | 61 + .../core/control/xojfile/XojExportHandler.h | 42 + .../cpp/src/core/control/zoom/ZoomControl.cpp | 422 ++ .../cpp/src/core/control/zoom/ZoomControl.h | 265 ++ .../src/core/control/zoom/ZoomListener.cpp | 5 + .../cpp/src/core/control/zoom/ZoomListener.h | 20 + .../src/main/cpp/src/core/enums/Action.enum.h | 195 + .../cpp/src/core/enums/generateConvert.php | 104 + .../generated/Action.NameMap.generated.h | 121 + entry/src/main/cpp/src/core/gui/Builder.cpp | 10 + entry/src/main/cpp/src/core/gui/Builder.h | 42 + .../cpp/src/core/gui/CreatePreviewImage.cpp | 39 + .../cpp/src/core/gui/CreatePreviewImage.h | 26 + .../main/cpp/src/core/gui/FloatingToolbox.cpp | 217 + .../main/cpp/src/core/gui/FloatingToolbox.h | 94 + entry/src/main/cpp/src/core/gui/GladeGui.cpp | 57 + entry/src/main/cpp/src/core/gui/GladeGui.h | 55 + .../main/cpp/src/core/gui/GladeSearchpath.cpp | 52 + .../main/cpp/src/core/gui/GladeSearchpath.h | 41 + .../main/cpp/src/core/gui/IconNameHelper.cpp | 15 + .../main/cpp/src/core/gui/IconNameHelper.h | 27 + entry/src/main/cpp/src/core/gui/Layout.cpp | 432 ++ entry/src/main/cpp/src/core/gui/Layout.h | 175 + .../main/cpp/src/core/gui/LayoutMapper.cpp | 177 + .../src/main/cpp/src/core/gui/LayoutMapper.h | 114 + .../cpp/src/core/gui/LegacyRedrawable.cpp | 18 + .../main/cpp/src/core/gui/LegacyRedrawable.h | 87 + .../src/main/cpp/src/core/gui/MainWindow.cpp | 742 ++++ entry/src/main/cpp/src/core/gui/MainWindow.h | 196 + .../src/core/gui/PagePreviewDecoration.cpp | 129 + .../cpp/src/core/gui/PagePreviewDecoration.h | 70 + entry/src/main/cpp/src/core/gui/PageView.cpp | 1205 ++++++ entry/src/main/cpp/src/core/gui/PageView.h | 323 ++ .../src/core/gui/PageViewFindObjectHelper.h | 204 + .../cpp/src/core/gui/PaperFormatUtils.cpp | 57 + .../main/cpp/src/core/gui/PaperFormatUtils.h | 42 + .../cpp/src/core/gui/PdfFloatingToolbox.cpp | 239 ++ .../cpp/src/core/gui/PdfFloatingToolbox.h | 97 + .../main/cpp/src/core/gui/PopoverFactory.h | 23 + .../main/cpp/src/core/gui/RepaintHandler.cpp | 29 + .../main/cpp/src/core/gui/RepaintHandler.h | 40 + entry/src/main/cpp/src/core/gui/SearchBar.cpp | 198 + entry/src/main/cpp/src/core/gui/SearchBar.h | 65 + entry/src/main/cpp/src/core/gui/Shadow.cpp | 217 + entry/src/main/cpp/src/core/gui/Shadow.h | 52 + entry/src/main/cpp/src/core/gui/ShadowCode.c | 99 + .../cpp/src/core/gui/ToolbarDefinitions.h | 44 + .../cpp/src/core/gui/ToolitemDragDrop.cpp | 88 + .../main/cpp/src/core/gui/ToolitemDragDrop.h | 57 + .../src/main/cpp/src/core/gui/XournalView.cpp | 861 ++++ entry/src/main/cpp/src/core/gui/XournalView.h | 195 + .../main/cpp/src/core/gui/XournalppCursor.cpp | 648 +++ .../main/cpp/src/core/gui/XournalppCursor.h | 76 + .../cpp/src/core/gui/dialog/AboutDialog.cpp | 76 + .../cpp/src/core/gui/dialog/AboutDialog.h | 29 + .../core/gui/dialog/AbstractLatexDialog.cpp | 106 + .../src/core/gui/dialog/AbstractLatexDialog.h | 93 + .../src/core/gui/dialog/ButtonConfigGui.cpp | 284 ++ .../cpp/src/core/gui/dialog/ButtonConfigGui.h | 62 + .../core/gui/dialog/DeviceClassConfigGui.cpp | 61 + .../core/gui/dialog/DeviceClassConfigGui.h | 42 + .../src/core/gui/dialog/DeviceListDialog.cpp | 39 + .../src/core/gui/dialog/DeviceListDialog.h | 37 + .../src/core/gui/dialog/DeviceTestingArea.cpp | 425 ++ .../src/core/gui/dialog/DeviceTestingArea.h | 82 + .../cpp/src/core/gui/dialog/ExportDialog.cpp | 192 + .../cpp/src/core/gui/dialog/ExportDialog.h | 76 + .../src/core/gui/dialog/ExtEdLatexDialog.cpp | 192 + .../src/core/gui/dialog/ExtEdLatexDialog.h | 48 + .../gui/dialog/FileChooserFiltersHelper.cpp | 44 + .../gui/dialog/FileChooserFiltersHelper.h | 26 + .../cpp/src/core/gui/dialog/FormatDialog.cpp | 216 + .../cpp/src/core/gui/dialog/FormatDialog.h | 72 + .../cpp/src/core/gui/dialog/GotoDialog.cpp | 34 + .../main/cpp/src/core/gui/dialog/GotoDialog.h | 38 + .../src/core/gui/dialog/IntEdLatexDialog.cpp | 184 + .../src/core/gui/dialog/IntEdLatexDialog.h | 55 + .../src/core/gui/dialog/LanguageConfigGui.cpp | 88 + .../src/core/gui/dialog/LanguageConfigGui.h | 37 + .../core/gui/dialog/LatexSettingsPanel.cpp | 205 + .../src/core/gui/dialog/LatexSettingsPanel.h | 50 + .../core/gui/dialog/PageTemplateDialog.cpp | 224 + .../src/core/gui/dialog/PageTemplateDialog.h | 80 + .../cpp/src/core/gui/dialog/PluginDialog.cpp | 48 + .../cpp/src/core/gui/dialog/PluginDialog.h | 48 + .../src/core/gui/dialog/PluginDialogEntry.cpp | 52 + .../src/core/gui/dialog/PluginDialogEntry.h | 40 + .../src/core/gui/dialog/RenameLayerDialog.cpp | 40 + .../src/core/gui/dialog/RenameLayerDialog.h | 36 + .../dialog/SelectBackgroundColorDialog.cpp | 119 + .../gui/dialog/SelectBackgroundColorDialog.h | 50 + .../core/gui/dialog/SelectOpacityDialog.cpp | 121 + .../src/core/gui/dialog/SelectOpacityDialog.h | 41 + .../src/core/gui/dialog/SettingsDialog.cpp | 1104 +++++ .../cpp/src/core/gui/dialog/SettingsDialog.h | 105 + .../gui/dialog/SettingsDialogPaletteTab.cpp | 213 + .../gui/dialog/SettingsDialogPaletteTab.h | 36 + .../core/gui/dialog/ToolbarManageDialog.cpp | 187 + .../src/core/gui/dialog/ToolbarManageDialog.h | 60 + .../cpp/src/core/gui/dialog/XojOpenDlg.cpp | 211 + .../main/cpp/src/core/gui/dialog/XojOpenDlg.h | 32 + .../cpp/src/core/gui/dialog/XojSaveDlg.cpp | 91 + .../main/cpp/src/core/gui/dialog/XojSaveDlg.h | 56 + .../BackgroundSelectDialogBase.cpp | 111 + .../BackgroundSelectDialogBase.h | 64 + .../backgroundSelect/BaseElementView.cpp | 145 + .../dialog/backgroundSelect/BaseElementView.h | 87 + .../backgroundSelect/ImageElementView.cpp | 46 + .../backgroundSelect/ImageElementView.h | 58 + .../dialog/backgroundSelect/ImagesDialog.cpp | 120 + .../dialog/backgroundSelect/ImagesDialog.h | 47 + .../backgroundSelect/PdfElementView.cpp | 34 + .../dialog/backgroundSelect/PdfElementView.h | 55 + .../backgroundSelect/PdfPagesDialog.cpp | 98 + .../dialog/backgroundSelect/PdfPagesDialog.h | 41 + .../ToolItemDragCurrentData.cpp | 47 + .../ToolItemDragCurrentData.h | 38 + .../toolbarCustomize/ToolbarAdapter.cpp | 345 ++ .../dialog/toolbarCustomize/ToolbarAdapter.h | 83 + .../ToolbarCustomizeDialog.cpp | 325 ++ .../toolbarCustomize/ToolbarCustomizeDialog.h | 93 + .../ToolbarDragDropHandler.cpp | 57 + .../toolbarCustomize/ToolbarDragDropHandler.h | 42 + .../ToolbarDragDropHelper.cpp | 105 + .../toolbarCustomize/ToolbarDragDropHelper.h | 25 + .../gui/inputdevices/AbstractInputHandler.cpp | 97 + .../gui/inputdevices/AbstractInputHandler.h | 45 + .../gui/inputdevices/CompassInputHandler.cpp | 90 + .../gui/inputdevices/CompassInputHandler.h | 42 + .../src/core/gui/inputdevices/DeviceId.cpp | 17 + .../cpp/src/core/gui/inputdevices/DeviceId.h | 26 + .../inputdevices/GeometryToolInputHandler.cpp | 309 ++ .../inputdevices/GeometryToolInputHandler.h | 164 + .../core/gui/inputdevices/HandRecognition.cpp | 183 + .../core/gui/inputdevices/HandRecognition.h | 112 + .../core/gui/inputdevices/InputContext.cpp | 327 ++ .../src/core/gui/inputdevices/InputContext.h | 113 + .../src/core/gui/inputdevices/InputEvents.cpp | 111 + .../src/core/gui/inputdevices/InputEvents.h | 83 + .../src/core/gui/inputdevices/InputUtils.cpp | 42 + .../src/core/gui/inputdevices/InputUtils.h | 50 + .../gui/inputdevices/KeyboardInputHandler.cpp | 17 + .../gui/inputdevices/KeyboardInputHandler.h | 27 + .../gui/inputdevices/MouseInputHandler.cpp | 157 + .../core/gui/inputdevices/MouseInputHandler.h | 51 + .../core/gui/inputdevices/PenInputHandler.cpp | 585 +++ .../core/gui/inputdevices/PenInputHandler.h | 158 + .../gui/inputdevices/PositionInputData.cpp | 7 + .../core/gui/inputdevices/PositionInputData.h | 37 + .../src/core/gui/inputdevices/PrintEvent.cpp | 172 + .../src/core/gui/inputdevices/PrintEvent.h | 23 + .../inputdevices/SetsquareInputHandler.cpp | 90 + .../gui/inputdevices/SetsquareInputHandler.h | 40 + .../gui/inputdevices/StylusInputHandler.cpp | 209 + .../gui/inputdevices/StylusInputHandler.h | 49 + .../inputdevices/TouchDrawingInputHandler.cpp | 158 + .../inputdevices/TouchDrawingInputHandler.h | 44 + .../gui/inputdevices/TouchInputHandler.cpp | 221 + .../core/gui/inputdevices/TouchInputHandler.h | 61 + .../touchdisable/TouchDisableCustom.cpp | 15 + .../touchdisable/TouchDisableCustom.h | 31 + .../touchdisable/TouchDisableInterface.cpp | 7 + .../touchdisable/TouchDisableInterface.h | 26 + .../touchdisable/TouchDisableX11.cpp | 86 + .../touchdisable/TouchDisableX11.h | 57 + .../gui/menus/PageTypeSelectionMenuBase.cpp | 86 + .../gui/menus/PageTypeSelectionMenuBase.h | 60 + .../gui/menus/StaticAssertActionNamespace.h | 52 + .../core/gui/menus/menubar/AbstractSubmenu.h | 26 + .../src/core/gui/menus/menubar/Menubar.cpp | 102 + .../cpp/src/core/gui/menus/menubar/Menubar.h | 80 + .../gui/menus/menubar/PageTypeSubmenu.cpp | 93 + .../core/gui/menus/menubar/PageTypeSubmenu.h | 43 + .../core/gui/menus/menubar/PluginsSubmenu.cpp | 33 + .../core/gui/menus/menubar/PluginsSubmenu.h | 34 + .../menus/menubar/RecentDocumentsSubmenu.cpp | 231 + .../menus/menubar/RecentDocumentsSubmenu.h | 74 + .../menus/menubar/ToolbarSelectionSubmenu.cpp | 88 + .../menus/menubar/ToolbarSelectionSubmenu.h | 38 + .../popoverMenus/PageTypeSelectionPopover.cpp | 458 ++ .../popoverMenus/PageTypeSelectionPopover.h | 79 + .../PageTypeSelectionPopoverGridOnly.cpp | 83 + .../PageTypeSelectionPopoverGridOnly.h | 45 + .../src/core/gui/scroll/ScrollHandling.cpp | 41 + .../cpp/src/core/gui/scroll/ScrollHandling.h | 39 + .../core/gui/sidebar/AbstractSidebarPage.cpp | 30 + .../core/gui/sidebar/AbstractSidebarPage.h | 81 + .../main/cpp/src/core/gui/sidebar/Sidebar.cpp | 171 + .../main/cpp/src/core/gui/sidebar/Sidebar.h | 139 + .../sidebar/indextree/SidebarIndexPage.cpp | 305 ++ .../gui/sidebar/indextree/SidebarIndexPage.h | 126 + .../sidebar/previews/base/SidebarLayout.cpp | 107 + .../gui/sidebar/previews/base/SidebarLayout.h | 28 + .../previews/base/SidebarPreviewBase.cpp | 152 + .../previews/base/SidebarPreviewBase.h | 138 + .../previews/base/SidebarPreviewBaseEntry.cpp | 165 + .../previews/base/SidebarPreviewBaseEntry.h | 103 + .../layer/SidebarPreviewLayerEntry.cpp | 72 + .../previews/layer/SidebarPreviewLayerEntry.h | 79 + .../previews/layer/SidebarPreviewLayers.cpp | 143 + .../previews/layer/SidebarPreviewLayers.h | 86 + .../previews/page/SidebarPreviewPageEntry.cpp | 56 + .../previews/page/SidebarPreviewPageEntry.h | 45 + .../previews/page/SidebarPreviewPages.cpp | 139 + .../previews/page/SidebarPreviewPages.h | 76 + .../gui/toolbarMenubar/AbstractSliderItem.cpp | 6 + .../gui/toolbarMenubar/AbstractSliderItem.h | 50 + .../gui/toolbarMenubar/AbstractToolItem.cpp | 25 + .../gui/toolbarMenubar/AbstractToolItem.h | 64 + .../toolbarMenubar/ColorSelectorToolItem.cpp | 61 + .../toolbarMenubar/ColorSelectorToolItem.h | 41 + .../core/gui/toolbarMenubar/ColorToolItem.cpp | 77 + .../core/gui/toolbarMenubar/ColorToolItem.h | 63 + .../gui/toolbarMenubar/ComboToolButton.cpp | 126 + .../core/gui/toolbarMenubar/ComboToolButton.h | 63 + .../DrawingTypeComboToolButton.cpp | 172 + .../DrawingTypeComboToolButton.h | 72 + .../core/gui/toolbarMenubar/FontButton.cpp | 78 + .../src/core/gui/toolbarMenubar/FontButton.h | 37 + .../toolbarMenubar/PluginPlaceholderLabel.cpp | 80 + .../toolbarMenubar/PluginPlaceholderLabel.h | 52 + .../gui/toolbarMenubar/PluginToolButton.cpp | 44 + .../gui/toolbarMenubar/PluginToolButton.h | 35 + .../toolbarMenubar/SliderItemCreationHelper.h | 88 + .../toolbarMenubar/StylePopoverFactory.cpp | 57 + .../gui/toolbarMenubar/StylePopoverFactory.h | 46 + .../core/gui/toolbarMenubar/ToolButton.cpp | 111 + .../src/core/gui/toolbarMenubar/ToolButton.h | 52 + .../gui/toolbarMenubar/ToolMenuHandler.cpp | 550 +++ .../core/gui/toolbarMenubar/ToolMenuHandler.h | 142 + .../core/gui/toolbarMenubar/ToolPageLayer.cpp | 315 ++ .../core/gui/toolbarMenubar/ToolPageLayer.h | 42 + .../gui/toolbarMenubar/ToolPageSpinner.cpp | 138 + .../core/gui/toolbarMenubar/ToolPageSpinner.h | 50 + .../toolbarMenubar/ToolPdfCombocontrol.cpp | 17 + .../gui/toolbarMenubar/ToolPdfCombocontrol.h | 14 + .../toolbarMenubar/ToolSelectCombocontrol.cpp | 29 + .../toolbarMenubar/ToolSelectCombocontrol.h | 24 + .../gui/toolbarMenubar/ToolZoomSlider.cpp | 78 + .../core/gui/toolbarMenubar/ToolZoomSlider.h | 61 + .../gui/toolbarMenubar/TooltipToolButton.cpp | 33 + .../gui/toolbarMenubar/TooltipToolButton.h | 36 + .../gui/toolbarMenubar/icon/ColorIcon.cpp | 62 + .../core/gui/toolbarMenubar/icon/ColorIcon.h | 35 + .../icon/ToolbarSeparatorImage.cpp | 48 + .../icon/ToolbarSeparatorImage.h | 40 + .../gui/toolbarMenubar/model/ColorPalette.cpp | 182 + .../gui/toolbarMenubar/model/ColorPalette.h | 217 + .../gui/toolbarMenubar/model/ToolbarData.cpp | 163 + .../gui/toolbarMenubar/model/ToolbarData.h | 53 + .../gui/toolbarMenubar/model/ToolbarEntry.cpp | 55 + .../gui/toolbarMenubar/model/ToolbarEntry.h | 51 + .../gui/toolbarMenubar/model/ToolbarItem.cpp | 22 + .../gui/toolbarMenubar/model/ToolbarItem.h | 40 + .../gui/toolbarMenubar/model/ToolbarModel.cpp | 162 + .../gui/toolbarMenubar/model/ToolbarModel.h | 48 + .../src/core/gui/widgets/SpinPageAdapter.cpp | 70 + .../src/core/gui/widgets/SpinPageAdapter.h | 65 + .../src/core/gui/widgets/XournalWidget.cpp | 327 ++ .../cpp/src/core/gui/widgets/XournalWidget.h | 88 + .../cpp/src/core/gui/widgets/ZoomCallib.cpp | 155 + .../cpp/src/core/gui/widgets/ZoomCallib.h | 43 + .../main/cpp/src/core/model/AudioElement.cpp | 48 + .../main/cpp/src/core/model/AudioElement.h | 46 + .../cpp/src/core/model/BackgroundConfig.cpp | 67 + .../cpp/src/core/model/BackgroundConfig.h | 47 + .../cpp/src/core/model/BackgroundImage.cpp | 87 + .../main/cpp/src/core/model/BackgroundImage.h | 49 + entry/src/main/cpp/src/core/model/Compass.cpp | 44 + entry/src/main/cpp/src/core/model/Compass.h | 49 + .../src/main/cpp/src/core/model/Document.cpp | 472 ++ entry/src/main/cpp/src/core/model/Document.h | 184 + .../cpp/src/core/model/DocumentChangeType.h | 14 + .../cpp/src/core/model/DocumentHandler.cpp | 33 + .../main/cpp/src/core/model/DocumentHandler.h | 44 + .../cpp/src/core/model/DocumentListener.cpp | 32 + .../cpp/src/core/model/DocumentListener.h | 38 + entry/src/main/cpp/src/core/model/Element.cpp | 162 + entry/src/main/cpp/src/core/model/Element.h | 123 + .../cpp/src/core/model/ElementContainer.h | 25 + .../src/core/model/ElementInsertionPosition.h | 58 + entry/src/main/cpp/src/core/model/Font.cpp | 71 + entry/src/main/cpp/src/core/model/Font.h | 68 + .../cpp/src/core/model/FormatDefinitions.cpp | 7 + .../cpp/src/core/model/FormatDefinitions.h | 20 + .../main/cpp/src/core/model/GeometryTool.cpp | 49 + .../main/cpp/src/core/model/GeometryTool.h | 112 + entry/src/main/cpp/src/core/model/Image.cpp | 268 ++ entry/src/main/cpp/src/core/model/Image.h | 99 + entry/src/main/cpp/src/core/model/Layer.cpp | 145 + entry/src/main/cpp/src/core/model/Layer.h | 126 + .../src/main/cpp/src/core/model/LineStyle.cpp | 38 + entry/src/main/cpp/src/core/model/LineStyle.h | 55 + .../cpp/src/core/model/LinkDestination.cpp | 99 + .../main/cpp/src/core/model/LinkDestination.h | 108 + .../src/main/cpp/src/core/model/MathVect.cpp | 17 + entry/src/main/cpp/src/core/model/MathVect.h | 42 + .../src/main/cpp/src/core/model/OverlayBase.h | 23 + .../main/cpp/src/core/model/PageHandler.cpp | 40 + .../src/main/cpp/src/core/model/PageHandler.h | 52 + .../main/cpp/src/core/model/PageListener.cpp | 19 + .../main/cpp/src/core/model/PageListener.h | 42 + entry/src/main/cpp/src/core/model/PageRef.h | 18 + .../src/main/cpp/src/core/model/PageType.cpp | 37 + entry/src/main/cpp/src/core/model/PageType.h | 59 + .../src/main/cpp/src/core/model/PaperSize.cpp | 30 + entry/src/main/cpp/src/core/model/PaperSize.h | 62 + .../main/cpp/src/core/model/PathParameter.h | 30 + entry/src/main/cpp/src/core/model/Point.cpp | 23 + entry/src/main/cpp/src/core/model/Point.h | 102 + .../src/main/cpp/src/core/model/Setsquare.cpp | 49 + entry/src/main/cpp/src/core/model/Setsquare.h | 57 + .../src/main/cpp/src/core/model/Snapping.cpp | 71 + entry/src/main/cpp/src/core/model/Snapping.h | 83 + .../main/cpp/src/core/model/SplineSegment.cpp | 70 + .../main/cpp/src/core/model/SplineSegment.h | 109 + entry/src/main/cpp/src/core/model/Stroke.cpp | 875 ++++ entry/src/main/cpp/src/core/model/Stroke.h | 228 + .../main/cpp/src/core/model/StrokeContour.cpp | 354 ++ .../main/cpp/src/core/model/StrokeContour.h | 44 + .../main/cpp/src/core/model/StrokeStyle.cpp | 79 + .../src/main/cpp/src/core/model/StrokeStyle.h | 30 + .../src/main/cpp/src/core/model/TexImage.cpp | 170 + entry/src/main/cpp/src/core/model/TexImage.h | 114 + entry/src/main/cpp/src/core/model/Text.cpp | 199 + entry/src/main/cpp/src/core/model/Text.h | 80 + entry/src/main/cpp/src/core/model/XojPage.cpp | 177 + entry/src/main/cpp/src/core/model/XojPage.h | 141 + .../src/core/model/eraser/ErasableStroke.cpp | 375 ++ .../src/core/model/eraser/ErasableStroke.h | 152 + .../eraser/ErasableStrokeOverlapTree.cpp | 233 + .../model/eraser/ErasableStrokeOverlapTree.h | 163 + .../cpp/src/core/model/eraser/PaddedBox.h | 34 + .../main/cpp/src/core/model/eraser/readme.txt | 4 + .../cpp/src/core/pdf/base/HybridPdfExport.cpp | 127 + .../cpp/src/core/pdf/base/HybridPdfExport.h | 52 + .../src/core/pdf/base/PdfExportBackend.cpp | 45 + .../cpp/src/core/pdf/base/PdfExportBackend.h | 43 + .../main/cpp/src/core/pdf/base/QPdfExport.cpp | 179 + .../main/cpp/src/core/pdf/base/QPdfExport.h | 36 + .../src/core/pdf/base/XojCairoPdfExport.cpp | 283 ++ .../cpp/src/core/pdf/base/XojCairoPdfExport.h | 83 + .../cpp/src/core/pdf/base/XojPdfAction.cpp | 5 + .../main/cpp/src/core/pdf/base/XojPdfAction.h | 31 + .../core/pdf/base/XojPdfBookmarkIterator.cpp | 5 + .../core/pdf/base/XojPdfBookmarkIterator.h | 29 + .../cpp/src/core/pdf/base/XojPdfDocument.cpp | 51 + .../cpp/src/core/pdf/base/XojPdfDocument.h | 54 + .../core/pdf/base/XojPdfDocumentInterface.cpp | 5 + .../core/pdf/base/XojPdfDocumentInterface.h | 45 + .../cpp/src/core/pdf/base/XojPdfExport.cpp | 12 + .../main/cpp/src/core/pdf/base/XojPdfExport.h | 43 + .../src/core/pdf/base/XojPdfExportFactory.cpp | 29 + .../src/core/pdf/base/XojPdfExportFactory.h | 30 + .../main/cpp/src/core/pdf/base/XojPdfPage.cpp | 3 + .../main/cpp/src/core/pdf/base/XojPdfPage.h | 113 + .../core/pdf/popplerapi/PopplerGlibAction.cpp | 100 + .../core/pdf/popplerapi/PopplerGlibAction.h | 42 + .../pdf/popplerapi/PopplerGlibDocument.cpp | 136 + .../core/pdf/popplerapi/PopplerGlibDocument.h | 53 + .../core/pdf/popplerapi/PopplerGlibPage.cpp | 331 ++ .../src/core/pdf/popplerapi/PopplerGlibPage.h | 57 + .../PopplerGlibPageBookmarkIterator.cpp | 49 + .../PopplerGlibPageBookmarkIterator.h | 35 + .../ActionBackwardCompatibilityLayer.cpp | 208 + .../plugin/ActionBackwardCompatibilityLayer.h | 29 + entry/src/main/cpp/src/core/plugin/Plugin.cpp | 367 ++ entry/src/main/cpp/src/core/plugin/Plugin.h | 218 + .../cpp/src/core/plugin/PluginController.cpp | 189 + .../cpp/src/core/plugin/PluginController.h | 63 + .../cpp/src/core/plugin/luapi_application.h | 3805 +++++++++++++++++ .../main/cpp/src/core/undo/AddUndoAction.cpp | 103 + .../main/cpp/src/core/undo/AddUndoAction.h | 41 + .../cpp/src/core/undo/ArrangeUndoAction.cpp | 59 + .../cpp/src/core/undo/ArrangeUndoAction.h | 51 + .../cpp/src/core/undo/ColorUndoAction.cpp | 97 + .../main/cpp/src/core/undo/ColorUndoAction.h | 42 + .../cpp/src/core/undo/DeleteUndoAction.cpp | 105 + .../main/cpp/src/core/undo/DeleteUndoAction.h | 42 + .../src/core/undo/EmergencySaveRestore.cpp | 22 + .../cpp/src/core/undo/EmergencySaveRestore.h | 34 + .../cpp/src/core/undo/EraseUndoAction.cpp | 106 + .../main/cpp/src/core/undo/EraseUndoAction.h | 45 + .../main/cpp/src/core/undo/FillUndoAction.cpp | 88 + .../main/cpp/src/core/undo/FillUndoAction.h | 42 + .../main/cpp/src/core/undo/FontUndoAction.cpp | 118 + .../main/cpp/src/core/undo/FontUndoAction.h | 43 + .../cpp/src/core/undo/GroupUndoAction.cpp | 52 + .../main/cpp/src/core/undo/GroupUndoAction.h | 44 + .../core/undo/InsertDeletePageUndoAction.cpp | 88 + .../core/undo/InsertDeletePageUndoAction.h | 41 + .../src/core/undo/InsertLayerUndoAction.cpp | 39 + .../cpp/src/core/undo/InsertLayerUndoAction.h | 40 + .../cpp/src/core/undo/InsertUndoAction.cpp | 106 + .../main/cpp/src/core/undo/InsertUndoAction.h | 60 + .../src/core/undo/LayerRenameUndoAction.cpp | 38 + .../cpp/src/core/undo/LayerRenameUndoAction.h | 38 + .../cpp/src/core/undo/LineStyleUndoAction.cpp | 86 + .../cpp/src/core/undo/LineStyleUndoAction.h | 53 + .../core/undo/MergeLayerDownUndoAction.cpp | 54 + .../src/core/undo/MergeLayerDownUndoAction.h | 48 + .../src/core/undo/MissingPdfUndoAction.cpp | 66 + .../cpp/src/core/undo/MissingPdfUndoAction.h | 33 + .../cpp/src/core/undo/MoveLayerUndoAction.cpp | 38 + .../cpp/src/core/undo/MoveLayerUndoAction.h | 41 + .../undo/MoveSelectionToLayerUndoAction.cpp | 68 + .../undo/MoveSelectionToLayerUndoAction.h | 45 + .../main/cpp/src/core/undo/MoveUndoAction.cpp | 96 + .../main/cpp/src/core/undo/MoveUndoAction.h | 53 + .../undo/PageBackgroundChangedUndoAction.cpp | 97 + .../undo/PageBackgroundChangedUndoAction.h | 50 + .../cpp/src/core/undo/PageLayerPosEntry.h | 43 + .../core/undo/PageSizeChangeUndoAction.cpp | 42 + .../src/core/undo/PageSizeChangeUndoAction.h | 38 + .../src/core/undo/RecognizerUndoAction.cpp | 61 + .../cpp/src/core/undo/RecognizerUndoAction.h | 44 + .../src/core/undo/RemoveLayerUndoAction.cpp | 37 + .../cpp/src/core/undo/RemoveLayerUndoAction.h | 39 + .../cpp/src/core/undo/RotateUndoAction.cpp | 58 + .../main/cpp/src/core/undo/RotateUndoAction.h | 44 + .../cpp/src/core/undo/ScaleUndoAction.cpp | 62 + .../main/cpp/src/core/undo/ScaleUndoAction.h | 48 + .../main/cpp/src/core/undo/SizeUndoAction.cpp | 118 + .../main/cpp/src/core/undo/SizeUndoAction.h | 46 + .../main/cpp/src/core/undo/SwapUndoAction.cpp | 67 + .../main/cpp/src/core/undo/SwapUndoAction.h | 44 + .../cpp/src/core/undo/TextBoxUndoAction.cpp | 48 + .../cpp/src/core/undo/TextBoxUndoAction.h | 41 + .../src/main/cpp/src/core/undo/UndoAction.cpp | 13 + entry/src/main/cpp/src/core/undo/UndoAction.h | 47 + .../cpp/src/core/undo/UndoRedoHandler.cpp | 229 + .../main/cpp/src/core/undo/UndoRedoHandler.h | 72 + .../main/cpp/src/core/view/CompassView.cpp | 156 + .../src/main/cpp/src/core/view/CompassView.h | 105 + .../src/core/view/DebugShowRepaintBounds.h | 9 + .../main/cpp/src/core/view/DocumentView.cpp | 115 + .../src/main/cpp/src/core/view/DocumentView.h | 93 + .../src/core/view/ElementContainerView.cpp | 20 + .../cpp/src/core/view/ElementContainerView.h | 31 + .../main/cpp/src/core/view/ElementView.cpp | 32 + .../cpp/src/core/view/ErasableStrokeView.cpp | 309 ++ .../cpp/src/core/view/ErasableStrokeView.h | 62 + .../cpp/src/core/view/GeometryToolView.cpp | 71 + .../main/cpp/src/core/view/GeometryToolView.h | 132 + .../src/main/cpp/src/core/view/ImageView.cpp | 39 + entry/src/main/cpp/src/core/view/ImageView.h | 30 + .../src/main/cpp/src/core/view/LayerView.cpp | 48 + entry/src/main/cpp/src/core/view/LayerView.h | 32 + entry/src/main/cpp/src/core/view/Mask.cpp | 211 + entry/src/main/cpp/src/core/view/Mask.h | 91 + .../src/main/cpp/src/core/view/Repaintable.h | 68 + .../main/cpp/src/core/view/SetsquareView.cpp | 293 ++ .../main/cpp/src/core/view/SetsquareView.h | 113 + .../src/main/cpp/src/core/view/StrokeView.cpp | 169 + entry/src/main/cpp/src/core/view/StrokeView.h | 45 + .../cpp/src/core/view/StrokeViewHelper.cpp | 73 + .../main/cpp/src/core/view/StrokeViewHelper.h | 40 + .../main/cpp/src/core/view/TexImageView.cpp | 92 + .../src/main/cpp/src/core/view/TexImageView.h | 30 + entry/src/main/cpp/src/core/view/TextView.cpp | 53 + entry/src/main/cpp/src/core/view/TextView.h | 42 + entry/src/main/cpp/src/core/view/View.h | 52 + .../core/view/background/BackgroundFlags.h | 37 + .../core/view/background/BackgroundView.cpp | 95 + .../src/core/view/background/BackgroundView.h | 52 + .../BaseIsometricBackgroundView.cpp | 86 + .../background/BaseIsometricBackgroundView.h | 41 + .../view/background/DottedBackgroundView.cpp | 57 + .../view/background/DottedBackgroundView.h | 37 + .../view/background/GraphBackgroundView.cpp | 136 + .../view/background/GraphBackgroundView.h | 42 + .../view/background/ImageBackgroundView.cpp | 33 + .../view/background/ImageBackgroundView.h | 33 + .../background/IsoDottedBackgroundView.cpp | 34 + .../view/background/IsoDottedBackgroundView.h | 35 + .../background/IsoGraphBackgroundView.cpp | 89 + .../view/background/IsoGraphBackgroundView.h | 35 + .../view/background/LinedBackgroundView.cpp | 42 + .../view/background/LinedBackgroundView.h | 36 + .../background/OneColorBackgroundView.cpp | 34 + .../view/background/OneColorBackgroundView.h | 42 + .../view/background/PdfBackgroundView.cpp | 30 + .../core/view/background/PdfBackgroundView.h | 41 + .../view/background/PlainBackgroundView.cpp | 21 + .../view/background/PlainBackgroundView.h | 35 + .../view/background/RuledBackgroundView.cpp | 48 + .../view/background/RuledBackgroundView.h | 40 + .../view/background/StavesBackgroundView.cpp | 55 + .../view/background/StavesBackgroundView.h | 41 + .../TransparentCheckerboardBackgroundView.cpp | 43 + .../TransparentCheckerboardBackgroundView.h | 37 + .../overlays/BaseShapeOrSplineToolView.cpp | 85 + .../view/overlays/BaseShapeOrSplineToolView.h | 45 + .../core/view/overlays/BaseStrokeToolView.cpp | 58 + .../core/view/overlays/BaseStrokeToolView.h | 51 + .../view/overlays/ImageSizeSelectionView.cpp | 57 + .../view/overlays/ImageSizeSelectionView.h | 56 + .../core/view/overlays/LaserPointerView.cpp | 85 + .../src/core/view/overlays/LaserPointerView.h | 83 + .../cpp/src/core/view/overlays/OverlayView.h | 45 + .../view/overlays/PdfElementSelectionView.cpp | 37 + .../view/overlays/PdfElementSelectionView.h | 57 + .../core/view/overlays/SearchResultView.cpp | 47 + .../src/core/view/overlays/SearchResultView.h | 57 + .../src/core/view/overlays/SelectorView.cpp | 56 + .../cpp/src/core/view/overlays/SelectorView.h | 51 + .../src/core/view/overlays/ShapeToolView.cpp | 45 + .../src/core/view/overlays/ShapeToolView.h | 58 + .../src/core/view/overlays/SplineToolView.cpp | 135 + .../src/core/view/overlays/SplineToolView.h | 79 + .../StrokeToolFilledHighlighterView.cpp | 73 + .../StrokeToolFilledHighlighterView.h | 25 + .../view/overlays/StrokeToolFilledView.cpp | 62 + .../core/view/overlays/StrokeToolFilledView.h | 59 + .../src/core/view/overlays/StrokeToolView.cpp | 147 + .../src/core/view/overlays/StrokeToolView.h | 108 + .../core/view/overlays/TextEditionView.cpp | 90 + .../src/core/view/overlays/TextEditionView.h | 68 + .../core/view/overlays/VerticalToolView.cpp | 127 + .../src/core/view/overlays/VerticalToolView.h | 82 + .../main/cpp/src/exe/Xournalpp-wrapper.cpp | 244 ++ entry/src/main/cpp/src/exe/Xournalpp.cpp | 60 + entry/src/main/cpp/src/exe/osx/setup-env.cpp | 130 + entry/src/main/cpp/src/exe/osx/setup-env.h | 6 + entry/src/main/cpp/src/exe/win32/console.cpp | 73 + entry/src/main/cpp/src/exe/win32/console.h | 6 + entry/src/main/cpp/src/exe/win32/gui_main.cpp | 16 + entry/src/main/cpp/src/exe/win32/xpp.rc.in | 31 + .../src/exe/xournalpp-wrapper.gresource.xml | 6 + entry/src/main/cpp/src/util/Assert.cpp | 15 + entry/src/main/cpp/src/util/CMakeLists.txt | 13 + entry/src/main/cpp/src/util/Color.cpp | 31 + entry/src/main/cpp/src/util/ElementRange.cpp | 110 + entry/src/main/cpp/src/util/GtkUtil.cpp | 177 + entry/src/main/cpp/src/util/GzUtil.cpp | 11 + entry/src/main/cpp/src/util/NamedColor.cpp | 65 + entry/src/main/cpp/src/util/OutputStream.cpp | 76 + entry/src/main/cpp/src/util/PathUtil.cpp | 535 +++ .../main/cpp/src/util/PlaceholderString.cpp | 107 + entry/src/main/cpp/src/util/Range.cpp | 60 + entry/src/main/cpp/src/util/Recolor.cpp | 57 + entry/src/main/cpp/src/util/SaveNameUtils.cpp | 48 + entry/src/main/cpp/src/util/Stacktrace.cpp | 21 + entry/src/main/cpp/src/util/StringUtils.cpp | 85 + entry/src/main/cpp/src/util/Util.cpp | 81 + entry/src/main/cpp/src/util/VersionInfo.cpp | 120 + entry/src/main/cpp/src/util/XojMsgBox.cpp | 222 + .../main/cpp/src/util/XojPreviewExtractor.cpp | 174 + entry/src/main/cpp/src/util/gdk4_helper.cpp | 32 + entry/src/main/cpp/src/util/gtk4_helper.cpp | 196 + .../main/cpp/src/util/include/filesystem.h | 17 + .../main/cpp/src/util/include/util/Assert.h | 28 + .../util/include/util/BasePointerIterator.h | 81 + .../src/util/include/util/CircularBuffer.h | 49 + .../main/cpp/src/util/include/util/Color.h | 247 ++ .../cpp/src/util/include/util/DispatchPool.h | 142 + .../cpp/src/util/include/util/ElementRange.h | 37 + .../src/util/include/util/EnumIndexedArray.h | 39 + .../cpp/src/util/include/util/GListView.h | 88 + .../src/util/include/util/GVariantTemplate.h | 129 + .../main/cpp/src/util/include/util/GtkUtil.h | 54 + .../main/cpp/src/util/include/util/GzUtil.h | 27 + .../main/cpp/src/util/include/util/Interval.h | 108 + .../main/cpp/src/util/include/util/LoopUtil.h | 31 + .../cpp/src/util/include/util/NamedColor.h | 113 + .../cpp/src/util/include/util/OutputStream.h | 50 + .../main/cpp/src/util/include/util/PairView.h | 133 + .../main/cpp/src/util/include/util/PathUtil.h | 173 + .../src/util/include/util/PlaceholderString.h | 140 + .../main/cpp/src/util/include/util/Point.h | 53 + .../util/include/util/PointerContainerView.h | 92 + .../util/include/util/PopupWindowWrapper.h | 79 + .../main/cpp/src/util/include/util/Range.h | 50 + .../main/cpp/src/util/include/util/Recolor.h | 55 + .../cpp/src/util/include/util/Rectangle.h | 92 + .../cpp/src/util/include/util/SaveNameUtils.h | 36 + .../cpp/src/util/include/util/SmallVector.h | 227 + .../cpp/src/util/include/util/Stacktrace.h | 26 + .../cpp/src/util/include/util/StringUtils.h | 38 + .../cpp/src/util/include/util/TinyVector.h | 134 + .../src/util/include/util/TypeIfThenElse.h | 35 + .../src/util/include/util/UnionOfIntervals.h | 223 + .../src/main/cpp/src/util/include/util/Util.h | 90 + .../cpp/src/util/include/util/VersionInfo.h | 27 + .../src/util/include/util/ViewIteratorBase.h | 77 + .../cpp/src/util/include/util/XojMsgBox.h | 95 + .../util/include/util/XojPreviewExtractor.h | 80 + .../cpp/src/util/include/util/gdk4_helper.h | 22 + .../cpp/src/util/include/util/glib_casts.h | 153 + .../cpp/src/util/include/util/gtk4_helper.h | 103 + .../src/main/cpp/src/util/include/util/i18n.h | 46 + .../cpp/src/util/include/util/logger/Logger.h | 26 + .../util/include/util/move_only_function.h | 70 + .../util/include/util/raii/CLibrariesSPtr.h | 115 + .../util/include/util/raii/CStringWrapper.h | 58 + .../util/include/util/raii/CairoWrappers.h | 70 + .../src/util/include/util/raii/GLibGuards.h | 62 + .../src/util/include/util/raii/GObjectSPtr.h | 48 + .../src/util/include/util/raii/GSourceURef.h | 45 + .../src/util/include/util/raii/GVariantSPtr.h | 44 + .../util/include/util/raii/GtkPaperSizeUPtr.h | 34 + .../util/include/util/raii/GtkWindowUPtr.h | 36 + .../util/include/util/raii/IdentityFunction.h | 22 + .../src/util/include/util/raii/PangoSPtr.h | 33 + .../cpp/src/util/include/util/safe_casts.h | 150 + .../cpp/src/util/include/util/serdesstream.h | 15 + .../util/serializing/BinObjectEncoding.h | 25 + .../util/serializing/HexObjectEncoding.h | 25 + .../util/serializing/InputStreamException.h | 28 + .../include/util/serializing/ObjectEncoding.h | 31 + .../util/serializing/ObjectInputStream.h | 90 + .../util/serializing/ObjectOutputStream.h | 58 + .../include/util/serializing/Serializable.h | 27 + .../cpp/src/util/include/util/utf8_view.h | 168 + entry/src/main/cpp/src/util/logger/Logger.cpp | 68 + .../main/cpp/src/util/raii/CStringWrapper.cpp | 35 + .../util/serializing/BinObjectEncoding.cpp | 13 + .../util/serializing/HexObjectEncoding.cpp | 26 + .../util/serializing/InputStreamException.cpp | 9 + .../src/util/serializing/ObjectEncoding.cpp | 13 + .../util/serializing/ObjectInputStream.cpp | 201 + .../util/serializing/ObjectOutputStream.cpp | 78 + .../src/xoj-preview-extractor/CMakeLists.txt | 65 + .../xoj-preview-extractor/desktop-install.sh | 4 + .../xournalpp-thumbnailer-xoj.schemas | 29 + .../xournalpp-thumbnailer.cpp | 214 + 830 files changed, 95931 insertions(+) create mode 100644 entry/src/main/cpp/src/CMakeLists.txt create mode 100644 entry/src/main/cpp/src/config-debug.h.in create mode 100644 entry/src/main/cpp/src/config-dev.h.in create mode 100644 entry/src/main/cpp/src/config-features.h.in create mode 100644 entry/src/main/cpp/src/config-git.h.in create mode 100644 entry/src/main/cpp/src/config-paths.h.in create mode 100644 entry/src/main/cpp/src/config.h.in create mode 100644 entry/src/main/cpp/src/core/CMakeLists.txt create mode 100644 entry/src/main/cpp/src/core/audio/AudioPlayer.cpp create mode 100644 entry/src/main/cpp/src/core/audio/AudioPlayer.h create mode 100644 entry/src/main/cpp/src/core/audio/AudioQueue.h create mode 100644 entry/src/main/cpp/src/core/audio/AudioRecorder.cpp create mode 100644 entry/src/main/cpp/src/core/audio/AudioRecorder.h create mode 100644 entry/src/main/cpp/src/core/audio/DeviceInfo.cpp create mode 100644 entry/src/main/cpp/src/core/audio/DeviceInfo.h create mode 100644 entry/src/main/cpp/src/core/audio/PortAudioConsumer.cpp create mode 100644 entry/src/main/cpp/src/core/audio/PortAudioConsumer.h create mode 100644 entry/src/main/cpp/src/core/audio/PortAudioProducer.cpp create mode 100644 entry/src/main/cpp/src/core/audio/PortAudioProducer.h create mode 100644 entry/src/main/cpp/src/core/audio/SNDFileCpp.h create mode 100644 entry/src/main/cpp/src/core/audio/VorbisConsumer.cpp create mode 100644 entry/src/main/cpp/src/core/audio/VorbisConsumer.h create mode 100644 entry/src/main/cpp/src/core/audio/VorbisProducer.cpp create mode 100644 entry/src/main/cpp/src/core/audio/VorbisProducer.h create mode 100644 entry/src/main/cpp/src/core/control/AudioController.cpp create mode 100644 entry/src/main/cpp/src/core/control/AudioController.h create mode 100644 entry/src/main/cpp/src/core/control/ClipboardHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/ClipboardHandler.h create mode 100644 entry/src/main/cpp/src/core/control/CompassController.cpp create mode 100644 entry/src/main/cpp/src/core/control/CompassController.h create mode 100644 entry/src/main/cpp/src/core/control/Control.cpp create mode 100644 entry/src/main/cpp/src/core/control/Control.h create mode 100644 entry/src/main/cpp/src/core/control/CrashHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/CrashHandler.h create mode 100644 entry/src/main/cpp/src/core/control/DeviceListHelper.cpp create mode 100644 entry/src/main/cpp/src/core/control/DeviceListHelper.h create mode 100644 entry/src/main/cpp/src/core/control/ExportHelper.cpp create mode 100644 entry/src/main/cpp/src/core/control/ExportHelper.h create mode 100644 entry/src/main/cpp/src/core/control/GeometryToolController.cpp create mode 100644 entry/src/main/cpp/src/core/control/GeometryToolController.h create mode 100644 entry/src/main/cpp/src/core/control/LatexController.cpp create mode 100644 entry/src/main/cpp/src/core/control/LatexController.h create mode 100644 entry/src/main/cpp/src/core/control/PageBackgroundChangeController.cpp create mode 100644 entry/src/main/cpp/src/core/control/PageBackgroundChangeController.h create mode 100644 entry/src/main/cpp/src/core/control/PdfCache.cpp create mode 100644 entry/src/main/cpp/src/core/control/PdfCache.h create mode 100644 entry/src/main/cpp/src/core/control/PrintHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/PrintHandler.h create mode 100644 entry/src/main/cpp/src/core/control/RecentManager.cpp create mode 100644 entry/src/main/cpp/src/core/control/RecentManager.h create mode 100644 entry/src/main/cpp/src/core/control/ScrollHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/ScrollHandler.h create mode 100644 entry/src/main/cpp/src/core/control/SearchControl.cpp create mode 100644 entry/src/main/cpp/src/core/control/SearchControl.h create mode 100644 entry/src/main/cpp/src/core/control/SetsquareController.cpp create mode 100644 entry/src/main/cpp/src/core/control/SetsquareController.h create mode 100644 entry/src/main/cpp/src/core/control/Tool.cpp create mode 100644 entry/src/main/cpp/src/core/control/Tool.h create mode 100644 entry/src/main/cpp/src/core/control/ToolBase.cpp create mode 100644 entry/src/main/cpp/src/core/control/ToolBase.h create mode 100644 entry/src/main/cpp/src/core/control/ToolEnums.cpp create mode 100644 entry/src/main/cpp/src/core/control/ToolEnums.h create mode 100644 entry/src/main/cpp/src/core/control/ToolHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/ToolHandler.h create mode 100644 entry/src/main/cpp/src/core/control/UndoRedoController.cpp create mode 100644 entry/src/main/cpp/src/core/control/UndoRedoController.h create mode 100644 entry/src/main/cpp/src/core/control/XournalMain.cpp create mode 100644 entry/src/main/cpp/src/core/control/XournalMain.h create mode 100644 entry/src/main/cpp/src/core/control/actions/ActionDatabase.cpp create mode 100644 entry/src/main/cpp/src/core/control/actions/ActionDatabase.h create mode 100644 entry/src/main/cpp/src/core/control/actions/ActionProperties.h create mode 100644 entry/src/main/cpp/src/core/control/actions/ActionRef.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/AutosaveJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/AutosaveJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/BaseExportJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/BaseExportJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/BlockingJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/BlockingJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/CustomExportJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/CustomExportJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/ImageExport.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/ImageExport.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/Job.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/Job.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/PdfExportJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/PdfExportJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/PreviewJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/PreviewJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/ProgressListener.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/RenderJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/RenderJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/SaveJob.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/SaveJob.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/Scheduler.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/Scheduler.h create mode 100644 entry/src/main/cpp/src/core/control/jobs/XournalScheduler.cpp create mode 100644 entry/src/main/cpp/src/core/control/jobs/XournalScheduler.h create mode 100644 entry/src/main/cpp/src/core/control/latex/LatexGenerator.cpp create mode 100644 entry/src/main/cpp/src/core/control/latex/LatexGenerator.h create mode 100644 entry/src/main/cpp/src/core/control/layer/LayerController.cpp create mode 100644 entry/src/main/cpp/src/core/control/layer/LayerController.h create mode 100644 entry/src/main/cpp/src/core/control/layer/LayerCtrlListener.cpp create mode 100644 entry/src/main/cpp/src/core/control/layer/LayerCtrlListener.h create mode 100644 entry/src/main/cpp/src/core/control/pagetype/PageTypeHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/pagetype/PageTypeHandler.h create mode 100644 entry/src/main/cpp/src/core/control/settings/ButtonConfig.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/ButtonConfig.h create mode 100644 entry/src/main/cpp/src/core/control/settings/LatexSettings.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/LatexSettings.h create mode 100644 entry/src/main/cpp/src/core/control/settings/MetadataManager.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/MetadataManager.h create mode 100644 entry/src/main/cpp/src/core/control/settings/PageTemplateSettings.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/PageTemplateSettings.h create mode 100644 entry/src/main/cpp/src/core/control/settings/RecolorParameters.h create mode 100644 entry/src/main/cpp/src/core/control/settings/Settings.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/Settings.h create mode 100644 entry/src/main/cpp/src/core/control/settings/SettingsEnums.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/SettingsEnums.h create mode 100644 entry/src/main/cpp/src/core/control/settings/ViewModes.cpp create mode 100644 entry/src/main/cpp/src/core/control/settings/ViewModes.h create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/CircleRecognizer.cpp create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/CircleRecognizer.h create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/Inertia.cpp create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/Inertia.h create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/RecoSegment.cpp create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/RecoSegment.h create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/ShapeRecognizer.cpp create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/ShapeRecognizer.h create mode 100644 entry/src/main/cpp/src/core/control/shaperecognizer/ShapeRecognizerConfig.h create mode 100644 entry/src/main/cpp/src/core/control/tools/ArrowHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/ArrowHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/BaseShapeHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/BaseShapeHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/CoordinateSystemHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/CoordinateSystemHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/CursorSelectionType.h create mode 100644 entry/src/main/cpp/src/core/control/tools/EditSelection.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/EditSelection.h create mode 100644 entry/src/main/cpp/src/core/control/tools/EditSelectionContents.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/EditSelectionContents.h create mode 100644 entry/src/main/cpp/src/core/control/tools/EllipseHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/EllipseHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/EraseHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/EraseHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/ImageHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/ImageHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/ImageSizeSelection.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/ImageSizeSelection.h create mode 100644 entry/src/main/cpp/src/core/control/tools/InputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/InputHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/LaserPointerHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/LaserPointerHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/PdfElemSelection.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/PdfElemSelection.h create mode 100644 entry/src/main/cpp/src/core/control/tools/RectangleHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/RectangleHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/RulerHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/RulerHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/Selector.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/Selector.h create mode 100644 entry/src/main/cpp/src/core/control/tools/SnapToGridInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/SnapToGridInputHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/SplineHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/SplineHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/StrokeHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/StrokeHandler.h create mode 100644 entry/src/main/cpp/src/core/control/tools/StrokeStabilizer.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/StrokeStabilizer.h create mode 100644 entry/src/main/cpp/src/core/control/tools/StrokeStabilizerEnum.h create mode 100644 entry/src/main/cpp/src/core/control/tools/TextEditor.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/TextEditor.h create mode 100644 entry/src/main/cpp/src/core/control/tools/TextEditorKeyBindings.h create mode 100644 entry/src/main/cpp/src/core/control/tools/VerticalToolHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/tools/VerticalToolHandler.h create mode 100644 entry/src/main/cpp/src/core/control/xml/Attribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/Attribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/DoubleArrayAttribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/DoubleArrayAttribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/DoubleAttribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/DoubleAttribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/IntAttribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/IntAttribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/SizeTAttribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/SizeTAttribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/TextAttribute.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/TextAttribute.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlAudioNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlAudioNode.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlImageNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlImageNode.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlNode.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlPointNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlPointNode.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlTexNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlTexNode.h create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlTextNode.cpp create mode 100644 entry/src/main/cpp/src/core/control/xml/XmlTextNode.h create mode 100644 entry/src/main/cpp/src/core/control/xojfile/LoadHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/xojfile/LoadHandler.h create mode 100644 entry/src/main/cpp/src/core/control/xojfile/LoadHandlerHelper.cpp create mode 100644 entry/src/main/cpp/src/core/control/xojfile/LoadHandlerHelper.h create mode 100644 entry/src/main/cpp/src/core/control/xojfile/SaveHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/xojfile/SaveHandler.h create mode 100644 entry/src/main/cpp/src/core/control/xojfile/XojExportHandler.cpp create mode 100644 entry/src/main/cpp/src/core/control/xojfile/XojExportHandler.h create mode 100644 entry/src/main/cpp/src/core/control/zoom/ZoomControl.cpp create mode 100644 entry/src/main/cpp/src/core/control/zoom/ZoomControl.h create mode 100644 entry/src/main/cpp/src/core/control/zoom/ZoomListener.cpp create mode 100644 entry/src/main/cpp/src/core/control/zoom/ZoomListener.h create mode 100644 entry/src/main/cpp/src/core/enums/Action.enum.h create mode 100644 entry/src/main/cpp/src/core/enums/generateConvert.php create mode 100644 entry/src/main/cpp/src/core/enums/generated/Action.NameMap.generated.h create mode 100644 entry/src/main/cpp/src/core/gui/Builder.cpp create mode 100644 entry/src/main/cpp/src/core/gui/Builder.h create mode 100644 entry/src/main/cpp/src/core/gui/CreatePreviewImage.cpp create mode 100644 entry/src/main/cpp/src/core/gui/CreatePreviewImage.h create mode 100644 entry/src/main/cpp/src/core/gui/FloatingToolbox.cpp create mode 100644 entry/src/main/cpp/src/core/gui/FloatingToolbox.h create mode 100644 entry/src/main/cpp/src/core/gui/GladeGui.cpp create mode 100644 entry/src/main/cpp/src/core/gui/GladeGui.h create mode 100644 entry/src/main/cpp/src/core/gui/GladeSearchpath.cpp create mode 100644 entry/src/main/cpp/src/core/gui/GladeSearchpath.h create mode 100644 entry/src/main/cpp/src/core/gui/IconNameHelper.cpp create mode 100644 entry/src/main/cpp/src/core/gui/IconNameHelper.h create mode 100644 entry/src/main/cpp/src/core/gui/Layout.cpp create mode 100644 entry/src/main/cpp/src/core/gui/Layout.h create mode 100644 entry/src/main/cpp/src/core/gui/LayoutMapper.cpp create mode 100644 entry/src/main/cpp/src/core/gui/LayoutMapper.h create mode 100644 entry/src/main/cpp/src/core/gui/LegacyRedrawable.cpp create mode 100644 entry/src/main/cpp/src/core/gui/LegacyRedrawable.h create mode 100644 entry/src/main/cpp/src/core/gui/MainWindow.cpp create mode 100644 entry/src/main/cpp/src/core/gui/MainWindow.h create mode 100644 entry/src/main/cpp/src/core/gui/PagePreviewDecoration.cpp create mode 100644 entry/src/main/cpp/src/core/gui/PagePreviewDecoration.h create mode 100644 entry/src/main/cpp/src/core/gui/PageView.cpp create mode 100644 entry/src/main/cpp/src/core/gui/PageView.h create mode 100644 entry/src/main/cpp/src/core/gui/PageViewFindObjectHelper.h create mode 100644 entry/src/main/cpp/src/core/gui/PaperFormatUtils.cpp create mode 100644 entry/src/main/cpp/src/core/gui/PaperFormatUtils.h create mode 100644 entry/src/main/cpp/src/core/gui/PdfFloatingToolbox.cpp create mode 100644 entry/src/main/cpp/src/core/gui/PdfFloatingToolbox.h create mode 100644 entry/src/main/cpp/src/core/gui/PopoverFactory.h create mode 100644 entry/src/main/cpp/src/core/gui/RepaintHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/RepaintHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/SearchBar.cpp create mode 100644 entry/src/main/cpp/src/core/gui/SearchBar.h create mode 100644 entry/src/main/cpp/src/core/gui/Shadow.cpp create mode 100644 entry/src/main/cpp/src/core/gui/Shadow.h create mode 100644 entry/src/main/cpp/src/core/gui/ShadowCode.c create mode 100644 entry/src/main/cpp/src/core/gui/ToolbarDefinitions.h create mode 100644 entry/src/main/cpp/src/core/gui/ToolitemDragDrop.cpp create mode 100644 entry/src/main/cpp/src/core/gui/ToolitemDragDrop.h create mode 100644 entry/src/main/cpp/src/core/gui/XournalView.cpp create mode 100644 entry/src/main/cpp/src/core/gui/XournalView.h create mode 100644 entry/src/main/cpp/src/core/gui/XournalppCursor.cpp create mode 100644 entry/src/main/cpp/src/core/gui/XournalppCursor.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/AboutDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/AboutDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/AbstractLatexDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/AbstractLatexDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ButtonConfigGui.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ButtonConfigGui.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceClassConfigGui.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceClassConfigGui.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceListDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceListDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceTestingArea.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/DeviceTestingArea.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ExportDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ExportDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ExtEdLatexDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ExtEdLatexDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/FileChooserFiltersHelper.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/FileChooserFiltersHelper.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/FormatDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/FormatDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/GotoDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/GotoDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/IntEdLatexDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/IntEdLatexDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/LanguageConfigGui.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/LanguageConfigGui.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/LatexSettingsPanel.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/LatexSettingsPanel.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PageTemplateDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PageTemplateDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PluginDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PluginDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PluginDialogEntry.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/PluginDialogEntry.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/RenameLayerDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/RenameLayerDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SelectBackgroundColorDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SelectBackgroundColorDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SelectOpacityDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SelectOpacityDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SettingsDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SettingsDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SettingsDialogPaletteTab.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/SettingsDialogPaletteTab.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ToolbarManageDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/ToolbarManageDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/XojOpenDlg.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/XojOpenDlg.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/XojSaveDlg.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/XojSaveDlg.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/BackgroundSelectDialogBase.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/BackgroundSelectDialogBase.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/BaseElementView.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/BaseElementView.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/ImageElementView.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/ImageElementView.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/ImagesDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/ImagesDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/PdfElementView.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/PdfElementView.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/PdfPagesDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/backgroundSelect/PdfPagesDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolItemDragCurrentData.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolItemDragCurrentData.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarAdapter.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarAdapter.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarCustomizeDialog.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarCustomizeDialog.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarDragDropHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarDragDropHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarDragDropHelper.cpp create mode 100644 entry/src/main/cpp/src/core/gui/dialog/toolbarCustomize/ToolbarDragDropHelper.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/AbstractInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/AbstractInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/CompassInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/CompassInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/DeviceId.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/DeviceId.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/GeometryToolInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/GeometryToolInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/HandRecognition.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/HandRecognition.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputContext.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputContext.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputEvents.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputEvents.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputUtils.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/InputUtils.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/KeyboardInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/KeyboardInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/MouseInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/MouseInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PenInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PenInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PositionInputData.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PositionInputData.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PrintEvent.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/PrintEvent.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/SetsquareInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/SetsquareInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/StylusInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/StylusInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/TouchDrawingInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/TouchDrawingInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/TouchInputHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/TouchInputHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableCustom.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableCustom.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableInterface.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableInterface.h create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableX11.cpp create mode 100644 entry/src/main/cpp/src/core/gui/inputdevices/touchdisable/TouchDisableX11.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/PageTypeSelectionMenuBase.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/PageTypeSelectionMenuBase.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/StaticAssertActionNamespace.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/AbstractSubmenu.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/Menubar.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/Menubar.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/PageTypeSubmenu.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/PageTypeSubmenu.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/PluginsSubmenu.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/PluginsSubmenu.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/RecentDocumentsSubmenu.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/RecentDocumentsSubmenu.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/ToolbarSelectionSubmenu.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/menubar/ToolbarSelectionSubmenu.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/popoverMenus/PageTypeSelectionPopover.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/popoverMenus/PageTypeSelectionPopover.h create mode 100644 entry/src/main/cpp/src/core/gui/menus/popoverMenus/PageTypeSelectionPopoverGridOnly.cpp create mode 100644 entry/src/main/cpp/src/core/gui/menus/popoverMenus/PageTypeSelectionPopoverGridOnly.h create mode 100644 entry/src/main/cpp/src/core/gui/scroll/ScrollHandling.cpp create mode 100644 entry/src/main/cpp/src/core/gui/scroll/ScrollHandling.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/AbstractSidebarPage.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/AbstractSidebarPage.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/Sidebar.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/Sidebar.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/indextree/SidebarIndexPage.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/indextree/SidebarIndexPage.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarLayout.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarLayout.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarPreviewBase.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarPreviewBase.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarPreviewBaseEntry.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/base/SidebarPreviewBaseEntry.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/layer/SidebarPreviewLayerEntry.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/layer/SidebarPreviewLayerEntry.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/layer/SidebarPreviewLayers.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/layer/SidebarPreviewLayers.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/page/SidebarPreviewPageEntry.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/page/SidebarPreviewPageEntry.h create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/page/SidebarPreviewPages.cpp create mode 100644 entry/src/main/cpp/src/core/gui/sidebar/previews/page/SidebarPreviewPages.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/AbstractSliderItem.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/AbstractSliderItem.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/AbstractToolItem.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/AbstractToolItem.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ColorSelectorToolItem.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ColorSelectorToolItem.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ColorToolItem.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ColorToolItem.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ComboToolButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ComboToolButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/DrawingTypeComboToolButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/DrawingTypeComboToolButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/FontButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/FontButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/PluginPlaceholderLabel.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/PluginPlaceholderLabel.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/PluginToolButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/PluginToolButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/SliderItemCreationHelper.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/StylePopoverFactory.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/StylePopoverFactory.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolMenuHandler.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolMenuHandler.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPageLayer.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPageLayer.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPageSpinner.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPageSpinner.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPdfCombocontrol.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolPdfCombocontrol.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolSelectCombocontrol.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolSelectCombocontrol.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolZoomSlider.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/ToolZoomSlider.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/TooltipToolButton.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/TooltipToolButton.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/icon/ColorIcon.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/icon/ColorIcon.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/icon/ToolbarSeparatorImage.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/icon/ToolbarSeparatorImage.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ColorPalette.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ColorPalette.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarData.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarData.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarEntry.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarEntry.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarItem.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarItem.h create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarModel.cpp create mode 100644 entry/src/main/cpp/src/core/gui/toolbarMenubar/model/ToolbarModel.h create mode 100644 entry/src/main/cpp/src/core/gui/widgets/SpinPageAdapter.cpp create mode 100644 entry/src/main/cpp/src/core/gui/widgets/SpinPageAdapter.h create mode 100644 entry/src/main/cpp/src/core/gui/widgets/XournalWidget.cpp create mode 100644 entry/src/main/cpp/src/core/gui/widgets/XournalWidget.h create mode 100644 entry/src/main/cpp/src/core/gui/widgets/ZoomCallib.cpp create mode 100644 entry/src/main/cpp/src/core/gui/widgets/ZoomCallib.h create mode 100644 entry/src/main/cpp/src/core/model/AudioElement.cpp create mode 100644 entry/src/main/cpp/src/core/model/AudioElement.h create mode 100644 entry/src/main/cpp/src/core/model/BackgroundConfig.cpp create mode 100644 entry/src/main/cpp/src/core/model/BackgroundConfig.h create mode 100644 entry/src/main/cpp/src/core/model/BackgroundImage.cpp create mode 100644 entry/src/main/cpp/src/core/model/BackgroundImage.h create mode 100644 entry/src/main/cpp/src/core/model/Compass.cpp create mode 100644 entry/src/main/cpp/src/core/model/Compass.h create mode 100644 entry/src/main/cpp/src/core/model/Document.cpp create mode 100644 entry/src/main/cpp/src/core/model/Document.h create mode 100644 entry/src/main/cpp/src/core/model/DocumentChangeType.h create mode 100644 entry/src/main/cpp/src/core/model/DocumentHandler.cpp create mode 100644 entry/src/main/cpp/src/core/model/DocumentHandler.h create mode 100644 entry/src/main/cpp/src/core/model/DocumentListener.cpp create mode 100644 entry/src/main/cpp/src/core/model/DocumentListener.h create mode 100644 entry/src/main/cpp/src/core/model/Element.cpp create mode 100644 entry/src/main/cpp/src/core/model/Element.h create mode 100644 entry/src/main/cpp/src/core/model/ElementContainer.h create mode 100644 entry/src/main/cpp/src/core/model/ElementInsertionPosition.h create mode 100644 entry/src/main/cpp/src/core/model/Font.cpp create mode 100644 entry/src/main/cpp/src/core/model/Font.h create mode 100644 entry/src/main/cpp/src/core/model/FormatDefinitions.cpp create mode 100644 entry/src/main/cpp/src/core/model/FormatDefinitions.h create mode 100644 entry/src/main/cpp/src/core/model/GeometryTool.cpp create mode 100644 entry/src/main/cpp/src/core/model/GeometryTool.h create mode 100644 entry/src/main/cpp/src/core/model/Image.cpp create mode 100644 entry/src/main/cpp/src/core/model/Image.h create mode 100644 entry/src/main/cpp/src/core/model/Layer.cpp create mode 100644 entry/src/main/cpp/src/core/model/Layer.h create mode 100644 entry/src/main/cpp/src/core/model/LineStyle.cpp create mode 100644 entry/src/main/cpp/src/core/model/LineStyle.h create mode 100644 entry/src/main/cpp/src/core/model/LinkDestination.cpp create mode 100644 entry/src/main/cpp/src/core/model/LinkDestination.h create mode 100644 entry/src/main/cpp/src/core/model/MathVect.cpp create mode 100644 entry/src/main/cpp/src/core/model/MathVect.h create mode 100644 entry/src/main/cpp/src/core/model/OverlayBase.h create mode 100644 entry/src/main/cpp/src/core/model/PageHandler.cpp create mode 100644 entry/src/main/cpp/src/core/model/PageHandler.h create mode 100644 entry/src/main/cpp/src/core/model/PageListener.cpp create mode 100644 entry/src/main/cpp/src/core/model/PageListener.h create mode 100644 entry/src/main/cpp/src/core/model/PageRef.h create mode 100644 entry/src/main/cpp/src/core/model/PageType.cpp create mode 100644 entry/src/main/cpp/src/core/model/PageType.h create mode 100644 entry/src/main/cpp/src/core/model/PaperSize.cpp create mode 100644 entry/src/main/cpp/src/core/model/PaperSize.h create mode 100644 entry/src/main/cpp/src/core/model/PathParameter.h create mode 100644 entry/src/main/cpp/src/core/model/Point.cpp create mode 100644 entry/src/main/cpp/src/core/model/Point.h create mode 100644 entry/src/main/cpp/src/core/model/Setsquare.cpp create mode 100644 entry/src/main/cpp/src/core/model/Setsquare.h create mode 100644 entry/src/main/cpp/src/core/model/Snapping.cpp create mode 100644 entry/src/main/cpp/src/core/model/Snapping.h create mode 100644 entry/src/main/cpp/src/core/model/SplineSegment.cpp create mode 100644 entry/src/main/cpp/src/core/model/SplineSegment.h create mode 100644 entry/src/main/cpp/src/core/model/Stroke.cpp create mode 100644 entry/src/main/cpp/src/core/model/Stroke.h create mode 100644 entry/src/main/cpp/src/core/model/StrokeContour.cpp create mode 100644 entry/src/main/cpp/src/core/model/StrokeContour.h create mode 100644 entry/src/main/cpp/src/core/model/StrokeStyle.cpp create mode 100644 entry/src/main/cpp/src/core/model/StrokeStyle.h create mode 100644 entry/src/main/cpp/src/core/model/TexImage.cpp create mode 100644 entry/src/main/cpp/src/core/model/TexImage.h create mode 100644 entry/src/main/cpp/src/core/model/Text.cpp create mode 100644 entry/src/main/cpp/src/core/model/Text.h create mode 100644 entry/src/main/cpp/src/core/model/XojPage.cpp create mode 100644 entry/src/main/cpp/src/core/model/XojPage.h create mode 100644 entry/src/main/cpp/src/core/model/eraser/ErasableStroke.cpp create mode 100644 entry/src/main/cpp/src/core/model/eraser/ErasableStroke.h create mode 100644 entry/src/main/cpp/src/core/model/eraser/ErasableStrokeOverlapTree.cpp create mode 100644 entry/src/main/cpp/src/core/model/eraser/ErasableStrokeOverlapTree.h create mode 100644 entry/src/main/cpp/src/core/model/eraser/PaddedBox.h create mode 100644 entry/src/main/cpp/src/core/model/eraser/readme.txt create mode 100644 entry/src/main/cpp/src/core/pdf/base/HybridPdfExport.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/HybridPdfExport.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/PdfExportBackend.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/PdfExportBackend.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/QPdfExport.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/QPdfExport.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojCairoPdfExport.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojCairoPdfExport.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfAction.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfAction.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfBookmarkIterator.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfBookmarkIterator.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfDocument.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfDocument.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfDocumentInterface.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfDocumentInterface.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfExport.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfExport.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfExportFactory.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfExportFactory.h create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfPage.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/base/XojPdfPage.h create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibAction.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibAction.h create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibDocument.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibDocument.h create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibPage.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibPage.h create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibPageBookmarkIterator.cpp create mode 100644 entry/src/main/cpp/src/core/pdf/popplerapi/PopplerGlibPageBookmarkIterator.h create mode 100644 entry/src/main/cpp/src/core/plugin/ActionBackwardCompatibilityLayer.cpp create mode 100644 entry/src/main/cpp/src/core/plugin/ActionBackwardCompatibilityLayer.h create mode 100644 entry/src/main/cpp/src/core/plugin/Plugin.cpp create mode 100644 entry/src/main/cpp/src/core/plugin/Plugin.h create mode 100644 entry/src/main/cpp/src/core/plugin/PluginController.cpp create mode 100644 entry/src/main/cpp/src/core/plugin/PluginController.h create mode 100644 entry/src/main/cpp/src/core/plugin/luapi_application.h create mode 100644 entry/src/main/cpp/src/core/undo/AddUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/AddUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/ArrangeUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/ArrangeUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/ColorUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/ColorUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/DeleteUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/DeleteUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/EmergencySaveRestore.cpp create mode 100644 entry/src/main/cpp/src/core/undo/EmergencySaveRestore.h create mode 100644 entry/src/main/cpp/src/core/undo/EraseUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/EraseUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/FillUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/FillUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/FontUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/FontUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/GroupUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/GroupUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/InsertDeletePageUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/InsertDeletePageUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/InsertLayerUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/InsertLayerUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/InsertUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/InsertUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/LayerRenameUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/LayerRenameUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/LineStyleUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/LineStyleUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/MergeLayerDownUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/MergeLayerDownUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/MissingPdfUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/MissingPdfUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/MoveLayerUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/MoveLayerUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/MoveSelectionToLayerUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/MoveSelectionToLayerUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/MoveUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/MoveUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/PageBackgroundChangedUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/PageBackgroundChangedUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/PageLayerPosEntry.h create mode 100644 entry/src/main/cpp/src/core/undo/PageSizeChangeUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/PageSizeChangeUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/RecognizerUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/RecognizerUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/RemoveLayerUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/RemoveLayerUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/RotateUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/RotateUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/ScaleUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/ScaleUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/SizeUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/SizeUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/SwapUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/SwapUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/TextBoxUndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/TextBoxUndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/UndoAction.cpp create mode 100644 entry/src/main/cpp/src/core/undo/UndoAction.h create mode 100644 entry/src/main/cpp/src/core/undo/UndoRedoHandler.cpp create mode 100644 entry/src/main/cpp/src/core/undo/UndoRedoHandler.h create mode 100644 entry/src/main/cpp/src/core/view/CompassView.cpp create mode 100644 entry/src/main/cpp/src/core/view/CompassView.h create mode 100644 entry/src/main/cpp/src/core/view/DebugShowRepaintBounds.h create mode 100644 entry/src/main/cpp/src/core/view/DocumentView.cpp create mode 100644 entry/src/main/cpp/src/core/view/DocumentView.h create mode 100644 entry/src/main/cpp/src/core/view/ElementContainerView.cpp create mode 100644 entry/src/main/cpp/src/core/view/ElementContainerView.h create mode 100644 entry/src/main/cpp/src/core/view/ElementView.cpp create mode 100644 entry/src/main/cpp/src/core/view/ErasableStrokeView.cpp create mode 100644 entry/src/main/cpp/src/core/view/ErasableStrokeView.h create mode 100644 entry/src/main/cpp/src/core/view/GeometryToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/GeometryToolView.h create mode 100644 entry/src/main/cpp/src/core/view/ImageView.cpp create mode 100644 entry/src/main/cpp/src/core/view/ImageView.h create mode 100644 entry/src/main/cpp/src/core/view/LayerView.cpp create mode 100644 entry/src/main/cpp/src/core/view/LayerView.h create mode 100644 entry/src/main/cpp/src/core/view/Mask.cpp create mode 100644 entry/src/main/cpp/src/core/view/Mask.h create mode 100644 entry/src/main/cpp/src/core/view/Repaintable.h create mode 100644 entry/src/main/cpp/src/core/view/SetsquareView.cpp create mode 100644 entry/src/main/cpp/src/core/view/SetsquareView.h create mode 100644 entry/src/main/cpp/src/core/view/StrokeView.cpp create mode 100644 entry/src/main/cpp/src/core/view/StrokeView.h create mode 100644 entry/src/main/cpp/src/core/view/StrokeViewHelper.cpp create mode 100644 entry/src/main/cpp/src/core/view/StrokeViewHelper.h create mode 100644 entry/src/main/cpp/src/core/view/TexImageView.cpp create mode 100644 entry/src/main/cpp/src/core/view/TexImageView.h create mode 100644 entry/src/main/cpp/src/core/view/TextView.cpp create mode 100644 entry/src/main/cpp/src/core/view/TextView.h create mode 100644 entry/src/main/cpp/src/core/view/View.h create mode 100644 entry/src/main/cpp/src/core/view/background/BackgroundFlags.h create mode 100644 entry/src/main/cpp/src/core/view/background/BackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/BackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/BaseIsometricBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/BaseIsometricBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/DottedBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/DottedBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/GraphBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/GraphBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/ImageBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/ImageBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/IsoDottedBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/IsoDottedBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/IsoGraphBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/IsoGraphBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/LinedBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/LinedBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/OneColorBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/OneColorBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/PdfBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/PdfBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/PlainBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/PlainBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/RuledBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/RuledBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/StavesBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/StavesBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/background/TransparentCheckerboardBackgroundView.cpp create mode 100644 entry/src/main/cpp/src/core/view/background/TransparentCheckerboardBackgroundView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/BaseShapeOrSplineToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/BaseShapeOrSplineToolView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/BaseStrokeToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/BaseStrokeToolView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/ImageSizeSelectionView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/ImageSizeSelectionView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/LaserPointerView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/LaserPointerView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/OverlayView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/PdfElementSelectionView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/PdfElementSelectionView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/SearchResultView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/SearchResultView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/SelectorView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/SelectorView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/ShapeToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/ShapeToolView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/SplineToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/SplineToolView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolFilledHighlighterView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolFilledHighlighterView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolFilledView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolFilledView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/StrokeToolView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/TextEditionView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/TextEditionView.h create mode 100644 entry/src/main/cpp/src/core/view/overlays/VerticalToolView.cpp create mode 100644 entry/src/main/cpp/src/core/view/overlays/VerticalToolView.h create mode 100644 entry/src/main/cpp/src/exe/Xournalpp-wrapper.cpp create mode 100644 entry/src/main/cpp/src/exe/Xournalpp.cpp create mode 100644 entry/src/main/cpp/src/exe/osx/setup-env.cpp create mode 100644 entry/src/main/cpp/src/exe/osx/setup-env.h create mode 100644 entry/src/main/cpp/src/exe/win32/console.cpp create mode 100644 entry/src/main/cpp/src/exe/win32/console.h create mode 100644 entry/src/main/cpp/src/exe/win32/gui_main.cpp create mode 100644 entry/src/main/cpp/src/exe/win32/xpp.rc.in create mode 100644 entry/src/main/cpp/src/exe/xournalpp-wrapper.gresource.xml create mode 100644 entry/src/main/cpp/src/util/Assert.cpp create mode 100644 entry/src/main/cpp/src/util/CMakeLists.txt create mode 100644 entry/src/main/cpp/src/util/Color.cpp create mode 100644 entry/src/main/cpp/src/util/ElementRange.cpp create mode 100644 entry/src/main/cpp/src/util/GtkUtil.cpp create mode 100644 entry/src/main/cpp/src/util/GzUtil.cpp create mode 100644 entry/src/main/cpp/src/util/NamedColor.cpp create mode 100644 entry/src/main/cpp/src/util/OutputStream.cpp create mode 100644 entry/src/main/cpp/src/util/PathUtil.cpp create mode 100644 entry/src/main/cpp/src/util/PlaceholderString.cpp create mode 100644 entry/src/main/cpp/src/util/Range.cpp create mode 100644 entry/src/main/cpp/src/util/Recolor.cpp create mode 100644 entry/src/main/cpp/src/util/SaveNameUtils.cpp create mode 100644 entry/src/main/cpp/src/util/Stacktrace.cpp create mode 100644 entry/src/main/cpp/src/util/StringUtils.cpp create mode 100644 entry/src/main/cpp/src/util/Util.cpp create mode 100644 entry/src/main/cpp/src/util/VersionInfo.cpp create mode 100644 entry/src/main/cpp/src/util/XojMsgBox.cpp create mode 100644 entry/src/main/cpp/src/util/XojPreviewExtractor.cpp create mode 100644 entry/src/main/cpp/src/util/gdk4_helper.cpp create mode 100644 entry/src/main/cpp/src/util/gtk4_helper.cpp create mode 100644 entry/src/main/cpp/src/util/include/filesystem.h create mode 100644 entry/src/main/cpp/src/util/include/util/Assert.h create mode 100644 entry/src/main/cpp/src/util/include/util/BasePointerIterator.h create mode 100644 entry/src/main/cpp/src/util/include/util/CircularBuffer.h create mode 100644 entry/src/main/cpp/src/util/include/util/Color.h create mode 100644 entry/src/main/cpp/src/util/include/util/DispatchPool.h create mode 100644 entry/src/main/cpp/src/util/include/util/ElementRange.h create mode 100644 entry/src/main/cpp/src/util/include/util/EnumIndexedArray.h create mode 100644 entry/src/main/cpp/src/util/include/util/GListView.h create mode 100644 entry/src/main/cpp/src/util/include/util/GVariantTemplate.h create mode 100644 entry/src/main/cpp/src/util/include/util/GtkUtil.h create mode 100644 entry/src/main/cpp/src/util/include/util/GzUtil.h create mode 100644 entry/src/main/cpp/src/util/include/util/Interval.h create mode 100644 entry/src/main/cpp/src/util/include/util/LoopUtil.h create mode 100644 entry/src/main/cpp/src/util/include/util/NamedColor.h create mode 100644 entry/src/main/cpp/src/util/include/util/OutputStream.h create mode 100644 entry/src/main/cpp/src/util/include/util/PairView.h create mode 100644 entry/src/main/cpp/src/util/include/util/PathUtil.h create mode 100644 entry/src/main/cpp/src/util/include/util/PlaceholderString.h create mode 100644 entry/src/main/cpp/src/util/include/util/Point.h create mode 100644 entry/src/main/cpp/src/util/include/util/PointerContainerView.h create mode 100644 entry/src/main/cpp/src/util/include/util/PopupWindowWrapper.h create mode 100644 entry/src/main/cpp/src/util/include/util/Range.h create mode 100644 entry/src/main/cpp/src/util/include/util/Recolor.h create mode 100644 entry/src/main/cpp/src/util/include/util/Rectangle.h create mode 100644 entry/src/main/cpp/src/util/include/util/SaveNameUtils.h create mode 100644 entry/src/main/cpp/src/util/include/util/SmallVector.h create mode 100644 entry/src/main/cpp/src/util/include/util/Stacktrace.h create mode 100644 entry/src/main/cpp/src/util/include/util/StringUtils.h create mode 100644 entry/src/main/cpp/src/util/include/util/TinyVector.h create mode 100644 entry/src/main/cpp/src/util/include/util/TypeIfThenElse.h create mode 100644 entry/src/main/cpp/src/util/include/util/UnionOfIntervals.h create mode 100644 entry/src/main/cpp/src/util/include/util/Util.h create mode 100644 entry/src/main/cpp/src/util/include/util/VersionInfo.h create mode 100644 entry/src/main/cpp/src/util/include/util/ViewIteratorBase.h create mode 100644 entry/src/main/cpp/src/util/include/util/XojMsgBox.h create mode 100644 entry/src/main/cpp/src/util/include/util/XojPreviewExtractor.h create mode 100644 entry/src/main/cpp/src/util/include/util/gdk4_helper.h create mode 100644 entry/src/main/cpp/src/util/include/util/glib_casts.h create mode 100644 entry/src/main/cpp/src/util/include/util/gtk4_helper.h create mode 100644 entry/src/main/cpp/src/util/include/util/i18n.h create mode 100644 entry/src/main/cpp/src/util/include/util/logger/Logger.h create mode 100644 entry/src/main/cpp/src/util/include/util/move_only_function.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/CLibrariesSPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/CStringWrapper.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/CairoWrappers.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GLibGuards.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GObjectSPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GSourceURef.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GVariantSPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GtkPaperSizeUPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/GtkWindowUPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/IdentityFunction.h create mode 100644 entry/src/main/cpp/src/util/include/util/raii/PangoSPtr.h create mode 100644 entry/src/main/cpp/src/util/include/util/safe_casts.h create mode 100644 entry/src/main/cpp/src/util/include/util/serdesstream.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/BinObjectEncoding.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/HexObjectEncoding.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/InputStreamException.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/ObjectEncoding.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/ObjectInputStream.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/ObjectOutputStream.h create mode 100644 entry/src/main/cpp/src/util/include/util/serializing/Serializable.h create mode 100644 entry/src/main/cpp/src/util/include/util/utf8_view.h create mode 100644 entry/src/main/cpp/src/util/logger/Logger.cpp create mode 100644 entry/src/main/cpp/src/util/raii/CStringWrapper.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/BinObjectEncoding.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/HexObjectEncoding.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/InputStreamException.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/ObjectEncoding.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/ObjectInputStream.cpp create mode 100644 entry/src/main/cpp/src/util/serializing/ObjectOutputStream.cpp create mode 100644 entry/src/main/cpp/src/xoj-preview-extractor/CMakeLists.txt create mode 100755 entry/src/main/cpp/src/xoj-preview-extractor/desktop-install.sh create mode 100644 entry/src/main/cpp/src/xoj-preview-extractor/xournalpp-thumbnailer-xoj.schemas create mode 100644 entry/src/main/cpp/src/xoj-preview-extractor/xournalpp-thumbnailer.cpp diff --git a/entry/src/main/cpp/src/CMakeLists.txt b/entry/src/main/cpp/src/CMakeLists.txt new file mode 100644 index 0000000..6f1fdac --- /dev/null +++ b/entry/src/main/cpp/src/CMakeLists.txt @@ -0,0 +1,174 @@ +cmake_minimum_required(VERSION 3.13) +cmake_policy(VERSION 3.13) + +# Todo: add to target by generator expression + + +############################## +# xoj default option target +############################## + +add_library(defaults INTERFACE) +target_include_directories(defaults INTERFACE ${PROJECT_BINARY_DIR}/src) +add_library(xoj::defaults ALIAS defaults) +if (MSVC) # clang-cl.exe, cl.exe + target_compile_definitions(defaults INTERFACE NOMINMAX WIN32_LEAN_AND_MEAN) + target_compile_options(defaults INTERFACE -utf-8) +else(MSVC) # gnu clang with gnu driver, + target_compile_options(defaults INTERFACE -Wall -Wreturn-type -Wuninitialized -Wunused-value -Wunused-variable -Wconversion) +endif () +# Todo (support-v3.10): guard this variable with cmake_dependent_option for CMake 3.10, and NOT(GCC) +# then downgrade cmake_minimum_required to CMake 3.10 +if (DEV_ENABLE_GCOV) + target_compile_options(defaults INTERFACE --coverage) + target_link_options(defaults INTERFACE --coverage) #cmake v3.13 +endif (DEV_ENABLE_GCOV) + +############################## +# util +############################## + +add_subdirectory(util) + +############################## +# xournalpp-core +############################## + +add_subdirectory(core) + +############################## +# xournalpp-wrapper: catches crashes +############################## + +# The crash dialog's ui file is stored as gresource to make sure the wrapper is standalone +find_program(GLIB_COMPILE_RESOURCES NAMES glib-compile-resources REQUIRED) + +set(XOURNAL_WRAPPER_GRESOURCE_C xournalpp-wrapper.gresource.c) +set(XOURNAL_WRAPPER_GRESOURCE_XML exe/xournalpp-wrapper.gresource.xml) + +add_custom_command( + OUTPUT ${XOURNAL_WRAPPER_GRESOURCE_C} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GLIB_COMPILE_RESOURCES} + ARGS + --generate-source + --sourcedir=${CMAKE_SOURCE_DIR} + --target=${CMAKE_CURRENT_BINARY_DIR}/${XOURNAL_WRAPPER_GRESOURCE_C} + ${XOURNAL_WRAPPER_GRESOURCE_XML} + VERBATIM + MAIN_DEPENDENCY ${XOURNAL_WRAPPER_GRESOURCE_XML} + DEPENDS + ${CMAKE_SOURCE_DIR}/ui/crashDialog.glade +) + +add_custom_target( + xournalpp-wrapper-resource + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${XOURNAL_WRAPPER_GRESOURCE_C} +) + +add_executable(xournalpp-wrapper exe/Xournalpp-wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/${XOURNAL_WRAPPER_GRESOURCE_C}) +set_source_files_properties( + ${CMAKE_CURRENT_BINARY_DIR}/${XOURNAL_WRAPPER_GRESOURCE_C} + PROPERTIES GENERATED TRUE +) + +add_dependencies(xournalpp-wrapper xournalpp-wrapper-resource) +target_link_libraries(xournalpp-wrapper + PUBLIC xoj::defaults xoj::util xoj::external_modules) +set_target_properties(xournalpp-wrapper PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" + MACOSX_BUNDLE $,TRUE,${CMAKE_MACOSX_BUNDLE}> + WIN32_EXECUTABLE $,TRUE,${CMAKE_WIN32_EXECUTABLE}> + ) +target_include_directories(xournalpp-wrapper PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") + + +############################## +# xournalpp main program +############################## + +add_executable(xournalpp exe/Xournalpp.cpp) +target_link_libraries(xournalpp PRIVATE xoj::core xoj::util) +set_target_properties(xournalpp PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" + MACOSX_BUNDLE $,TRUE,${CMAKE_MACOSX_BUNDLE}> + WIN32_EXECUTABLE $,TRUE,${CMAKE_WIN32_EXECUTABLE}> + ) +if (WIN32) + # Make sure Windows executable has all symbols + target_link_options(xournalpp PRIVATE "-Wl,--export-all-symbols") +endif() + +############################### +# Generate icons +# Todo(fabian): move this into an own windows specific cmake script +# e.g. exe/win32/CMakeLists.txt +############################### + +############################### +# Add MacOS specific files +# Todo(Roland): consider moving this into an own MacOS specific cmake script +# e.g. exe/macos/CMakeLists.txt +############################## + +if (APPLE) + # MacOS specific source files + file (GLOB xournalpp-sources-osx exe/osx/*.cpp exe/osx/*.h) + target_sources(xournalpp PRIVATE ${xournalpp-sources-osx}) + target_include_directories(xournalpp PRIVATE exe) +endif() + +if (WIN32) + # generate .ico from .png + find_package(ImageMagick REQUIRED COMPONENTS magick) + set (ICON_SIZES 16 32 48 256) + foreach (SIZE IN LISTS ICON_SIZES) + set (RESIZED_ICON ${PROJECT_BINARY_DIR}/xournalpp-icon-${SIZE}.png) + add_custom_command ( + OUTPUT ${RESIZED_ICON} + COMMAND rsvg-convert ${PROJECT_SOURCE_DIR}/ui/pixmaps/com.github.xournalpp.xournalpp.svg --width=${SIZE} --height=${SIZE} -o ${RESIZED_ICON} + ) + set (ICON_DEPS ${ICON_DEPS} ${RESIZED_ICON}) + unset (RESIZED_ICON) + endforeach() + add_custom_command ( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/win32/xournalpp.ico + COMMAND magick convert ${ICON_DEPS} ${CMAKE_CURRENT_BINARY_DIR}/win32/xournalpp.ico + DEPENDS ${ICON_DEPS} + ) + unset(ICON_SIZES) + unset(ICON_DEPS) + add_custom_target(xournalpp_icon DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/win32/xournalpp.ico) + add_dependencies(xournalpp xournalpp_icon) + add_dependencies(xournalpp-wrapper xournalpp_icon) + + # windows specific source files + target_sources(xournalpp PRIVATE exe/win32/console.cpp) + target_sources(xournalpp-wrapper PRIVATE exe/win32/console.cpp) + if (MSVC) + target_sources(xournalpp PRIVATE exe/win32/gui_main.cpp) + target_sources(xournalpp-wrapper PRIVATE exe/win32/gui_main.cpp) + endif() + + # windows specific appinfo + if(NOT MSVC) + set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") + set(CMAKE_RC_COMPILER_INIT windres) + endif() + enable_language(RC) + + configure_file(exe/win32/xpp.rc.in ${CMAKE_CURRENT_BINARY_DIR}/win32/xpp.rc) + target_sources(xournalpp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/win32/xpp.rc) + target_sources(xournalpp-wrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/win32/xpp.rc) +endif () +# Until here +################################ + +install(TARGETS xournalpp-wrapper + RUNTIME DESTINATION bin + COMPONENT xournalpp-wrapper + ) +install(TARGETS xournalpp + RUNTIME DESTINATION bin + COMPONENT xournalpp + ) + +add_subdirectory(xoj-preview-extractor) diff --git a/entry/src/main/cpp/src/config-debug.h.in b/entry/src/main/cpp/src/config-debug.h.in new file mode 100644 index 0000000..d4cc3db --- /dev/null +++ b/entry/src/main/cpp/src/config-debug.h.in @@ -0,0 +1,69 @@ +/* + * Xournal++ + * + * Static debugging definitions for Xournal++ + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +/** + * Input debugging, e.g. eraser events etc. + */ +#cmakedefine DEBUG_INPUT +#cmakedefine DEBUG_INPUT_GDK_PRINT_EVENTS +#cmakedefine DEBUG_INPUT_PRINT_ALL_MOTION_EVENTS + +/** + * Shape recognizer debug: output score etc. + */ +#cmakedefine DEBUG_RECOGNIZER + +/** + * Scheduler debug: show jobs etc. + */ +#cmakedefine DEBUG_SCHEDULER + +/** + * Draw a surrounding border to all elements + */ +#cmakedefine DEBUG_SHOW_ELEMENT_BOUNDS + +/** + * Draw a border around all repaint rects + */ +#cmakedefine DEBUG_SHOW_REPAINT_BOUNDS + +/** + * Draw a border around all painted rects + */ +#cmakedefine DEBUG_SHOW_PAINT_BOUNDS + +/** + * Draw the mask in StrokeView + */ +#cmakedefine DEBUG_MASKS + +/** + * Print info on draw requests and calls + */ +#cmakedefine DEBUG_DRAW_WIDGET + +/** + * Show repainted areas when erasing an highlighter stroke + */ +#cmakedefine DEBUG_ERASABLE_STROKE_BOXES + +/** + * Show eraser logging info when we get an inconsistent result + */ +#cmakedefine ENABLE_ERASER_DEBUG + +/** + * Enable some debugging output around the action database + */ +#cmakedefine DEBUG_ACTION_DB diff --git a/entry/src/main/cpp/src/config-dev.h.in b/entry/src/main/cpp/src/config-dev.h.in new file mode 100644 index 0000000..a715296 --- /dev/null +++ b/entry/src/main/cpp/src/config-dev.h.in @@ -0,0 +1,50 @@ +/* + * Xournal++ + * + * Static definitions for Xournal++ + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +/** + * Toolbar config file name + */ +#define TOOLBAR_CONFIG "@DEV_TOOLBAR_CONFIG@" + +/** + * Settings file name + */ +#define SETTINGS_XML_FILE "@DEV_SETTINGS_XML_FILE@" + +/** + * Gimp Palette File + */ +#define DEFAULT_PALETTE_FILE "@DEV_DEFAULT_PALETTE_FILE@" + +/** + * Print config file name + */ +#define PRINT_CONFIG_FILE "@DEV_PRINT_CONFIG_FILE@" + +/** + * Metadata file name + */ +#define METADATA_FILE "@DEV_METADATA_FILE@" + +/** + * Directory where errorlogfiles will be placed + */ +#define ERRORLOG_DIR "@DEV_ERRORLOG_DIR@" + + +/* --- Logging --- */ + +/** + * Compile with call log + */ +#cmakedefine DEV_CALL_LOG diff --git a/entry/src/main/cpp/src/config-features.h.in b/entry/src/main/cpp/src/config-features.h.in new file mode 100644 index 0000000..62f124d --- /dev/null +++ b/entry/src/main/cpp/src/config-features.h.in @@ -0,0 +1,30 @@ +/* + * Xournal++ + * + * Additional features of Xournal++ + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +/* --- Stable features --- */ + +#cmakedefine ENABLE_PLUGINS + +#cmakedefine ENABLE_GTK_SOURCEVIEW + +#cmakedefine ENABLE_AUDIO + +/* --- Testing features --- */ + +#cmakedefine ENABLE_QPDF + +#cmakedefine ENABLE_CPPTRACE + +// Example: #cmakedefine NAME_FROM_Cmake_list +// in CMakeFile.txt: +// option (NAME_FROM_Cmake_list "Description" OFF) diff --git a/entry/src/main/cpp/src/config-git.h.in b/entry/src/main/cpp/src/config-git.h.in new file mode 100644 index 0000000..99a9a52 --- /dev/null +++ b/entry/src/main/cpp/src/config-git.h.in @@ -0,0 +1,40 @@ +/* + * Xournal++ + * + * Current git repo info + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + + +/* --- Current git repo info --- */ + +/** + * Origin URL of current repository + */ +constexpr auto GIT_ORIGIN_URL = "@GIT_ORIGIN_URL@"; + +/** + * Git origin repo owner + */ +constexpr auto GIT_ORIGIN_OWNER = "@GIT_ORIGIN_OWNER@"; + +/** + * Git origin repo name + */ +constexpr auto GIT_ORIGIN_REPO = "@GIT_ORIGIN_REPO@"; + +/** + * Current branch of git repo + */ +constexpr auto GIT_BRANCH = "@GIT_BRANCH@"; + +/** + * Current git commit id (with "-dirty" appended if dirty source tree) + */ +constexpr auto GIT_COMMIT_ID = "@RELEASE_IDENTIFIER@"; diff --git a/entry/src/main/cpp/src/config-paths.h.in b/entry/src/main/cpp/src/config-paths.h.in new file mode 100644 index 0000000..b941e7f --- /dev/null +++ b/entry/src/main/cpp/src/config-paths.h.in @@ -0,0 +1,22 @@ +/* + * Xournal++ + * + * Paths to resources + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +/** + * Path to current source directory + */ +#define PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@" + +/** + * Path to install directory (for integration test) + */ +#define PROJECT_INSTALL_DIR "@PROJECT_INSTALL_DIR@" diff --git a/entry/src/main/cpp/src/config.h.in b/entry/src/main/cpp/src/config.h.in new file mode 100644 index 0000000..31b7bde --- /dev/null +++ b/entry/src/main/cpp/src/config.h.in @@ -0,0 +1,57 @@ +/* + * Xournal++ + * + * Static project constants + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +/* --- Project info --- */ + +/** + * Full name of this project and package + */ +constexpr auto PROJECT_NAME = "@PROJECT_NAME@"; + +/** + * Project version number + */ +constexpr auto PROJECT_VERSION = "@PROJECT_VERSION@"; + +/** + * Address where crash reports for this project should be sent + */ +constexpr auto PROJECT_CRASHREPORT = "@PROJECT_CRASHREPORT@"; + +/** + * Full name and version of this project + */ +constexpr auto PROJECT_STRING = "@PROJECT_NAME@ @PROJECT_VERSION@"; + +/** + * Project URL + */ +constexpr auto PROJECT_HOMEPAGE_URL = "@PROJECT_HOMEPAGE_URL@"; + +/** + * File format version + */ +constexpr int FILE_FORMAT_VERSION = @DEV_FILE_FORMAT_VERSION@; + + +/* --- I18N --- */ + +/** + * Enable i18n + */ +#cmakedefine ENABLE_NLS + +/** + * Gettext package + */ +constexpr auto GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@"; diff --git a/entry/src/main/cpp/src/core/CMakeLists.txt b/entry/src/main/cpp/src/core/CMakeLists.txt new file mode 100644 index 0000000..ee22df8 --- /dev/null +++ b/entry/src/main/cpp/src/core/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.12) +cmake_policy(VERSION 3.12) + +file(GLOB_RECURSE xournalpp_sources *.cpp *.h) + +if(NOT ENABLE_AUDIO) + list(FILTER xournalpp_sources EXCLUDE REGEX "${PROJECT_SOURCE_DIR}/src/core/audio/.*" ) + list(FILTER xournalpp_sources EXCLUDE REGEX "${PROJECT_SOURCE_DIR}/src/core/control/AudioController.cpp" ) +endif() +set(xournalpp_include_dirs "${CMAKE_CURRENT_SOURCE_DIR}") + +add_library(xournalpp-core STATIC ${xournalpp_sources}) +target_compile_features(xournalpp-core PUBLIC ${PROJECT_CXX_FEATURES}) +target_compile_features(xournalpp-core PUBLIC cxx_std_20) +target_link_libraries(xournalpp-core + PRIVATE xoj::util ${xournalpp_LDFLAGS} + PUBLIC xoj::defaults xoj::external_modules + ) +target_include_directories(xournalpp-core PUBLIC ${xournalpp_include_dirs}) +add_library(xoj::core ALIAS xournalpp-core) diff --git a/entry/src/main/cpp/src/core/audio/AudioPlayer.cpp b/entry/src/main/cpp/src/core/audio/AudioPlayer.cpp new file mode 100644 index 0000000..d730ffa --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/AudioPlayer.cpp @@ -0,0 +1,85 @@ +#include "AudioPlayer.h" + +#include "audio/AudioQueue.h" // for AudioQueue +#include "audio/DeviceInfo.h" // for DeviceInfo +#include "audio/PortAudioConsumer.h" // for PortAudioConsumer +#include "audio/VorbisProducer.h" // for VorbisProducer +#include "control/Control.h" // for Control +#include "control/actions/ActionDatabase.h" // for ActionDatabase +#include "gui/MainWindow.h" // for MainWindow + +class Settings; + +AudioPlayer::AudioPlayer(Control& control, Settings& settings): + control(control), + settings(settings), + audioQueue(std::make_unique>()), + portAudioConsumer(std::make_unique(*this, *audioQueue)), + vorbisProducer(std::make_unique(*audioQueue)) {} + +AudioPlayer::~AudioPlayer() { this->stop(); } + +auto AudioPlayer::start(fs::path const& file, unsigned int timestamp) -> bool { + // Start the producer for reading the data + bool status = this->vorbisProducer->start(file, timestamp); + + // Start playing + if (status) { + status = status && this->play(); + } + + return status; +} + +auto AudioPlayer::isPlaying() -> bool { return this->portAudioConsumer->isPlaying(); } + +void AudioPlayer::pause() { + if (!this->portAudioConsumer->isPlaying()) { + return; + } + + // Stop playing audio + this->portAudioConsumer->stopPlaying(); +} + +auto AudioPlayer::play() -> bool { + if (this->portAudioConsumer->isPlaying()) { + return false; + } + + return this->portAudioConsumer->startPlaying(); +} + +void AudioPlayer::disableAudioPlaybackButtons() { + if (this->audioQueue->hasStreamEnded()) { + auto* actionDB = this->control.getActionDatabase(); + actionDB->enableAction(Action::AUDIO_PAUSE_PLAYBACK, false); + actionDB->enableAction(Action::AUDIO_STOP_PLAYBACK, false); + actionDB->enableAction(Action::AUDIO_SEEK_FORWARDS, false); + actionDB->enableAction(Action::AUDIO_SEEK_BACKWARDS, false); + actionDB->setActionState(Action::AUDIO_PAUSE_PLAYBACK, false); + } +} + +void AudioPlayer::stop() { + disableAudioPlaybackButtons(); + // Stop playing audio + this->portAudioConsumer->stopPlaying(); + + this->audioQueue->signalEndOfStream(); + + // Abort libsox + this->vorbisProducer->abort(); + + // Reset the queue for the next playback + this->audioQueue->reset(); +} + +void AudioPlayer::seek(int seconds) { + // set seek flag here in vorbisProducer + this->vorbisProducer->seek(seconds); +} + +auto AudioPlayer::getOutputDevices() -> std::vector { return this->portAudioConsumer->getOutputDevices(); } + +auto AudioPlayer::getSettings() -> Settings& { return this->settings; } diff --git a/entry/src/main/cpp/src/core/audio/AudioPlayer.h b/entry/src/main/cpp/src/core/audio/AudioPlayer.h new file mode 100644 index 0000000..a6ad72a --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/AudioPlayer.h @@ -0,0 +1,56 @@ +/* + * Xournal++ + * + * Class to play audio from a MP3-file + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for make_unique, unique_ptr +#include // for vector + +#include "filesystem.h" // for path + +template +class AudioQueue; +class Control; +class DeviceInfo; +class PortAudioConsumer; +class Settings; +class VorbisProducer; + + +class AudioPlayer final { +public: + explicit AudioPlayer(Control& control, Settings& settings); + AudioPlayer(AudioPlayer const&) = delete; + AudioPlayer(AudioPlayer&&) = delete; + auto operator=(AudioPlayer const&) -> AudioPlayer& = delete; + auto operator=(AudioPlayer&&) -> AudioPlayer& = delete; + ~AudioPlayer(); + + bool start(fs::path const& file, unsigned int timestamp = 0); + bool isPlaying(); + void stop(); + bool play(); + void pause(); + void seek(int seconds); + + std::vector getOutputDevices(); + + Settings& getSettings(); + void disableAudioPlaybackButtons(); + +private: + Control& control; + Settings& settings; + + std::unique_ptr> audioQueue; + std::unique_ptr portAudioConsumer; + std::unique_ptr vorbisProducer; +}; diff --git a/entry/src/main/cpp/src/core/audio/AudioQueue.h b/entry/src/main/cpp/src/core/audio/AudioQueue.h new file mode 100644 index 0000000..394a16f --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/AudioQueue.h @@ -0,0 +1,147 @@ +/* + * Xournal++ + * + * Queue to connect an audio producer and an audio consumer + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "util/Assert.h" +#include "util/safe_casts.h" // for as_signed + +template +class AudioQueue { +public: + void reset() { + std::lock_guard lock(internalLock); + this->popNotified = false; + this->pushNotified = false; + this->streamEnd = false; + internalQueue.clear(); + + this->sampleRate = -1; + this->channels = 0; + } + + bool empty() { + std::lock_guard lock(internalLock); + return internalQueue.empty(); + } + + size_t size() { + std::lock_guard lock(internalLock); + return internalQueue.size(); + } + + template + void emplace(Iter begI, Iter endI) { + std::lock_guard lock(internalLock); + std::move(begI, endI, std::front_inserter(internalQueue)); + + this->pushNotified = true; + this->pushLockCondition.notify_one(); + } + + template + InsertIter pop(InsertIter insertIter, size_t nSamples) { + std::lock_guard lock(internalLock); + + if (this->channels == 0) { + this->popNotified = true; + this->popLockCondition.notify_one(); + return insertIter; + } + + + auto queueSize = internalQueue.size(); + auto returnBufferLength = std::min(nSamples, queueSize - queueSize % this->channels); + auto begI = rbegin(internalQueue); + auto endI = std::next(begI, as_signed(returnBufferLength)); + + auto ret = std::move(begI, endI, insertIter); + internalQueue.erase(endI.base(), begI.base()); + + this->popNotified = true; + this->popLockCondition.notify_one(); + return ret; + } + + void signalEndOfStream() { + std::lock_guard lock(internalLock); + this->streamEnd = true; + this->pushNotified = true; + this->popNotified = true; + this->pushLockCondition.notify_one(); + this->popLockCondition.notify_one(); + } + + void waitForProducer(std::unique_lock& lock) { + // static_assert(lock.mutex() == &this->queueLock); + xoj_assert(lock.mutex() == &this->queueLock); + while (!this->pushNotified && !hasStreamEnded()) { this->pushLockCondition.wait(lock); } + this->pushNotified = false; + } + + void waitForConsumer(std::unique_lock& lock) { + // static_assert(lock.mutex() == &this->queueLock); + xoj_assert(lock.mutex() == &this->queueLock); + while (!this->popNotified && !hasStreamEnded()) { this->popLockCondition.wait(lock); } + this->popNotified = false; + } + + bool hasStreamEnded() { + std::lock_guard lock(internalLock); + return this->streamEnd; + } + + [[nodiscard]] std::unique_lock acquire_lock() { + std::unique_lock retLock{this->queueLock, std::defer_lock}; + std::lock(retLock, this->internalLock); + std::lock_guard lock{this->internalLock, std::adopt_lock}; + return retLock; + } + + void setAudioAttributes(double lSampleRate, unsigned int lChannels) { + std::lock_guard lock(internalLock); + this->sampleRate = lSampleRate; + this->channels = lChannels; + } + + /** + * @return a pair of the sample rate and the channel count, + * std::pair<>::first is the sample rate and std::pair<>::second the channel count. + * Todo (readability, type-safety): create a struct AudioAttributes; remove this comment + */ + + [[nodiscard]] std::pair getAudioAttributes() { + std::lock_guard lock(internalLock); + return {this->sampleRate, this->channels}; + } + +private: + std::mutex queueLock; + std::mutex internalLock; + + std::deque internalQueue; + + std::condition_variable pushLockCondition; + std::condition_variable popLockCondition; + + double sampleRate{std::numeric_limits::quiet_NaN()}; + uint32_t channels{0}; + + bool streamEnd{false}; + bool pushNotified{false}; + bool popNotified{false}; +}; diff --git a/entry/src/main/cpp/src/core/audio/AudioRecorder.cpp b/entry/src/main/cpp/src/core/audio/AudioRecorder.cpp new file mode 100644 index 0000000..a5709ee --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/AudioRecorder.cpp @@ -0,0 +1,32 @@ +#include "AudioRecorder.h" + +#include "audio/AudioQueue.h" // for AudioQueue +#include "audio/DeviceInfo.h" // for DeviceInfo +#include "audio/PortAudioProducer.h" // for PortAudioProducer +#include "audio/VorbisConsumer.h" // for VorbisConsumer + +AudioRecorder::AudioRecorder(Settings& settings): + audioQueue(std::make_unique>()), + portAudioProducer(std::make_unique(settings, *audioQueue)), + vorbisConsumer(std::make_unique(settings, *audioQueue)) {} + +AudioRecorder::~AudioRecorder() { this->stop(); } + +auto AudioRecorder::start(fs::path const& file) -> bool { + bool status = this->portAudioProducer->startRecording(); + // Start the consumer for writing the data + status = status && this->vorbisConsumer->start(file); + return status; +} + +void AudioRecorder::stop() { + this->portAudioProducer->stopRecording(); + this->vorbisConsumer->join(); // libsox must write all the data before we can continue + this->audioQueue->reset(); // next recording requires empty queue +} + +auto AudioRecorder::isRecording() const -> bool { return this->portAudioProducer->isRecording(); } + +auto AudioRecorder::getInputDevices() const -> std::vector { + return this->portAudioProducer->getInputDevices(); +} diff --git a/entry/src/main/cpp/src/core/audio/AudioRecorder.h b/entry/src/main/cpp/src/core/audio/AudioRecorder.h new file mode 100644 index 0000000..98b90fe --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/AudioRecorder.h @@ -0,0 +1,44 @@ +/* + * Xournal++ + * + * Class to record audio and store it as MP3-file + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for make_unique, unique_ptr +#include // for vector + +#include "filesystem.h" // for path + +template +class AudioQueue; +class DeviceInfo; +class PortAudioProducer; +class Settings; +class VorbisConsumer; + +class AudioRecorder final { +public: + explicit AudioRecorder(Settings& settings); + AudioRecorder(AudioRecorder const&) = delete; + AudioRecorder(AudioRecorder&&) = delete; + auto operator=(AudioRecorder const&) -> AudioRecorder& = delete; + auto operator=(AudioRecorder&&) -> AudioRecorder& = delete; + ~AudioRecorder(); + + bool start(fs::path const& file); + void stop(); + [[nodiscard]] bool isRecording() const; + std::vector getInputDevices() const; + +private: + std::unique_ptr> audioQueue; + std::unique_ptr portAudioProducer; + std::unique_ptr vorbisConsumer; +}; diff --git a/entry/src/main/cpp/src/core/audio/DeviceInfo.cpp b/entry/src/main/cpp/src/core/audio/DeviceInfo.cpp new file mode 100644 index 0000000..eb99296 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/DeviceInfo.cpp @@ -0,0 +1,15 @@ +#include "DeviceInfo.h" + +DeviceInfo::DeviceInfo(portaudio::Device* device, bool selected): + deviceName(device->name()), + index(device->index()), + selected(selected), + inputChannels((device->isFullDuplexDevice() || device->isInputOnlyDevice()) ? device->maxInputChannels() : 0), + outputChannels((device->isFullDuplexDevice() || device->isOutputOnlyDevice()) ? device->maxOutputChannels() : + 0) {} + +auto DeviceInfo::getDeviceName() const -> const std::string& { return deviceName; } + +auto DeviceInfo::getIndex() const -> PaDeviceIndex { return index; } + +auto DeviceInfo::getSelected() const -> bool { return selected; } diff --git a/entry/src/main/cpp/src/core/audio/DeviceInfo.h b/entry/src/main/cpp/src/core/audio/DeviceInfo.h new file mode 100644 index 0000000..6412150 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/DeviceInfo.h @@ -0,0 +1,37 @@ +/* + * Xournal++ + * + * Class storing information about an audio device + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ +#pragma once + +#include // for string + +#include "config-features.h" + +#ifdef ENABLE_AUDIO +#include // for PaDeviceIndex + + +class DeviceInfo { +public: + DeviceInfo(portaudio::Device* device, bool selected); + +public: + const std::string& getDeviceName() const; + PaDeviceIndex getIndex() const; + bool getSelected() const; + +private: + const std::string deviceName; + const PaDeviceIndex index{}; + const bool selected{}; + const int inputChannels{}; + const int outputChannels{}; +}; +#endif diff --git a/entry/src/main/cpp/src/core/audio/PortAudioConsumer.cpp b/entry/src/main/cpp/src/core/audio/PortAudioConsumer.cpp new file mode 100644 index 0000000..a228bd7 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/PortAudioConsumer.cpp @@ -0,0 +1,162 @@ +#include "PortAudioConsumer.h" + +#include // for for_each, transform, max +#include // for _Deque_iterator +#include // for next, prev +#include // for to_string, string + +#include // for g_warning + +#include "audio/AudioQueue.h" // for AudioQueue +#include "audio/DeviceInfo.h" // for DeviceInfo +#include "control/settings/Settings.h" // for Settings +#include "util/safe_casts.h" // for as_unsigned + +#include "AudioPlayer.h" // for AudioPlayer + +constexpr auto FRAMES_PER_BUFFER{64U}; + +auto PortAudioConsumer::getOutputDevices() const -> std::vector { + std::vector deviceList; + deviceList.reserve(as_unsigned(this->sys.deviceCount())); + + for (auto i = this->sys.devicesBegin(); i != sys.devicesEnd(); ++i) { + if (i->isFullDuplexDevice() || i->isOutputOnlyDevice()) { + DeviceInfo deviceInfo(&(*i), this->audioPlayer.getSettings().getAudioOutputDevice() == i->index()); + deviceList.push_back(deviceInfo); + } + } + return deviceList; +} + +auto PortAudioConsumer::getSelectedOutputDevice() const -> DeviceInfo { + PaDeviceIndex idx = this->audioPlayer.getSettings().getAudioOutputDevice(); + if (idx == -1) { + return DeviceInfo(&sys.defaultOutputDevice(), true); + } + try { + return DeviceInfo(&sys.deviceByIndex(idx), true); + } catch (const portaudio::PaException& e) { + g_warning("PortAudioConsumer: Selected output device was not found - fallback to default output device\nCaused " + "by: %s", + e.what()); + return DeviceInfo(&sys.defaultOutputDevice(), true); + } +} + +auto PortAudioConsumer::isPlaying() const -> bool { return this->outputStream && this->outputStream->isActive(); } + +auto PortAudioConsumer::startPlaying() -> bool { + // Abort a playback stream if one is currently active + if (isPlaying()) { + this->outputStream->abort(); + } + + auto [sampleRate, channels] = this->audioQueue.getAudioAttributes(); + + if (sampleRate == -1) { + g_warning("PortAudioConsumer: Timing issue - Sample rate requested before known"); + return false; + } + + // Get the device information of our output device + portaudio::Device* device = nullptr; + try { + device = &sys.deviceByIndex(getSelectedOutputDevice().getIndex()); + } catch (const portaudio::PaException&) { + g_warning("PortAudioConsumer: Unable to find selected output device"); + return false; + } + + if (device->maxOutputChannels() < channels) { + this->audioQueue.signalEndOfStream(); + g_warning("Output device has not enough channels to play audio file. (Requires at least 2 channels)"); + return false; + } + + this->outputChannels = channels; + portaudio::DirectionSpecificStreamParameters outParams(*device, channels, portaudio::FLOAT32, true, + device->defaultLowOutputLatency(), nullptr); + portaudio::StreamParameters params(portaudio::DirectionSpecificStreamParameters::null(), outParams, sampleRate, + FRAMES_PER_BUFFER, paNoFlag); + + try { + this->outputStream = std::make_unique>( + params, *this, &PortAudioConsumer::playCallback); + } catch (const portaudio::PaException& e) { + this->audioQueue.signalEndOfStream(); + g_warning("PortAudioConsumer: Unable to open stream to device\nCaused by: %s", e.what()); + return false; + } + // Start the recording + try { + this->outputStream->start(); + } catch (const portaudio::PaException& e) { + this->audioQueue.signalEndOfStream(); + g_warning("PortAudioConsumer: Unable to start stream\nCaused by: %s", e.what()); + this->outputStream.reset(); + return false; + } + return true; +} + +auto PortAudioConsumer::playCallback(const void* /*inputBuffer*/, void* outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags statusFlags) -> int { + if (statusFlags) { + g_warning("PortAudioConsumer: PortAudio reported a stream warning: %s", std::to_string(statusFlags).c_str()); + } + + if (outputBuffer != nullptr) { + auto begI = static_cast(outputBuffer); + auto midI = this->audioQueue.pop(begI, framesPerBuffer * as_unsigned(this->outputChannels)); + auto endI = std::next(begI, as_signed(framesPerBuffer) * this->outputChannels); + // Fill buffer to requested length if necessary + + if (midI != endI) { + // Show underflow warning if there are not enough samples and the stream is not yet finished + if (!this->audioQueue.hasStreamEnded()) { + g_warning("PortAudioConsumer: Not enough audio samples available to fill requested frame"); + } + + if (midI > std::next(begI, this->outputChannels)) { + // If there is previous audio data use this data to ramp down the audio samples + std::transform(std::prev(midI, this->outputChannels), std::prev(endI, this->outputChannels), midI, + [](auto&& ampl) { return ampl / 2.0; }); + } else { + // If there is no data that could be used to ramp down just output silence + std::for_each(midI, endI, [](auto& ampl) { ampl = 0; }); + } + } + + // Continue playback if there is still data available + if (this->audioQueue.hasStreamEnded() && this->audioQueue.empty()) { + this->audioPlayer.disableAudioPlaybackButtons(); + return paComplete; + } + + + return paContinue; + } + + // The output buffer is no longer available - Abort! + this->audioQueue.signalEndOfStream(); + this->audioPlayer.disableAudioPlaybackButtons(); + return paAbort; +} + +void PortAudioConsumer::stopPlaying() { + // Stop the playback + if (this->outputStream) { + try { + if (this->outputStream->isActive()) { + this->outputStream->stop(); + } + } catch (const portaudio::PaException&) { + /* + * We try closing the stream but this->outputStream might be an invalid object at this time if the stream + * was previously closed by the backend. Just ignore this as the stream is closed either way. + */ + } + } + this->outputStream.reset(); +} diff --git a/entry/src/main/cpp/src/core/audio/PortAudioConsumer.h b/entry/src/main/cpp/src/core/audio/PortAudioConsumer.h new file mode 100644 index 0000000..0c6b761 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/PortAudioConsumer.h @@ -0,0 +1,46 @@ +/* + * Xournal++ + * + * Class to play audio using libportaudio + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for unique_ptr +#include // for vector + +#include // for MemFunCallbackStream, System + +#include "DeviceInfo.h" // for DeviceInfo + +class AudioPlayer; +template +class AudioQueue; + +class PortAudioConsumer final { +public: + explicit PortAudioConsumer(AudioPlayer& audioPlayer, AudioQueue& audioQueue): + audioPlayer(audioPlayer), audioQueue(audioQueue) {} + + std::vector getOutputDevices() const; + DeviceInfo getSelectedOutputDevice() const; + bool isPlaying() const; + bool startPlaying(); + int playCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags); + void stopPlaying(); + +private: + portaudio::System& sys{portaudio::System::instance()}; + AudioPlayer& audioPlayer; + AudioQueue& audioQueue; + + std::unique_ptr> outputStream; + + int outputChannels = 0; +}; diff --git a/entry/src/main/cpp/src/core/audio/PortAudioProducer.cpp b/entry/src/main/cpp/src/core/audio/PortAudioProducer.cpp new file mode 100644 index 0000000..8521265 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/PortAudioProducer.cpp @@ -0,0 +1,127 @@ +#include "PortAudioProducer.h" + +#include // for min, max +#include // for size_t +#include // for next +#include // for to_string, string + +#include // for g_message + +#include "audio/AudioQueue.h" // for AudioQueue +#include "audio/DeviceInfo.h" // for DeviceInfo +#include "control/settings/Settings.h" // for Settings +#include "util/safe_casts.h" // for as_unsigned + + +constexpr auto FRAMES_PER_BUFFER{64U}; + +auto PortAudioProducer::getInputDevices() const -> std::vector { + auto devCount = as_unsigned(this->sys.deviceCount()); + std::vector deviceList; + deviceList.reserve(devCount); + + for (auto i = this->sys.devicesBegin(); i != sys.devicesEnd(); ++i) { + + if (i->isFullDuplexDevice() || i->isInputOnlyDevice()) { + DeviceInfo deviceInfo(&(*i), this->settings.getAudioInputDevice() == i->index()); + deviceList.push_back(deviceInfo); + } + } + return deviceList; +} + +auto PortAudioProducer::getSelectedInputDevice() const -> DeviceInfo { + PaDeviceIndex idx = this->settings.getAudioInputDevice(); + if (idx == -1) { + return DeviceInfo(&sys.defaultInputDevice(), true); + } + try { + return DeviceInfo(&sys.deviceByIndex(idx), true); + } catch (const portaudio::PaException& e) { + g_message( + "PortAudioProducer: Selected input device not found - fallback to default input device\nCaused by: %s", + e.what()); + return DeviceInfo(&sys.defaultInputDevice(), true); + } +} + +auto PortAudioProducer::isRecording() const -> bool { return this->inputStream && this->inputStream->isActive(); } + +auto PortAudioProducer::startRecording() -> bool { + // Check if there already is a recording + if (this->inputStream) { + return false; + } + + // Get the device information of our input device + portaudio::Device* device = nullptr; + try { + device = &sys.deviceByIndex(getSelectedInputDevice().getIndex()); + } catch (const portaudio::PaException&) { + g_message("PortAudioProducer: Unable to find selected input device"); + return false; + } + + // Restrict recording channels to 2 as playback devices should have 2 channels at least + this->inputChannels = std::min(2, device->maxInputChannels()); + portaudio::DirectionSpecificStreamParameters inParams(*device, this->inputChannels, portaudio::FLOAT32, true, + device->defaultLowInputLatency(), nullptr); + portaudio::StreamParameters params(inParams, portaudio::DirectionSpecificStreamParameters::null(), + this->settings.getAudioSampleRate(), FRAMES_PER_BUFFER, paNoFlag); + + this->audioQueue.setAudioAttributes(this->settings.getAudioSampleRate(), + static_cast(this->inputChannels)); + + // Specify the callback used for buffering the recorded data + try { + this->inputStream = std::make_unique>( + params, *this, &PortAudioProducer::recordCallback); + } catch (const portaudio::PaException&) { + g_message("PortAudioProducer: Unable to open stream"); + return false; + } + + // Start the recording + try { + this->inputStream->start(); + } catch (const portaudio::PaException&) { + g_message("PortAudioProducer: Unable to start stream"); + this->inputStream.reset(); + return false; + } + + return true; +} + +auto PortAudioProducer::recordCallback(const void* inputBuffer, void* /*outputBuffer*/, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags statusFlags) -> int { + if (statusFlags) { + g_message("PortAudioProducer: statusFlag: %s", std::to_string(statusFlags).c_str()); + } + + if (inputBuffer != nullptr) { + size_t providedFrames = framesPerBuffer * as_unsigned(this->inputChannels); + auto begI = static_cast(inputBuffer); + this->audioQueue.emplace(begI, std::next(begI, as_signed(providedFrames))); + } + return paContinue; +} + +void PortAudioProducer::stopRecording() { + // Stop the recording + if (this->inputStream) { + try { + if (this->inputStream->isActive()) { + this->inputStream->stop(); + } + } catch (const portaudio::PaException&) { + g_message("PortAudioProducer: Closing stream failed"); + } + } + + // Notify the consumer at the other side that there will be no more data + this->audioQueue.signalEndOfStream(); + + // Allow new recording by removing the old one + this->inputStream.reset(); +} diff --git a/entry/src/main/cpp/src/core/audio/PortAudioProducer.h b/entry/src/main/cpp/src/core/audio/PortAudioProducer.h new file mode 100644 index 0000000..597b22b --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/PortAudioProducer.h @@ -0,0 +1,52 @@ +/* + * Xournal++ + * + * Class to record audio using libportaudio + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for unique_ptr +#include // for vector + +#include // for MemFunCallbackStream, System + +#include "DeviceInfo.h" // for DeviceInfo + +class Settings; +template +class AudioQueue; + + +class PortAudioProducer { +public: + PortAudioProducer(Settings& settings, AudioQueue& audioQueue): settings(settings), audioQueue(audioQueue){}; + + std::vector getInputDevices() const; + + DeviceInfo getSelectedInputDevice() const; + + bool isRecording() const; + + bool startRecording(); + + int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags); + + void stopRecording(); + +private: + portaudio::System& sys{portaudio::System::instance()}; + Settings& settings; + + AudioQueue& audioQueue; + + std::unique_ptr> inputStream; + + int inputChannels = 0; +}; diff --git a/entry/src/main/cpp/src/core/audio/SNDFileCpp.h b/entry/src/main/cpp/src/core/audio/SNDFileCpp.h new file mode 100644 index 0000000..bc89b17 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/SNDFileCpp.h @@ -0,0 +1,61 @@ +/* + * Xournal++ + * + * Helper functions for libsndfile + * platform dependent code to open std::filesystem::paths + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#ifdef _WIN32 +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif + +#include + +#include + +#include "filesystem.h" + +namespace xoj::audio { + +struct SNDFILE_Deleter { + void operator()(SNDFILE* file) { sf_close(file); } +}; + +using SNDFileGuard = std::unique_ptr; + + +/** + * Open a file with libsndfile + * + * @param path std::string const& path to ensure system single byte encoding + * @return The resource pointer to the sound file + */ +inline SNDFILE* sf_open_pp(std::string const& path, int mode, SF_INFO* info) { + return sf_open(path.c_str(), mode, info); +} +#if (defined(ENABLE_SNDFILE_WINDOWS_PROTOTYPES) && ENABLE_SNDFILE_WINDOWS_PROTOTYPES) +/** + * Open a file with libsndfile + * + * @param path std::wstring const& path to ensure system double byte encoding + * @return The resource pointer to the sound file + */ +inline SNDFILE* sf_open_pp(std::wstring const& path, int mode, SF_INFO* info) { + return sf_wchar_open(path.c_str(), mode, info); +} +#endif + +// naming similar to std::make_unique +inline auto make_snd_file(fs::path const& file, int mode, SF_INFO* info) { + return SNDFileGuard{sf_open_pp(file, mode, info)}; +} + +} // namespace xoj::audio \ No newline at end of file diff --git a/entry/src/main/cpp/src/core/audio/VorbisConsumer.cpp b/entry/src/main/cpp/src/core/audio/VorbisConsumer.cpp new file mode 100644 index 0000000..3fb9f82 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/VorbisConsumer.cpp @@ -0,0 +1,80 @@ +#include "VorbisConsumer.h" + +#include // for for_each, min, max +#include // for size_t +#include // for _Deque_iterator +#include // for back_insert_iterator, back_in... +#include // for unique_ptr +#include // for string +#include // for move +#include // for vector + +#include // for g_warning +#include // for SF_INFO, sf_strerror, sf_writ... + +#include "audio/AudioQueue.h" // for AudioQueue +#include "control/settings/Settings.h" // for Settings +#include "util/StringUtils.h" + +#include "SNDFileCpp.h" // for make_snd_file, xoj + +using namespace xoj; + +auto VorbisConsumer::start(fs::path const& file) -> bool { + auto [sampleRate, channels] = this->audioQueue.getAudioAttributes(); + + if (sampleRate == -1) { + g_warning("VorbisConsumer: Timing issue - Sample rate requested before known"); + return false; + } + + SF_INFO sfInfo; + sfInfo.channels = channels; + sfInfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; + sfInfo.samplerate = static_cast(this->settings.getAudioSampleRate()); + + auto sfFile = audio::make_snd_file(file.native(), SFM_WRITE, &sfInfo); + if (!sfFile) { + g_warning("VorbisConsumer: output file \"%s\" could not be opened\ncaused by:%s", + char_cast(file.u8string().c_str()), sf_strerror(sfFile.get())); + return false; + } + + this->consumerThread = std::thread([this, sfFile = std::move(sfFile), channels = channels] { + auto lock{audioQueue.acquire_lock()}; + auto buffer_size{size_t(64 * channels)}; + std::vector buffer; + buffer.reserve(buffer_size); // efficiency + float audioGain = static_cast(this->settings.getAudioGain()); + + while (!(this->stopConsumer || (audioQueue.hasStreamEnded() && audioQueue.empty()))) { + audioQueue.waitForProducer(lock); + while (audioQueue.size() > buffer_size || (audioQueue.hasStreamEnded() && !audioQueue.empty())) { + buffer.resize(0); + this->audioQueue.pop(std::back_inserter(buffer), buffer_size); + // apply gain + if (audioGain != 1.0f) { + std::for_each(begin(buffer), end(buffer), [audioGain](auto& val) { val *= audioGain; }); + } + sf_writef_float(sfFile.get(), buffer.data(), + std::min(sf_count_t(buffer.size()) / channels, 64)); + } + } + }); + return true; +} + +void VorbisConsumer::join() { + // Join the consumer thread to wait for completion + if (this->consumerThread.joinable()) { + this->consumerThread.join(); + } +} + +void VorbisConsumer::stop() { + // Stop consumer + this->audioQueue.signalEndOfStream(); + + // Wait for consumer to finish + join(); +} diff --git a/entry/src/main/cpp/src/core/audio/VorbisConsumer.h b/entry/src/main/cpp/src/core/audio/VorbisConsumer.h new file mode 100644 index 0000000..6d78b79 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/VorbisConsumer.h @@ -0,0 +1,39 @@ +/* + * Xournal++ + * + * Class to save audio data in an opus file + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for atomic +#include // for thread + +#include "filesystem.h" // for path + +class Settings; +template +class AudioQueue; + +class VorbisConsumer final { +public: + explicit VorbisConsumer(Settings& settings, AudioQueue& audioQueue): + settings(settings), audioQueue(audioQueue) {} + +public: + bool start(fs::path const& file); + void join(); + void stop(); + +private: + Settings& settings; + AudioQueue& audioQueue; + + std::thread consumerThread{}; + std::atomic stopConsumer{false}; +}; diff --git a/entry/src/main/cpp/src/core/audio/VorbisProducer.cpp b/entry/src/main/cpp/src/core/audio/VorbisProducer.cpp new file mode 100644 index 0000000..da15348 --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/VorbisProducer.cpp @@ -0,0 +1,84 @@ +#include "VorbisProducer.h" + +#include // for fill_n, max +#include // for size_t, SEEK_CUR, SEEK_SET +#include // for begin, end +#include // for unique_ptr +#include // for string +#include // for move +#include // for vector + +#include // for g_warning +#include // for SF_INFO, sf_seek, sf_count_t, sf_readf... + +#include "audio/AudioQueue.h" // for AudioQueue +#include "audio/SNDFileCpp.h" // for make_snd_file, xoj +#include "util/StringUtils.h" + +using namespace xoj; + +constexpr auto sample_buffer_size = size_t{16384U}; + +auto VorbisProducer::start(fs::path const& file, unsigned int timestamp) -> bool { + SF_INFO sfInfo{}; + auto sfFile = audio::make_snd_file(file, SFM_READ, &sfInfo); + if (!sfFile) { + g_warning("VorbisProducer: input file \"%s\" could not be opened\ncaused by:%s", + char_cast(file.u8string().c_str()), sf_strerror(sfFile.get())); + return false; + } + + sf_count_t seekPosition = sfInfo.samplerate / 1000 * sf_count_t(timestamp); + + if (seekPosition < sfInfo.frames) { + sf_seek(sfFile.get(), seekPosition, SEEK_SET); + } else { + g_warning("VorbisProducer: Seeking outside of audio file extent"); + } + + this->audioQueue.setAudioAttributes(sfInfo.samplerate, static_cast(sfInfo.channels)); + + this->producerThread = std::thread([this, sfInfo, sfFile = std::move(sfFile)] { + sf_count_t numFrames{1}; + size_t const bufferSize{size_t(1024U) * size_t(sfInfo.channels)}; + std::vector sampleBuffer(bufferSize); + auto lock = audioQueue.acquire_lock(); + + while (!this->stopProducer && numFrames > 0 && !this->audioQueue.hasStreamEnded()) { + sampleBuffer.resize(bufferSize); + numFrames = sf_readf_float(sfFile.get(), sampleBuffer.data(), 1024); + sampleBuffer.resize(size_t(numFrames * sfInfo.channels)); + + while (this->audioQueue.size() >= sample_buffer_size && !this->audioQueue.hasStreamEnded() && + !this->stopProducer) { + audioQueue.waitForConsumer(lock); + } + + if (auto tmpSeekSeconds = this->seekSeconds.load(); tmpSeekSeconds != 0) { + sf_seek(sfFile.get(), tmpSeekSeconds * sfInfo.samplerate, SEEK_CUR); + this->seekSeconds -= tmpSeekSeconds; + } + + this->audioQueue.emplace(begin(sampleBuffer), end(sampleBuffer)); + } + this->audioQueue.signalEndOfStream(); + }); + return true; +} + +void VorbisProducer::abort() { + this->stopProducer = true; + // Wait for producer to finish + stop(); + this->stopProducer = false; +} + +void VorbisProducer::stop() { + // Wait for producer to finish + if (this->producerThread.joinable()) { + this->producerThread.join(); + } +} + + +void VorbisProducer::seek(int seconds) { this->seekSeconds = seconds; } diff --git a/entry/src/main/cpp/src/core/audio/VorbisProducer.h b/entry/src/main/cpp/src/core/audio/VorbisProducer.h new file mode 100644 index 0000000..2018d9a --- /dev/null +++ b/entry/src/main/cpp/src/core/audio/VorbisProducer.h @@ -0,0 +1,37 @@ +/* + * Xournal++ + * + * Class to read audio data from an mp3 file + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for atomic +#include // for thread + +#include "filesystem.h" // for path + +template +class AudioQueue; + +class VorbisProducer final { +public: + explicit VorbisProducer(AudioQueue& audioQueue): audioQueue(audioQueue) {} + + bool start(fs::path const& file, unsigned int timestamp); + void abort(); + void stop(); + void seek(int seconds); + +private: + AudioQueue& audioQueue; + std::thread producerThread{}; + + std::atomic stopProducer{false}; + std::atomic seekSeconds{0}; +}; diff --git a/entry/src/main/cpp/src/core/control/AudioController.cpp b/entry/src/main/cpp/src/core/control/AudioController.cpp new file mode 100644 index 0000000..1154d2c --- /dev/null +++ b/entry/src/main/cpp/src/core/control/AudioController.cpp @@ -0,0 +1,132 @@ +#include "AudioController.h" + +#include // for array +#include // for snprintf +#include // for tm, localtime, time +#include // for string, allocator + +#include // for g_get_monotonic_time + +#include "audio/AudioPlayer.h" // for AudioPlayer +#include "audio/AudioRecorder.h" // for AudioRecorder +#include "audio/DeviceInfo.h" // for DeviceInfo +#include "control/Control.h" // for Control +#include "control/actions/ActionDatabase.h" // for ActionDatabase +#include "control/settings/Settings.h" // for Settings +#include "gui/MainWindow.h" // for MainWindow +#include "gui/toolbarMenubar/ToolMenuHandler.h" // for ToolMenuHandler +#include "util/XojMsgBox.h" // for XojMsgBox +#include "util/i18n.h" // for _ +#include "util/safe_casts.h" // for as_signed + +using std::string; +using std::vector; + +AudioController::AudioController(Settings* settings, Control* control): + settings(*settings), + control(*control), + audioRecorder(std::make_unique(*settings)), + audioPlayer(std::make_unique(*control, *settings)) {} + +AudioController::~AudioController() = default; + + +auto AudioController::startRecording() -> bool { + if (!this->isRecording()) { + if (getAudioFolder().empty()) { + return false; + } + + this->timestamp = static_cast(g_get_monotonic_time() / 1000); + + std::array buffer{}; + time_t secs = time(nullptr); + tm* t = localtime(&secs); + // This prints the date and time in ISO format. + snprintf(buffer.data(), buffer.size(), "%04d-%02d-%02d_%02d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + string data(buffer.data()); + data += ".ogg"; + + audioFilename = data; + + g_message("Start recording"); + + bool isRecording = this->audioRecorder->start(getAudioFolder() / data); + + if (!isRecording) { + audioFilename = ""; + this->timestamp = 0; + } + + return isRecording; + } + return false; +} + +auto AudioController::stopRecording() -> bool { + if (this->audioRecorder->isRecording()) { + audioFilename = ""; + this->timestamp = 0; + + g_message("Stop recording"); + + this->audioRecorder->stop(); + } + return true; +} + +auto AudioController::isRecording() -> bool { return this->audioRecorder->isRecording(); } + +auto AudioController::isPlaying() -> bool { return this->audioPlayer->isPlaying(); } + +auto AudioController::startPlayback(fs::path const& file, unsigned int timestamp) -> bool { + this->audioPlayer->stop(); + bool status = this->audioPlayer->start(file, timestamp); + if (status) { + auto* actionDB = this->control.getActionDatabase(); + actionDB->enableAction(Action::AUDIO_PAUSE_PLAYBACK, true); + actionDB->enableAction(Action::AUDIO_STOP_PLAYBACK, true); + actionDB->enableAction(Action::AUDIO_SEEK_FORWARDS, true); + actionDB->enableAction(Action::AUDIO_SEEK_BACKWARDS, true); + } + return status; +} + +void AudioController::pausePlayback() { + this->control.getActionDatabase()->setActionState(Action::AUDIO_PAUSE_PLAYBACK, true); + + this->audioPlayer->pause(); +} + +void AudioController::seekForwards() { this->audioPlayer->seek(as_signed(this->settings.getDefaultSeekTime())); } + +void AudioController::seekBackwards() { this->audioPlayer->seek(-1 * as_signed(this->settings.getDefaultSeekTime())); } + +void AudioController::continuePlayback() { + this->control.getActionDatabase()->setActionState(Action::AUDIO_PAUSE_PLAYBACK, false); + + this->audioPlayer->play(); +} + +void AudioController::stopPlayback() { this->audioPlayer->stop(); } + +auto AudioController::getAudioFilename() const -> fs::path const& { return this->audioFilename; } + +auto AudioController::getAudioFolder() const -> fs::path { + auto const& af = this->settings.getAudioFolder(); + + if (!fs::is_directory(af)) { + string msg = _("Audio folder not set or invalid! Recording won't work!\nPlease set the " + "recording folder under \"Preferences > Audio recording\""); + XojMsgBox::showErrorToUser(this->control.getGtkWindow(), msg); + return fs::path{}; + } + return af; +} + +auto AudioController::getStartTime() const -> size_t { return this->timestamp; } + +auto AudioController::getOutputDevices() const -> vector { return this->audioPlayer->getOutputDevices(); } + +auto AudioController::getInputDevices() const -> vector { return this->audioRecorder->getInputDevices(); } diff --git a/entry/src/main/cpp/src/core/control/AudioController.h b/entry/src/main/cpp/src/core/control/AudioController.h new file mode 100644 index 0000000..450d575 --- /dev/null +++ b/entry/src/main/cpp/src/core/control/AudioController.h @@ -0,0 +1,72 @@ +/* + * Xournal++ + * + * Audio Recording / Playing controller + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include "config-features.h" // for ENABLE_AUDIO +#ifdef ENABLE_AUDIO + +#include // for size_t +#include // for make_unique, unique_ptr +#include // for vector + +#include // for AutoSystem + +#include "filesystem.h" // for path + +class AudioPlayer; +class AudioRecorder; +class Control; +class DeviceInfo; +class Settings; + +class AudioController final { +public: + // Todo convert Pointers to reference (changes to control.cpp are necessary) + AudioController(Settings* settings, Control* control); + ~AudioController(); + + bool startRecording(); + bool stopRecording(); + bool isRecording(); + + bool isPlaying(); + bool startPlayback(fs::path const& file, unsigned int timestamp); + void pausePlayback(); + void continuePlayback(); + void stopPlayback(); + void seekForwards(); + void seekBackwards(); + + fs::path const& getAudioFilename() const; + fs::path getAudioFolder() const; + size_t getStartTime() const; + std::vector getOutputDevices() const; + std::vector getInputDevices() const; + +private: + Settings& settings; + Control& control; + + /** + * RAII initializer don't move below the portaudio::System::instance() calls in + * AudioRecorder and AudioPlayer + * */ + portaudio::AutoSystem autoSys; + std::unique_ptr audioRecorder; + std::unique_ptr audioPlayer; + + fs::path audioFilename; + size_t timestamp = 0; +}; +#else +class AudioController final {}; +#endif diff --git a/entry/src/main/cpp/src/core/control/ClipboardHandler.cpp b/entry/src/main/cpp/src/core/control/ClipboardHandler.cpp new file mode 100644 index 0000000..81a631b --- /dev/null +++ b/entry/src/main/cpp/src/core/control/ClipboardHandler.cpp @@ -0,0 +1,323 @@ +#include "ClipboardHandler.h" + +#include // for multiset, operator!= +#include // for move +#include // for vector + +#include // for cairo_svg_surface_c... +#include // for cairo_create, cairo... +#include // for gdk_pixbuf_get_from_surface +#include // for g_object_unref, g_s... + +#include "control/tools/EditSelection.h" // for EditSelection +#include "model/Element.h" // for Element, ELEMENT_TEXT +#include "model/Text.h" // for Text +#include "util/Util.h" // for DPI_NORMALIZATION_F... +#include "util/gtk4_helper.h" // for gtk_widget_get_clipboard +#include "util/safe_casts.h" // for as_unsigned +#include "util/serializing/BinObjectEncoding.h" // for BinObjectEncoding +#include "util/serializing/ObjectInputStream.h" // for ObjectInputStream +#include "util/serializing/ObjectOutputStream.h" // for ObjectOutputStream +#include "view/ElementContainerView.h" // for ElementContainerView +#include "view/View.h" // for Context + +#include "config.h" // for PROJECT_STRING + +using std::string; + +ClipboardListener::~ClipboardListener() = default; + +ClipboardHandler::ClipboardHandler(ClipboardListener* listener, GtkWidget* widget) { + this->listener = listener; + this->clipboard = gtk_widget_get_clipboard(widget); + + this->handlerId = g_signal_connect(this->clipboard, "owner-change", G_CALLBACK(&ownerChangedCallback), this); + + this->listener->clipboardCutCopyEnabled(false); + + gtk_clipboard_request_contents(clipboard, gdk_atom_intern_static_string("TARGETS"), + reinterpret_cast(receivedClipboardContents), this); +} + +ClipboardHandler::~ClipboardHandler() { g_signal_handler_disconnect(this->clipboard, this->handlerId); } + +static GdkAtom atomXournal = gdk_atom_intern_static_string("application/xournal"); + +auto ClipboardHandler::paste() -> bool { + /* Request targets again, since the owner-change signal is not emitted on MacOS and under X11 with no XFIXES + * extension. See https://docs.gtk.org/gdk3/struct.EventOwnerChange.html and + * https://gitlab.gnome.org/GNOME/gtk/-/issues/1757 */ + gtk_clipboard_request_contents(clipboard, gdk_atom_intern_static_string("TARGETS"), + reinterpret_cast(receivedClipboardContents), this); + + if (this->containsXournal) { + gtk_clipboard_request_contents(this->clipboard, atomXournal, + reinterpret_cast(pasteClipboardContents), this); + return true; + } + if (this->containsText) { + gtk_clipboard_request_text(this->clipboard, reinterpret_cast(pasteClipboardText), + this); + return true; + } + if (this->containsImage) { + gtk_clipboard_request_image(this->clipboard, + reinterpret_cast(pasteClipboardImage), this); + return true; + } + + return false; +} + +auto ClipboardHandler::cut() -> bool { + bool result = this->copy(); + this->listener->deleteSelection(); + + return result; +} + +auto ElementCompareFunc(const Element* a, const Element* b) -> bool { + if (a->getY() == b->getY()) { + return (a->getX() - b->getX()) < 0; + } + return (a->getY() - b->getY()) < 0; +} + +static GdkAtom atomSvg1 = gdk_atom_intern_static_string("image/svg"); +static GdkAtom atomSvg2 = gdk_atom_intern_static_string("image/svg+xml"); + +// The contents of the clipboard +class ClipboardContents { +public: + ClipboardContents(string text, GdkPixbuf* image, string svg, GString* str) { + this->text = std::move(text); + this->image = image; + this->svg = std::move(svg); + this->str = str; + } + + ~ClipboardContents() { + g_object_unref(this->image); + g_string_free(this->str, true); + } + + + static void getFunction(GtkClipboard* clipboard, GtkSelectionData* selection, guint info, + ClipboardContents* contents) { + GdkAtom target = gtk_selection_data_get_target(selection); + + if (target == gdk_atom_intern_static_string("UTF8_STRING")) { + gtk_selection_data_set_text(selection, contents->text.c_str(), -1); + } else if (target == gdk_atom_intern_static_string("image/png") || + target == gdk_atom_intern_static_string("image/jpeg") || + target == gdk_atom_intern_static_string("image/gif")) { + gtk_selection_data_set_pixbuf(selection, contents->image); + } else if (atomSvg1 == target || atomSvg2 == target) { + gtk_selection_data_set(selection, target, 8, reinterpret_cast(contents->svg.c_str()), + static_cast(contents->svg.length())); + } else if (atomXournal == target) { + gtk_selection_data_set(selection, target, 8, reinterpret_cast(contents->str->str), + static_cast(contents->str->len)); + } + } + + static void clearFunction(GtkClipboard* clipboard, ClipboardContents* contents) { delete contents; } + +private: + string text; + GdkPixbuf* image; + string svg; + GString* str; +}; + +static auto svgWriteFunction(GString* string, const unsigned char* data, unsigned int length) -> cairo_status_t { + g_string_append_len(string, reinterpret_cast(data), length); + return CAIRO_STATUS_SUCCESS; +} + +auto ClipboardHandler::copy() -> bool { + if (!this->selection) { + return false; + } + + ///////////////////////////////////////////////////////////////// + // prepare xournal contents + ///////////////////////////////////////////////////////////////// + + ObjectOutputStream out(new BinObjectEncoding()); + + out.writeString(PROJECT_STRING); + + this->selection->serialize(out); + + ///////////////////////////////////////////////////////////////// + // prepare text contents + ///////////////////////////////////////////////////////////////// + + std::multiset textElements(ElementCompareFunc); + + for (const Element* e: this->selection->getElementsView()) { + if (e->getType() == ELEMENT_TEXT) { + textElements.insert(dynamic_cast(e)); + } + } + + string text{}; + for (const Text* t: textElements) { + if (!text.empty()) { + text += "\n"; + } + text += t->getText(); + } + + ///////////////////////////////////////////////////////////////// + // prepare image contents: PNG + ///////////////////////////////////////////////////////////////// + + double dpiFactor = 1.0 / Util::DPI_NORMALIZATION_FACTOR * 300.0; + + int width = static_cast(selection->getWidth() * dpiFactor); + int height = static_cast(selection->getHeight() * dpiFactor); + cairo_surface_t* surfacePng = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t* crPng = cairo_create(surfacePng); + cairo_scale(crPng, dpiFactor, dpiFactor); + + cairo_translate(crPng, -selection->getOriginalXOnView(), -selection->getOriginalYOnView()); + + xoj::view::ElementContainerView view(this->selection); + view.draw(xoj::view::Context::createDefault(crPng)); + + cairo_destroy(crPng); + + GdkPixbuf* image = gdk_pixbuf_get_from_surface(surfacePng, 0, 0, width, height); + + cairo_surface_destroy(surfacePng); + + ///////////////////////////////////////////////////////////////// + // prepare image contents: SVG + ///////////////////////////////////////////////////////////////// + + GString* svgString = g_string_sized_new(1048576); // 1MB + + cairo_surface_t* surfaceSVG = + cairo_svg_surface_create_for_stream(reinterpret_cast(svgWriteFunction), svgString, + selection->getWidth(), selection->getHeight()); + cairo_t* crSVG = cairo_create(surfaceSVG); + + cairo_translate(crSVG, -selection->getOriginalXOnView(), -selection->getOriginalYOnView()); + view.draw(xoj::view::Context::createDefault(crSVG)); + + cairo_surface_destroy(surfaceSVG); + cairo_destroy(crSVG); + + ///////////////////////////////////////////////////////////////// + // copy to clipboard + ///////////////////////////////////////////////////////////////// + + GtkTargetList* list = gtk_target_list_new(nullptr, 0); + GtkTargetEntry* targets = nullptr; + int n_targets = 0; + + // if we have text elements... + if (!text.empty()) { + gtk_target_list_add_text_targets(list, 0); + } + // we always copy an image to clipboard + gtk_target_list_add_image_targets(list, 0, true); + gtk_target_list_add(list, atomSvg1, 0, 0); + gtk_target_list_add(list, atomSvg2, 0, 0); + gtk_target_list_add(list, atomXournal, 0, 0); + + targets = gtk_target_table_new_from_list(list, &n_targets); + + auto* contents = new ClipboardContents(text, image, svgString->str, out.stealData()); + + gtk_clipboard_set_with_data(this->clipboard, targets, static_cast(n_targets), + reinterpret_cast(ClipboardContents::getFunction), + reinterpret_cast(ClipboardContents::clearFunction), contents); + gtk_clipboard_set_can_store(this->clipboard, nullptr, 0); + + gtk_target_table_free(targets, n_targets); + gtk_target_list_unref(list); + + g_string_free(svgString, true); + + return true; +} + +void ClipboardHandler::setSelection(EditSelection* selection) { + this->selection = selection; + + this->listener->clipboardCutCopyEnabled(selection != nullptr); +} + +void ClipboardHandler::setCopyCutEnabled(bool enabled) { + if (enabled) { + listener->clipboardCutCopyEnabled(true); + } else if (!selection) { + listener->clipboardCutCopyEnabled(false); + } +} + +void ClipboardHandler::ownerChangedCallback(GtkClipboard* clip, GdkEvent* event, ClipboardHandler* handler) { + if (gdk_event_get_event_type(event) == GDK_OWNER_CHANGE) { + handler->clipboardUpdated(event->owner_change.selection); + } +} + +void ClipboardHandler::clipboardUpdated(GdkAtom atom) { + gtk_clipboard_request_contents(clipboard, gdk_atom_intern_static_string("TARGETS"), + reinterpret_cast(receivedClipboardContents), this); +} + +void ClipboardHandler::pasteClipboardImage(GtkClipboard* clipboard, GdkPixbuf* pixbuf, ClipboardHandler* handler) { + if (pixbuf) { + handler->listener->clipboardPasteImage(pixbuf); + } else { + g_warning("Trying to paste image, but pixbuf is null"); + } +} + +void ClipboardHandler::pasteClipboardContents(GtkClipboard* clipboard, GtkSelectionData* selectionData, + ClipboardHandler* handler) { + ObjectInputStream in; + + if (in.read(reinterpret_cast(gtk_selection_data_get_data(selectionData)), + as_unsigned(gtk_selection_data_get_length(selectionData)))) { + handler->listener->clipboardPasteXournal(in); + } +} + +void ClipboardHandler::pasteClipboardText(GtkClipboard* clipboard, const gchar* text, ClipboardHandler* handler) { + if (text) { + handler->listener->clipboardPasteText(text); + } +} + +auto gtk_selection_data_targets_include_xournal(GtkSelectionData* selection_data) -> gboolean { + GdkAtom* targets = nullptr; + gint n_targets = 0; + gboolean result = false; + + if (gtk_selection_data_get_targets(selection_data, &targets, &n_targets)) { + for (int i = 0; i < n_targets; i++) { + if (targets[i] == atomXournal) { + result = true; + break; + } + } + g_free(targets); + } + + return result; +} + +void ClipboardHandler::receivedClipboardContents(GtkClipboard* clipboard, GtkSelectionData* selectionData, + ClipboardHandler* handler) { + handler->containsText = gtk_selection_data_targets_include_text(selectionData); + handler->containsXournal = gtk_selection_data_targets_include_xournal(selectionData); + handler->containsImage = gtk_selection_data_targets_include_image(selectionData, false); + + handler->listener->clipboardPasteEnabled(handler->containsText || handler->containsXournal || + handler->containsImage); +} diff --git a/entry/src/main/cpp/src/core/control/ClipboardHandler.h b/entry/src/main/cpp/src/core/control/ClipboardHandler.h new file mode 100644 index 0000000..cf88250 --- /dev/null +++ b/entry/src/main/cpp/src/core/control/ClipboardHandler.h @@ -0,0 +1,73 @@ +/* + * Xournal++ + * + * The main Control + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include // for numeric_limits +#include // for string + +#include // for GdkPixbuf +#include // for GdkAtom, GdkEvent +#include // for gchar, gulong +#include // for GtkClipboard, GtkSelectionData + +class ObjectInputStream; +class EditSelection; + +class ClipboardListener { +public: + virtual void clipboardCutCopyEnabled(bool enabled) = 0; + virtual void clipboardPasteEnabled(bool enabled) = 0; + virtual void clipboardPasteText(std::string text) = 0; + virtual void clipboardPasteImage(GdkPixbuf* img) = 0; + virtual void clipboardPasteXournal(ObjectInputStream& in) = 0; + virtual void deleteSelection() = 0; + + virtual ~ClipboardListener(); +}; + +class ClipboardHandler { +public: + ClipboardHandler(ClipboardListener* listener, GtkWidget* widget); + virtual ~ClipboardHandler(); + +public: + bool paste(); + bool cut(); + bool copy(); + + void setSelection(EditSelection* selection); + + void setCopyCutEnabled(bool enabled); + +private: + static void ownerChangedCallback(GtkClipboard* clip, GdkEvent* event, ClipboardHandler* handler); + void clipboardUpdated(GdkAtom atom); + static void receivedClipboardContents(GtkClipboard* clipboard, GtkSelectionData* selectionData, + ClipboardHandler* handler); + + static void pasteClipboardContents(GtkClipboard* clipboard, GtkSelectionData* selectionData, + ClipboardHandler* handler); + static void pasteClipboardImage(GtkClipboard* clipboard, GdkPixbuf* pixbuf, ClipboardHandler* handler); + + static void pasteClipboardText(GtkClipboard* clipboard, const gchar* text, ClipboardHandler* handler); + +private: + ClipboardListener* listener = nullptr; + GtkClipboard* clipboard = nullptr; + gulong handlerId = std::numeric_limits::max(); + + EditSelection* selection = nullptr; + + bool containsText = false; + bool containsXournal = false; + bool containsImage = false; +}; diff --git a/entry/src/main/cpp/src/core/control/CompassController.cpp b/entry/src/main/cpp/src/core/control/CompassController.cpp new file mode 100644 index 0000000..c6d7c63 --- /dev/null +++ b/entry/src/main/cpp/src/core/control/CompassController.cpp @@ -0,0 +1,125 @@ +#include "CompassController.h" + +#include "control/Control.h" +#include "control/layer/LayerController.h" +#include "gui/XournalView.h" +#include "model/Compass.h" +#include "model/GeometryTool.h" +#include "model/Stroke.h" +#include "model/XojPage.h" + +CompassController::CompassController(XojPageView* view, Compass* compass): GeometryToolController(view, compass) {} + +CompassController::~CompassController() = default; + +auto CompassController::getType() const -> GeometryToolType { return GeometryToolType::COMPASS; } + +auto CompassController::posRelToSide(double x, double y) const -> xoj::util::Point { + cairo_matrix_t inv = geometryTool->getMatrix(); + cairo_matrix_invert(&inv); + cairo_matrix_transform_point(&inv, &x, &y); + return xoj::util::Point(x, -y); +} + +auto CompassController::isInsideGeometryTool(double x, double y, double border) const -> bool { + const xoj::util::Point p = posRelToSide(x, y); + return std::hypot(p.x, p.y) <= geometryTool->getHeight() + border; +} + +auto CompassController::getPointForAngle(double a) const -> xoj::util::Point { + cairo_matrix_t matrix = geometryTool->getMatrix(); + double x = geometryTool->getHeight() * std::cos(a); + double y = geometryTool->getHeight() * std::sin(a); + cairo_matrix_transform_point(&matrix, &x, &y); + + return xoj::util::Point(x, y); +} + +auto CompassController::getPointForRadius(double r) const -> xoj::util::Point { + cairo_matrix_t matrix = geometryTool->getMatrix(); + double x = r; + double y = 0.; + cairo_matrix_transform_point(&matrix, &x, &y); + + return xoj::util::Point(x, y); +} + +void CompassController::createOutlineStroke(double a) { + if (!std::isnan(a)) { + angleMax = a; + angleMin = a; + + const xoj::util::Point p = this->getPointForAngle(a); + initializeStroke(); + stroke->addPoint(Point(p.x, p.y)); + stroke->addPoint(Point(p.x, p.y)); // doubled point + geometryTool->notify(); + } else { + g_warning("No valid stroke from compass!"); + } +} + +void CompassController::createRadialStroke(double x) { + if (!std::isnan(x)) { + radiusMax = x; + radiusMin = x; + + const xoj::util::Point p = posRelToSide(x, 0.); + initializeStroke(); + stroke->addPoint(Point(p.x, p.y)); + stroke->addPoint(Point(p.x, p.y)); // doubled point + geometryTool->notify(); + } else { + g_warning("No valid radius from compass!"); + } +} + +void CompassController::updateOutlineStroke(double x) { + angleMax = std::max(this->angleMax, x); + angleMin = std::min(this->angleMin, x); + stroke->deletePointsFrom(0); + const auto h = view->getXournal()->getControl()->getToolHandler(); + const bool filled = (h->getFill() != -1); + const xoj::util::Point& c = this->getGeometryTool()->getOrigin(); + + if (filled && angleMax < angleMin + 2 * M_PI) { + stroke->addPoint(Point(c.x, c.y)); + } + for (auto i = 0; i <= 100; i++) { + const xoj::util::Point p = + getPointForAngle(angleMin + static_cast(i) / 100.0 * std::min(angleMax - angleMin, 2 * M_PI)); + stroke->addPoint(Point(p.x, p.y)); + } + if (filled && angleMax < angleMin + 2 * M_PI) { + stroke->addPoint(Point(c.x, c.y)); + } + geometryTool->notify(); +} + +void CompassController::updateRadialStroke(double x) { + radiusMax = std::max(this->radiusMax, x); + radiusMin = std::min(this->radiusMin, x); + stroke->deletePointsFrom(0); + const xoj::util::Point p1 = getPointForRadius(radiusMin); + const xoj::util::Point p2 = getPointForRadius(radiusMax); + + stroke->addPoint(Point(p1.x, p1.y)); + stroke->addPoint(Point(p2.x, p2.y)); + geometryTool->notify(); +} + +void CompassController::finalizeOutlineStroke() { + angleMax = std::numeric_limits::lowest(); + angleMin = std::numeric_limits::max(); + addStrokeToLayer(); +} + +void CompassController::finalizeRadialStroke() { + radiusMax = std::numeric_limits::lowest(); + radiusMin = std::numeric_limits::max(); + addStrokeToLayer(); +} + +auto CompassController::existsOutlineStroke() -> bool { return angleMax != std::numeric_limits::lowest(); } + +auto CompassController::existsRadialStroke() -> bool { return radiusMax != std::numeric_limits::lowest(); } diff --git a/entry/src/main/cpp/src/core/control/CompassController.h b/entry/src/main/cpp/src/core/control/CompassController.h new file mode 100644 index 0000000..9256866 --- /dev/null +++ b/entry/src/main/cpp/src/core/control/CompassController.h @@ -0,0 +1,136 @@ +/* + * Xournal++ + * + * A compass controller + * + * @author Xournal++ Team + * https://github.com/xournalpp/xournalpp + * + * @license GNU GPLv2 or later + */ + +#pragma once + +#include + +#include "gui/PageView.h" +#include "model/GeometryTool.h" +#include "util/Point.h" + +#include "GeometryToolController.h" + +class Compass; +class Stroke; + +/** + * @brief A class that controls a compass + + * The compass can be moved, rotated and scaled + * There are methods for translating coordinates + * and methods to deal with the temporary stroke + * that is displayed near the the outline of the + * compass or the marked radius. + */ + +class CompassController: public GeometryToolController { +public: + CompassController(XojPageView* view, Compass* compass); + ~CompassController() override; + +public: + GeometryToolType getType() const override; + + /** + * @brief returns the position of a point relative to a coordinate system, in which the + * positive x-axis coincides with the distinguished compass axis and the origin lies + * in the center of the compass + * @param x the x-coordinate of the point (in document coordinates) + * @param y the y-coordinate of the point (in document coordinates) + */ + xoj::util::Point posRelToSide(double x, double y) const; + + /** + * @brief checks whether a point with given coordinates lies in the geometry tool with an additional + * border enlarging (or shrinked) it + * @param x the x-coordinate of the given point (in document coordinates) + * @param y the y-coordinate of the given point (in document coordinates) + * @param border the size of the border (if negative, the geometry tool is shrinked via the border) + */ + bool isInsideGeometryTool(double x, double y, double border = 0.0) const override; + + /** + * @brief the point (in document coordinates) for a given angle on the outline of the compass + * @param a the angle with respect to the distinguished compass axis of the point + */ + xoj::util::Point getPointForAngle(double a) const; + + /** + * @brief the point (in document coordinates) for a given radius on the marked radius of the compass + * @param r the x-coordinate with respect to a coordinate system, in which the positive x-axis + * coincides with the marked radius and the origin lies in the center of the compass + */ + xoj::util::Point getPointForRadius(double r) const; + + /** + * @brief creates a stroke starting at the given angle of the outline of the compass + * @param a the angle of the point on the outline of the compass (when unrotated and untranslated) + */ + void createOutlineStroke(double a); + + /** + * @brief updates the stroke near the outline of the compass + * @param a the angle of the point on the outline of the compass (when unrotated and untranslated) + * updating the stroke + */ + void updateOutlineStroke(double a); + + /** + * @brief finishes the stroke near the outline of the compass + */ + void finalizeOutlineStroke(); + + /** + * @brief finishes the stroke aligned to the marked radius of the compass + */ + void finalizeRadialStroke(); + + /** + * @brief creates a stroke starting at the given point of the marked radius of the compass + * @param x the x-coordinate with respect to the marked radius + */ + void createRadialStroke(double x); + + /** + * @brief updates the stroke near the marked radius of the compass + * @param x the x-coordinate with respect to the marked radius + */ + void updateRadialStroke(double x); + + /** + * checks whether a stroke near the outline already exists + */ + bool existsOutlineStroke(); + + /** + * checks whether a radius already exists + */ + bool existsRadialStroke(); + +private: + /** + * @brief when a stroke near the radius with the measuring marks + * is drawn, the minimal and maximal radii are saved in + * the variables radiusMax and radiusMin + * + */ + double radiusMax = std::numeric_limits::lowest(); + double radiusMin = std::numeric_limits::max(); + + /** + * @brief when a stroke near the outline of the compass is drawn, the minimal and maximal + * angles of the point to be drawn (with respect to an unrotated, and untranslated coordinate system) + * are saved in the variables angleMin and angleMax + */ + double angleMax = std::numeric_limits::lowest(); + double angleMin = std::numeric_limits::max(); +}; diff --git a/entry/src/main/cpp/src/core/control/Control.cpp b/entry/src/main/cpp/src/core/control/Control.cpp new file mode 100644 index 0000000..68919cf --- /dev/null +++ b/entry/src/main/cpp/src/core/control/Control.cpp @@ -0,0 +1,2612 @@ +#include "Control.h" + +#include // for max +#include // for size_t +#include // for exce... +#include // for bind +#include // for end +#include // for make... +#include // for opti... +#include // for regex +#include // for string +#include // for move + +#include "control/AudioController.h" // for Audi... +#include "control/ClipboardHandler.h" // for Clip... +#include "control/CompassController.h" // for Comp... +#include "control/RecentManager.h" // for Rece... +#include "control/ScrollHandler.h" // for Scro... +#include "control/SetsquareController.h" // for Sets... +#include "control/Tool.h" // for Tool +#include "control/ToolHandler.h" // for Tool... +#include "control/actions/ActionDatabase.h" // for Acti... +#include "control/jobs/AutosaveJob.h" // for Auto... +#include "control/jobs/BaseExportJob.h" // for Base... +#include "control/jobs/CustomExportJob.h" // for Cust... +#include "control/jobs/PdfExportJob.h" // for PdfE... +#include "control/jobs/SaveJob.h" // for SaveJob +#include "control/jobs/Scheduler.h" // for JOB_... +#include "control/jobs/XournalScheduler.h" // for Xour... +#include "control/layer/LayerController.h" // for Laye... +#include "control/pagetype/PageTypeHandler.h" // for Page... +#include "control/settings/ButtonConfig.h" // for Butt... +#include "control/settings/MetadataManager.h" // for Meta... +#include "control/settings/PageTemplateSettings.h" // for Page... +#include "control/settings/Settings.h" // for Sett... +#include "control/settings/SettingsEnums.h" // for Button +#include "control/settings/ViewModes.h" // for ViewM.. +#include "control/tools/TextEditor.h" // for Text... +#include "control/xojfile/LoadHandler.h" // for Load... +#include "control/zoom/ZoomControl.h" // for Zoom... +#include "gui/FloatingToolbox.h" // for Floa... +#include "gui/MainWindow.h" // for Main... +#include "gui/PageView.h" // for XojP... +#include "gui/PdfFloatingToolbox.h" // for PdfF... +#include "gui/SearchBar.h" // for Sear... +#include "gui/XournalView.h" // for Xour... +#include "gui/XournalppCursor.h" // for Xour... +#include "gui/dialog/AboutDialog.h" // for Abou... +#include "gui/dialog/FormatDialog.h" // for Form... +#include "gui/dialog/GotoDialog.h" // for Goto... +#include "gui/dialog/PageTemplateDialog.h" // for Page... +#include "gui/dialog/SelectBackgroundColorDialog.h" // for Sele... +#include "gui/dialog/SelectOpacityDialog.h" // for Opac... +#include "gui/dialog/SettingsDialog.h" // for Sett... +#include "gui/dialog/ToolbarManageDialog.h" // for Tool... +#include "gui/dialog/XojOpenDlg.h" // for XojO... +#include "gui/dialog/XojSaveDlg.h" // for XojS... +#include "gui/dialog/toolbarCustomize/ToolbarDragDropHandler.h" // for Tool... +#include "gui/inputdevices/CompassInputHandler.h" // for Comp... +#include "gui/inputdevices/GeometryToolInputHandler.h" // for Geom... +#include "gui/inputdevices/HandRecognition.h" // for Hand... +#include "gui/inputdevices/SetsquareInputHandler.h" // for Sets... +#include "gui/menus/menubar/Menubar.h" // for Menubar +#include "gui/sidebar/Sidebar.h" // for Sidebar +#include "gui/toolbarMenubar/ToolMenuHandler.h" // for Tool... +#include "gui/toolbarMenubar/model/ToolbarData.h" // for Tool... +#include "gui/toolbarMenubar/model/ToolbarModel.h" // for Tool... +#include "model/BackgroundImage.h" // for Back... +#include "model/Compass.h" // for Comp... +#include "model/Document.h" // for Docu... +#include "model/DocumentChangeType.h" // for DOCU... +#include "model/DocumentListener.h" // for Docu... +#include "model/Element.h" // for Element +#include "model/Font.h" // for XojFont +#include "model/Image.h" // for Image +#include "model/Layer.h" // for Layer +#include "model/LineStyle.h" // for Line... +#include "model/PageType.h" // for Page... +#include "model/Setsquare.h" // for Sets... +#include "model/Stroke.h" // for Stroke +#include "model/StrokeStyle.h" // for Stro... +#include "model/TexImage.h" // for TexI... +#include "model/Text.h" // for Text +#include "model/XojPage.h" // for XojPage +#include "pdf/base/XojPdfPage.h" // for XojP... +#include "plugin/PluginController.h" // for Plug... +#include "settings/RecolorParameters.h" // for RecolorParameters +#include "undo/AddUndoAction.h" // for AddU... +#include "undo/InsertDeletePageUndoAction.h" // for Inse... +#include "undo/InsertUndoAction.h" // for Inse... +#include "undo/MoveSelectionToLayerUndoAction.h" // for Move... +#include "undo/PageSizeChangeUndoAction.h" // for PageSizeChangeUndoAction +#include "undo/SwapUndoAction.h" // for SwapUndoAction +#include "undo/UndoAction.h" // for Undo... +#include "util/Assert.h" // for xoj_assert +#include "util/Color.h" // for oper... +#include "util/PathUtil.h" // for clea... +#include "util/PlaceholderString.h" // for Plac... +#include "util/PopupWindowWrapper.h" // for PopupWindowWrapper +#include "util/Stacktrace.h" // for Stac... +#include "util/StringUtils.h" // for char_cast +#include "util/Util.h" // for exec... +#include "util/XojMsgBox.h" // for XojM... +#include "util/glib_casts.h" // for wrap_v +#include "util/i18n.h" // for _, FS +#include "util/safe_casts.h" // for as_unsigned +#include "util/serializing/InputStreamException.h" // for Inpu... +#include "util/serializing/ObjectInputStream.h" // for Obje... +#include "view/CompassView.h" // for Comp... +#include "view/SetsquareView.h" // for Sets... +#include "view/overlays/OverlayView.h" // for Over... + +#include "CrashHandler.h" // for emer... +#include "LatexController.h" // for Late... +#include "PageBackgroundChangeController.h" // for Page... +#include "PrintHandler.h" // for print +#include "UndoRedoController.h" // for Undo... +#include "config-dev.h" // for SETT... +#include "config.h" // for PROJ... + +using std::string; + +Control::Control(GApplication* gtkApp, GladeSearchpath* gladeSearchPath, bool disableAudio): gtkApp(gtkApp) { + this->undoRedo = new UndoRedoHandler(this); + this->undoRedo->addUndoRedoListener(this); + this->isBlocking = false; + + this->gladeSearchPath = gladeSearchPath; + + this->metadata = new MetadataManager(); + this->cursor = new XournalppCursor(this); + + auto name = Util::getConfigFile(SETTINGS_XML_FILE); + this->settings = new Settings(std::move(name)); + this->settings->load(); + this->loadPaletteFromSettings(); + + this->pageTypes = new PageTypeHandler(gladeSearchPath); + +#ifdef ENABLE_AUDIO + if (!(disableAudio || this->settings->isAudioDisabled())) { + this->audioController = std::make_unique(this->settings, this); + } +#endif + + this->scrollHandler = new ScrollHandler(this); + + this->scheduler = new XournalScheduler(); + + this->doc = new Document(this); + + // for crashhandling + setEmergencyDocument(this->doc); + + + this->zoom = new ZoomControl(); + this->zoom->setZoomStep(this->settings->getZoomStep() / 100.0); + this->zoom->setZoomStepScroll(this->settings->getZoomStepScroll() / 100.0); + + this->toolHandler = new ToolHandler(this, this->actionDB.get(), this->settings); + this->toolHandler->loadSettings(); + this->initButtonTool(); + + /** + * This is needed to update the previews + */ + this->changeTimout = g_timeout_add_seconds(5, xoj::util::wrap_v, this); + + this->pageBackgroundChangeController = std::make_unique(this); + + this->layerController = new LayerController(this); + this->layerController->registerListener(this); + + this->pluginController = new PluginController(this); + this->pluginController->registerToolbar(); +} + +Control::~Control() { + g_source_remove(this->changeTimout); + this->enableAutosave(false); + + deleteLastAutosaveFile(); + this->scheduler->stop(); + this->changedPages.clear(); // can be removed, will be done by implicit destructor + + delete this->pluginController; + this->pluginController = nullptr; + delete this->clipboardHandler; + this->clipboardHandler = nullptr; + delete this->undoRedo; + this->undoRedo = nullptr; + delete this->settings; + this->settings = nullptr; + delete this->toolHandler; + this->toolHandler = nullptr; + delete this->sidebar; + this->sidebar = nullptr; + + setEmergencyDocument(nullptr); + delete this->doc; + this->doc = nullptr; + delete this->searchBar; + this->searchBar = nullptr; + delete this->scrollHandler; + this->scrollHandler = nullptr; + delete this->pageTypes; + this->pageTypes = nullptr; + delete this->metadata; + this->metadata = nullptr; + delete this->cursor; + this->cursor = nullptr; + delete this->zoom; + this->zoom = nullptr; + delete this->scheduler; + this->scheduler = nullptr; + delete this->dragDropHandler; + this->dragDropHandler = nullptr; + delete this->layerController; + this->layerController = nullptr; +} + +struct Control::MissingPdfData { + bool wasPdfAttached; + std::string missingFileName; +}; + +void Control::setLastAutosaveFile(fs::path newAutosaveFile) { + try { + if (!this->lastAutosaveFilename.empty() && !fs::equivalent(newAutosaveFile, this->lastAutosaveFilename) && + fs::exists(newAutosaveFile)) { + deleteLastAutosaveFile(); + } + } catch (const fs::filesystem_error& e) { + auto fmtstr = FS(_F("Filesystem error: {1}") % e.what()); + Util::execInUiThread([fmtstr, win = getGtkWindow()]() { XojMsgBox::showErrorToUser(win, fmtstr); }); + } + this->lastAutosaveFilename = std::move(newAutosaveFile); +} + +void Control::deleteLastAutosaveFile() { + try { + if (fs::exists(this->lastAutosaveFilename)) { + fs::remove(this->lastAutosaveFilename); + } + } catch (const fs::filesystem_error& e) { + auto fmtstr = FS(_F("Could not remove old autosave file \"{1}\": {2}") % this->lastAutosaveFilename.u8string() % + e.what()); + Util::execInUiThread([fmtstr, win = getGtkWindow()]() { XojMsgBox::showErrorToUser(win, fmtstr); }); + } + this->lastAutosaveFilename.clear(); +} + +auto Control::checkChangedDocument(Control* control) -> bool { + if (!control->doc->try_lock_shared()) { + // call again later + return true; + } + for (auto const& page: control->changedPages) { + auto p = control->doc->indexOf(page); + if (p != npos) { + for (DocumentListener* dl: control->changedDocumentListeners) { + dl->pageChanged(p); + } + } + } + control->changedPages.clear(); + control->doc->unlock_shared(); + + // Call again + return true; +} + +void Control::saveSettings() { + this->toolHandler->saveSettings(); + + gint width = 0; + gint height = 0; + gtk_window_get_size(getGtkWindow(), &width, &height); + + if (!this->win->isMaximized()) { + this->settings->setMainWndSize(width, height); + } + this->settings->setMainWndMaximized(this->win->isMaximized()); + + this->sidebar->saveSize(); +} + +void Control::initWindow(MainWindow* win) { + this->win = win; + + this->actionDB = std::make_unique(this); + + selectTool(toolHandler->getToolType()); + this->sidebar = new Sidebar(win, this); + + XojMsgBox::setDefaultWindow(getGtkWindow()); + + updatePageNumbers(0, npos); + + // toolHandler->eraserTypeChanged(); + + this->searchBar = new SearchBar(this); + + if (settings->isPresentationMode()) { + setViewPresentationMode(true); + } else if (settings->isViewFixedRows()) { + setViewRows(settings->getViewRows()); + } else { + setViewColumns(settings->getViewColumns()); + } + + setViewLayoutVert(settings->getViewLayoutVert()); + setViewLayoutR2L(settings->getViewLayoutR2L()); + setViewLayoutB2T(settings->getViewLayoutB2T()); + + setViewPairedPages(settings->isShowPairedPages()); + + toolLineStyleChanged(); + + this->clipboardHandler = new ClipboardHandler(this, win->getXournal()->getWidget()); + + this->enableAutosave(settings->isAutosaveEnabled()); +} + +auto Control::autosaveCallback(Control* control) -> bool { + if (!control->undoRedo->isChangedAutosave()) { + // do nothing, nothing changed + return true; + } + + auto* job = new AutosaveJob(control); + control->scheduler->addJob(job, JOB_PRIORITY_NONE); + job->unref(); + + return true; +} + +void Control::enableAutosave(bool enable) { + if (this->autosaveTimeout) { + g_source_remove(this->autosaveTimeout); + this->autosaveTimeout = 0; + } + + if (enable) { + auto timeout = guint(settings->getAutosaveTimeout()) * 60U; + this->autosaveTimeout = g_timeout_add_seconds(timeout, xoj::util::wrap_v, this); + } +} + +void Control::updatePageNumbers(size_t page, size_t pdfPage) { + if (this->win == nullptr) { + return; + } + + this->win->updatePageNumbers(page, this->doc->getPageCount(), pdfPage); + this->sidebar->selectPageNr(page, pdfPage); + + this->metadata->storeMetadata(this->doc->getEvMetadataFilename(), static_cast(page), + getZoomControl()->getZoomReal()); + if (settings->isPageNumberInTitlebarShown()) { + this->updateWindowTitle(); + } + + auto current = getCurrentPageNo(); + auto count = this->doc->getPageCount(); + + this->actionDB->enableAction(Action::GOTO_FIRST, current != 0); + this->actionDB->enableAction(Action::GOTO_PREVIOUS, current != 0); + this->actionDB->enableAction(Action::GOTO_PREVIOUS_ANNOTATED_PAGE, current != 0); + this->actionDB->enableAction(Action::GOTO_PAGE, count > 1); + this->actionDB->enableAction(Action::GOTO_NEXT, current < count - 1); + this->actionDB->enableAction(Action::GOTO_LAST, current < count - 1); + this->actionDB->enableAction(Action::GOTO_NEXT_ANNOTATED_PAGE, current < count - 1); +} + +bool Control::toggleCompass() { + return toggleGeometryTool(); +} +bool Control::toggleSetsquare() { + return toggleGeometryTool(); +} + +template +bool Control::toggleGeometryTool() { + bool needsNewGeometryTool = !this->geometryToolController || this->geometryToolController->getType() != toolType; + this->resetGeometryTool(); + if (!needsNewGeometryTool) { + return false; + } + const auto view = this->win->getXournal()->getViewFor(getCurrentPageNo()); + const auto* xournal = GTK_XOURNAL(this->win->getXournal()->getWidget()); + auto tool = new ToolClass(); + view->addOverlayView(std::make_unique(tool, view, zoom)); + this->geometryTool = std::unique_ptr(tool); + this->geometryToolController = std::make_unique(view, tool); + auto geometryToolInputHandler = + std::make_unique(this->win->getXournal(), geometryToolController.get()); + Range range = view->getVisiblePart(); + if (range.isValid()) { + double originX = (range.minX + range.maxX) * .5; + double originY = (range.minY + range.maxY) * .5; + geometryToolController->translate({originX, originY}); + } else { + geometryToolController->translate({view->getWidth() * .5, view->getHeight() * .5}); + } + xournal->input->setGeometryToolInputHandler(std::move(geometryToolInputHandler)); + geometryTool->notify(); + return true; +} + +void Control::resetGeometryTool() { + this->geometryToolController.reset(); + this->geometryTool.reset(); + auto* xournal = GTK_XOURNAL(this->win->getXournal()->getWidget()); + xournal->input->resetGeometryToolInputHandler(); +} + +void Control::showFloatingToolbox(int x, int y) { + GtkWidget* mainWindow = GTK_WIDGET(this->getGtkWindow()); + GtkWidget* mainBox = this->getWindow()->get("mainBox"); + + gint mainBoxX, mainBoxY; + gtk_widget_translate_coordinates(mainWindow, mainBox, x, y, &mainBoxX, &mainBoxY); + + this->getWindow()->getFloatingToolbox()->show(mainBoxX, mainBoxY); +} + +auto Control::copy() -> bool { + if (this->win && this->win->getXournal()->copy()) { + return true; + } + return this->clipboardHandler->copy(); +} + +auto Control::cut() -> bool { + if (this->win && this->win->getXournal()->cut()) { + return true; + } + return this->clipboardHandler->cut(); +} + +auto Control::paste() -> bool { + if (this->win && this->win->getXournal()->paste()) { + return true; + } + return this->clipboardHandler->paste(); +} + +void Control::selectAlpha(OpacityFeature feature) { + int alpha = 0; + + switch (feature) { + case OPACITY_FILL_PEN: + alpha = this->toolHandler->getPenFill(); + break; + case OPACITY_FILL_HIGHLIGHTER: + alpha = this->toolHandler->getHighlighterFill(); + break; + case OPACITY_SELECT_PDF_TEXT_MARKER: + alpha = this->toolHandler->getSelectPDFTextMarkerOpacity(); + break; + default: + g_warning("Unhandled OpacityFeature for selectAlpha event: %s", opacityFeatureToString(feature).data()); + Stacktrace::printStacktrace(); + break; + } + auto dlg = xoj::popup::PopupWindowWrapper( + gladeSearchPath, alpha, feature, [&th = *toolHandler](int alpha, OpacityFeature feature) { + switch (feature) { + case OPACITY_FILL_PEN: + th.setPenFill(alpha); + break; + case OPACITY_FILL_HIGHLIGHTER: + th.setHighlighterFill(alpha); + break; + case OPACITY_SELECT_PDF_TEXT_MARKER: + th.setSelectPDFTextMarkerOpacity(alpha); + break; + default: + g_warning("Unhandled OpacityFeature for callback of SelectOpacityDialog: %s", + opacityFeatureToString(feature).data()); + Stacktrace::printStacktrace(); + break; + } + }); + dlg.show(getGtkWindow()); +} + +void Control::clearSelectionEndText() { + clearSelection(); + if (win) { + win->getXournal()->endTextAllPages(); + } +} + +void Control::selectAllOnPage() { + auto pageNr = getCurrentPageNo(); + if (pageNr == npos) { + return; + } + + win->getXournal()->clearSelection(); + + this->doc->lock(); + XojPageView* view = win->getXournal()->getViewFor(pageNr); + if (view == nullptr) { + this->doc->unlock(); + return; + } + + PageRef page = this->doc->getPage(pageNr); + Layer* layer = page->getSelectedLayer(); + + auto elements = layer->clearNoFree(); + this->doc->unlock(); + + if (!elements.empty()) { + InsertionOrder insertionOrder; + insertionOrder.reserve(elements.size()); + Element::Index n = 0; + for (auto&& e: elements) { + insertionOrder.emplace_back(std::move(e), n++); + } + auto [sel, rg] = + SelectionFactory::createFromFloatingElements(this, page, layer, view, std::move(insertionOrder)); + + page->fireRangeChanged(rg); + + win->getXournal()->setSelection(sel.release()); + } +} + +void Control::reorderSelection(EditSelection::OrderChange change) { + EditSelection* sel = win->getXournal()->getSelection(); + if (!sel) { + return; + } + + auto undoAction = sel->rearrangeInsertionOrder(change); + this->undoRedo->addUndoAction(std::move(undoAction)); +} + +/** + * Fire page selected, but first check if the page Number is valid + * + * @return the page ID or size_t_npos if the page is not found + */ +auto Control::firePageSelected(const PageRef& page) -> size_t { + this->doc->lock_shared(); + size_t pageId = this->doc->indexOf(page); + this->doc->unlock_shared(); + if (pageId == npos) { + return npos; + } + + firePageSelected(pageId); + return pageId; +} + +void Control::firePageSelected(size_t page) { + if (page != this->getCurrentPageNo()) { + DocumentHandler::firePageSelected(page); + } +} + +void Control::manageToolbars() { + xoj::popup::PopupWindowWrapper dlg( + this->gladeSearchPath, this->win->getToolbarModel(), [win = this->win]() { + if (const auto& tbs = win->getToolbarModel()->getToolbars(); + std::none_of(tbs.begin(), tbs.end(), + [tb = win->getSelectedToolbar()](const auto& t) { return t.get() == tb; })) { + // The active toolbar has been deleted! + xoj_assert(!tbs.empty()); + win->toolbarSelected(tbs.front().get()); + XojMsgBox::showErrorToUser( + GTK_WINDOW(win->getWindow()), + _("You deleted the active toolbar. Falling back to the default toolbar.")); + } + + win->updateToolbarMenu(); + auto filepath = Util::getConfigFile(TOOLBAR_CONFIG); + win->getToolbarModel()->save(filepath); + }); + dlg.show(GTK_WINDOW(this->win->getWindow())); + + this->win->updateToolbarMenu(); + auto filepath = Util::getConfigFile(TOOLBAR_CONFIG); + this->win->getToolbarModel()->save(filepath); +} + +void Control::customizeToolbars() { + xoj_assert(this->win != nullptr); + + if (this->win->getSelectedToolbar()->isPredefined()) { + enum { YES = 1, NO }; + XojMsgBox::askQuestion(getGtkWindow(), + FC(_F("The Toolbarconfiguration \"{1}\" is predefined, " + "would you create a copy to edit?") % + this->win->getSelectedToolbar()->getName()), + std::string(), {{_("Yes"), YES}, {_("No"), NO}}, [ctrl = this](int response) { + if (response == YES) { + auto data = std::make_unique(*ctrl->win->getSelectedToolbar()); + ToolbarModel* model = ctrl->win->getToolbarModel(); + model->initCopyNameId(data.get()); + ctrl->win->toolbarSelected(model->add(std::move(data))); + ctrl->win->updateToolbarMenu(); + + xoj_assert(!ctrl->win->getSelectedToolbar()->isPredefined()); + ctrl->customizeToolbars(); + } + }); + } else { + if (!this->dragDropHandler) { + this->dragDropHandler = new ToolbarDragDropHandler(this); + } + this->dragDropHandler->configure(); + } +} + +void Control::setToolDrawingType(DrawingType type) { + if (this->toolHandler->getDrawingType() != type) { + + if (this->toolHandler->getDrawingType() == DRAWING_TYPE_SPLINE) { + // Shape changed from spline to something else: finish ongoing splines + if (win) { + win->getXournal()->endSplineAllPages(); + } + } + this->toolHandler->setDrawingType(type); + } +} + +void Control::setFullscreen(bool enabled) { + win->setFullscreen(enabled); + actionDB->setActionState(Action::FULLSCREEN, enabled); +} + +void Control::setShowSidebar(bool enabled) { + win->setSidebarVisible(enabled); + actionDB->setActionState(Action::SHOW_SIDEBAR, enabled); + + if (settings->isSidebarVisible() != enabled && + settings->getActiveViewMode() == PresetViewModeIds::VIEW_MODE_DEFAULT) { + settings->setSidebarVisible(enabled); + ViewMode viewMode = settings->getViewModes()[PresetViewModeIds::VIEW_MODE_DEFAULT]; + viewMode.showSidebar = enabled; + settings->setViewMode(PresetViewModeIds::VIEW_MODE_DEFAULT, viewMode); + } +} + +void Control::setShowToolbar(bool enabled) { + win->setToolbarVisible(enabled); + actionDB->setActionState(Action::SHOW_TOOLBAR, enabled); + + if (settings->isToolbarVisible() != enabled && + settings->getActiveViewMode() == PresetViewModeIds::VIEW_MODE_DEFAULT) { + settings->setToolbarVisible(enabled); + ViewMode viewMode = settings->getViewModes()[PresetViewModeIds::VIEW_MODE_DEFAULT]; + viewMode.showToolbar = enabled; + settings->setViewMode(PresetViewModeIds::VIEW_MODE_DEFAULT, viewMode); + } +} + +void Control::setShowMenubar(bool enabled) { + win->setMenubarVisible(enabled); + actionDB->setActionState(Action::SHOW_MENUBAR, enabled); +} + +void Control::disableSidebarTmp(bool disabled) { this->sidebar->setTmpDisabled(disabled); } + +void Control::addDefaultPage(const std::optional& pageTemplate, Document* doc) { + const std::string& templ = pageTemplate.value_or(settings->getPageTemplate()); + PageTemplateSettings model; + model.parse(templ); + + auto page = std::make_shared(model.getPageWidth(), model.getPageHeight()); + page->setBackgroundColor(model.getBackgroundColor()); + page->setBackgroundType(model.getBackgroundType()); + + if (doc == nullptr || doc == this->doc) { + // Apply to the curent document + this->doc->lock(); + this->doc->addPage(std::move(page)); + this->doc->unlock(); + } else { + doc->addPage(std::move(page)); + } +} + +void Control::updatePageActions() { + auto currentPage = getCurrentPageNo(); + auto nbPages = this->doc->getPageCount(); + this->actionDB->enableAction(Action::DELETE_PAGE, nbPages > 1); + this->actionDB->enableAction(Action::MOVE_PAGE_TOWARDS_BEGINNING, currentPage != 0); + this->actionDB->enableAction(Action::MOVE_PAGE_TOWARDS_END, currentPage < nbPages - 1); +} + +void Control::deletePage() { + clearSelectionEndText(); + + // if the current page contains the geometry tool, reset it + size_t pNr = getCurrentPageNo(); + if (geometryToolController) { + doc->lock_shared(); + auto page = doc->indexOf(geometryToolController->getPage()); + doc->unlock_shared(); + if (page == pNr) { + resetGeometryTool(); + } + } + // don't allow delete pages if we have less than 2 pages, + // so we can be (more or less) sure there is at least one page. + if (this->doc->getPageCount() < 2) { + return; + } + + if (pNr == npos || pNr > this->doc->getPageCount()) { + // something went wrong... + return; + } + + this->doc->lock_shared(); + PageRef page = doc->getPage(pNr); + this->doc->unlock_shared(); + + // first send event, then delete page... + firePageDeleted(pNr); + + this->doc->lock(); + doc->deletePage(pNr); + this->doc->unlock(); + + this->undoRedo->addUndoAction(std::make_unique(page, pNr, false)); + + if (pNr >= this->doc->getPageCount()) { + pNr = this->doc->getPageCount() - 1; + } + + scrollHandler->scrollToPage(pNr); + this->win->getXournal()->forceUpdatePagenumbers(); +} + +void Control::duplicatePage() { + auto page = getCurrentPage(); + if (!page) { + return; + } + auto pageCopy = std::make_shared(*page); + + insertPage(pageCopy, getCurrentPageNo() + 1); +} + +void Control::movePageTowardsBeginning() { + auto currentPageNo = this->getCurrentPageNo(); + if (currentPageNo < 1) { + g_warning("Control::movePageTowardsBeginning() called on the first page"); + return; + } + if (currentPageNo == npos) { + g_warning("Control::movePageTowardsBeginning() called with current page selected"); + return; + } + + auto lock = std::unique_lock(*this->doc); + PageRef page = this->doc->getPage(currentPageNo); + PageRef otherPage = doc->getPage(currentPageNo - 1); + if (!page || !otherPage) { + g_warning("Control::movePageTowardsBeginning() called but no page %zu or %zu", currentPageNo, + currentPageNo - 1); + return; + } + + doc->deletePage(currentPageNo); + doc->insertPage(page, currentPageNo - 1); + lock.unlock(); + + UndoRedoHandler* undo = this->getUndoRedoHandler(); + undo->addUndoAction(std::make_unique(currentPageNo - 1, true, page, otherPage)); + + this->firePageDeleted(currentPageNo); + this->firePageInserted(currentPageNo - 1); + this->firePageSelected(currentPageNo - 1); + + this->getScrollHandler()->scrollToPage(currentPageNo - 1); +} + + +void Control::movePageTowardsEnd() { + auto currentPageNo = this->getCurrentPageNo(); + if (currentPageNo == npos) { + g_warning("Control::movePageTowardsEnd() called with current page selected"); + return; + } + + auto lock = std::unique_lock(*this->doc); + auto nbPage = this->doc->getPageCount(); + if (currentPageNo >= nbPage - 1) { + g_warning("Control::movePageTowardsEnd() called on last page"); + return; + } + + PageRef page = this->doc->getPage(currentPageNo); + PageRef otherPage = doc->getPage(currentPageNo + 1); + if (!page || !otherPage) { + g_warning("Control::movePageTowardsEnd() called but no page %zu or %zu", currentPageNo, currentPageNo + 1); + return; + } + + doc->deletePage(currentPageNo); + doc->insertPage(page, currentPageNo + 1); + lock.unlock(); + + this->undoRedo->addUndoAction(std::make_unique(currentPageNo, false, page, otherPage)); + + this->firePageDeleted(currentPageNo); + this->firePageInserted(currentPageNo + 1); + this->firePageSelected(currentPageNo + 1); + + this->getScrollHandler()->scrollToPage(currentPageNo + 1); +} + +/// Remove mnemonic indicators in menu labels +static std::string removeMnemonics(std::string orig) { + std::regex reg("_(.)"); + return std::regex_replace(orig, reg, "$1"); +} + +void Control::askInsertPdfPage(size_t pdfPage) { + using Responses = enum { CANCEL = 1, AFTER = 2, END = 3 }; + std::vector buttons = {{_("Cancel"), Responses::CANCEL}, + {_("Insert after current page"), Responses::AFTER}, + {_("Insert at end"), Responses::END}}; + + // Must match the labels in main.glade and PageTypeHandler.cpp + std::string pathToMenuEntry = removeMnemonics(_("_Journal") + std::string(" → ") + _("Paper B_ackground") + + std::string(" → ") + _("With PDF background")); + + XojMsgBox::askQuestion(this->getGtkWindow(), + FC(_F("Your current document does not contain PDF Page no {1}\n" + "Would you like to insert this page?\n\n" + "Tip: You can select {2} to insert a PDF page.") % + static_cast(pdfPage + 1) % pathToMenuEntry), + "", buttons, [ctrl = this, pdfPage](int response) { + if (response == Responses::AFTER || response == Responses::END) { + Document* doc = ctrl->getDocument(); + + doc->lock_shared(); + size_t position = response == Responses::AFTER ? ctrl->getCurrentPageNo() + 1 : + doc->getPageCount(); + XojPdfPageSPtr pdf = doc->getPdfPage(pdfPage); + doc->unlock_shared(); + + if (pdf) { + auto page = std::make_shared(pdf->getWidth(), pdf->getHeight()); + page->setBackgroundPdfPageNr(pdfPage); + ctrl->insertPage(page, position); + } + } + }); +} + +void Control::insertNewPage(size_t position, bool automatedInsertion) { + pageBackgroundChangeController->insertNewPage(position, automatedInsertion); +} + +void Control::appendNewPdfPages() { + auto pageCount = this->doc->getPageCount(); + // find last page with pdf background and get its pdf page number + auto currentPdfPageCount = [&]() { + for (size_t i = pageCount; i != 0; --i) { + if (auto page = doc->getPage(i - 1); page && page->getBackgroundType().isPdfPage()) { + return page->getPdfPageNr() + 1; + } + } + return size_t{0U}; + }(); + + auto pdfPageCount = this->doc->getPdfPageCount(); + auto insertCount = pdfPageCount - currentPdfPageCount; + + if (insertCount == 0) { + string msg = FS(_F("No pdf pages available to append. You may need to reopen the document first.")); + XojMsgBox::showErrorToUser(getGtkWindow(), msg); + } + for (size_t i = 0; i != insertCount; ++i) { + + doc->lock_shared(); + XojPdfPageSPtr pdf = doc->getPdfPage(currentPdfPageCount + i); + doc->unlock_shared(); + + if (pdf) { + auto newPage = std::make_shared(pdf->getWidth(), pdf->getHeight()); + newPage->setBackgroundPdfPageNr(currentPdfPageCount + i); + insertPage(newPage, pageCount + i); + } else { + string msg = FS(_F("Unable to retrieve pdf page.")); // should not happen + XojMsgBox::showErrorToUser(getGtkWindow(), msg); + } + } +} + +void Control::insertPage(const PageRef& page, size_t position, bool shouldScrollToPage) { + this->doc->lock(); + this->doc->insertPage(page, position); // insert the new page to the document and update page numbers + this->doc->unlock(); + undoRedo->addUndoAction(std::make_unique(page, position, true)); + + // notify document listeners about the inserted page; this creates the new XojViewPage, recalculates the layout + // and creates a preview page in the sidebar + firePageInserted(position); + + getCursor()->updateCursor(); + + // make the inserted page fully visible (or at least as much from the top which fits on the screen), + // and make the page appear selected + if (shouldScrollToPage) { + scrollHandler->scrollToPage(position); + firePageSelected(position); + } + + updatePageActions(); +} + +void Control::gotoPage() { + auto popup = xoj::popup::PopupWindowWrapper( + this->gladeSearchPath, this->getCurrentPageNo(), this->doc->getPageCount(), + [scroll = this->scrollHandler](size_t pageNumber) { + xoj_assert(pageNumber != 0); + scroll->scrollToPage(pageNumber - 1); + }); + popup.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::updateBackgroundSizeButton() { + if (this->win == nullptr) { + return; + } + + // Update paper color button + auto const& p = getCurrentPage(); + if (!p || this->win == nullptr) { + return; + } + + PageType bg = p->getBackgroundType(); + this->actionDB->enableAction(Action::PAPER_BACKGROUND_COLOR, !bg.isSpecial()); + // PDF page size is defined, you cannot change it + this->actionDB->enableAction(Action::PAPER_FORMAT, !bg.isPdfPage()); +} + +void Control::paperTemplate() { + auto popup = xoj::popup::PopupWindowWrapper(this->gladeSearchPath, settings, + win->getToolMenuHandler(), pageTypes); + popup.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::paperFormat() { + auto const& page = getCurrentPage(); + if (!page || page->getBackgroundType().isPdfPage()) { + return; + } + clearSelectionEndText(); + + auto popup = xoj::popup::PopupWindowWrapper( + this->gladeSearchPath, settings, page->getWidth(), page->getHeight(), + [ctrl = this, page](double width, double height) { + ctrl->doc->lock(); + double oldW = page->getWidth(); + double oldH = page->getHeight(); + Document::setPageSize(page, width, height); + size_t pageNo = ctrl->doc->indexOf(page); + size_t pageCount = ctrl->doc->getPageCount(); + ctrl->doc->unlock(); + ctrl->undoRedo->addUndoAction(std::make_unique(page, oldW, oldH)); + if (pageNo != npos && pageNo < pageCount) { + ctrl->firePageSizeChanged(pageNo); + } + }); + popup.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::changePageBackgroundColor() { + auto pNr = getCurrentPageNo(); + this->doc->lock_shared(); + auto const& p = this->doc->getPage(pNr); + this->doc->unlock_shared(); + + if (!p) { + return; + } + + clearSelectionEndText(); + + PageType bg = p->getBackgroundType(); + if (bg.isSpecial()) { + return; + } + + xoj::popup::PopupWindowWrapper dlg(this, [p, pNr, ctrl = this](Color color) { + p->setBackgroundColor(color); + ctrl->firePageChanged(pNr); + }); + dlg.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::setViewPairedPages(bool enabled) { + settings->setShowPairedPages(enabled); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewFullscreenMode(bool enabled) { + if (enabled) { + this->loadViewMode(VIEW_MODE_FULLSCREEN); + } else { + this->loadViewMode(VIEW_MODE_DEFAULT); + } +} + +void Control::setViewPresentationMode(bool enabled) { + if (enabled) { + this->loadViewMode(VIEW_MODE_PRESENTATION); + + bool success = zoom->updateZoomPresentationValue(); + if (!success) { + g_warning("Error calculating zoom value"); + return; + } + } else { + this->loadViewMode(VIEW_MODE_DEFAULT); + + if (settings->isViewFixedRows()) { + setViewRows(settings->getViewRows()); + } else { + setViewColumns(settings->getViewColumns()); + } + + setViewLayoutVert(settings->getViewLayoutVert()); + setViewLayoutR2L(settings->getViewLayoutR2L()); + setViewLayoutB2T(settings->getViewLayoutB2T()); + } + zoom->setZoomPresentationMode(enabled); + settings->setPresentationMode(enabled); + + // Disable Zoom + this->actionDB->enableAction(Action::ZOOM_IN, !enabled); + this->actionDB->enableAction(Action::ZOOM_OUT, !enabled); + this->actionDB->enableAction(Action::ZOOM_FIT, !enabled); + this->actionDB->enableAction(Action::ZOOM_100, !enabled); + this->actionDB->enableAction(Action::ZOOM, !enabled); + + // TODO Figure out how to replace this + // fireEnableAction(ACTION_FOOTER_ZOOM_SLIDER, !enabled); + + this->actionDB->enableAction(Action::SET_LAYOUT_BOTTOM_TO_TOP, !enabled); + this->actionDB->enableAction(Action::SET_LAYOUT_RIGHT_TO_LEFT, !enabled); + this->actionDB->enableAction(Action::SET_LAYOUT_VERTICAL, !enabled); + this->actionDB->enableAction(Action::SET_COLUMNS_OR_ROWS, !enabled); + + // disable selection of scroll hand tool + // TODO Figure out how to replace this + // fireEnableAction(ACTION_TOOL_HAND, !enabled); + + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setPairsOffset(int numOffset) { + settings->setPairsOffset(numOffset); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewColumns(int numColumns) { + settings->setViewColumns(numColumns); + settings->setViewFixedRows(false); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewRows(int numRows) { + settings->setViewRows(numRows); + settings->setViewFixedRows(true); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewLayoutVert(bool vert) { + settings->setViewLayoutVert(vert); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewLayoutR2L(bool r2l) { + settings->setViewLayoutR2L(r2l); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +void Control::setViewLayoutB2T(bool b2t) { + settings->setViewLayoutB2T(b2t); + win->getXournal()->layoutPages(); + scrollHandler->scrollToPage(getCurrentPageNo()); +} + +auto Control::getCurrentPageNo() const -> size_t { + if (this->win) { + return this->win->getXournal()->getCurrentPage(); + } + return 0; +} + +auto Control::searchTextOnPage(const std::string& text, size_t pageNumber, size_t index, size_t* occurrences, + XojPdfRectangle* matchRect) -> bool { + return getWindow()->getXournal()->searchTextOnPage(text, pageNumber, index, occurrences, matchRect); +} + +auto Control::getCurrentPage() -> PageRef { + this->doc->lock_shared(); + PageRef p = this->doc->getPage(getCurrentPageNo()); + this->doc->unlock_shared(); + + return p; +} + +void Control::undoRedoChanged() { + this->actionDB->enableAction(Action::UNDO, undoRedo->canUndo()); + this->actionDB->enableAction(Action::REDO, undoRedo->canRedo()); + + win->setUndoDescription(undoRedo->undoDescription()); + win->setRedoDescription(undoRedo->redoDescription()); + + updateWindowTitle(); +} + +void Control::undoRedoPageChanged(PageRef page) { + if (std::find(begin(this->changedPages), end(this->changedPages), page) == end(this->changedPages)) { + this->changedPages.emplace_back(std::move(page)); + } +} + +void Control::selectTool(ToolType type) { + // keep text-selection when switching from text to seletion tool + auto oldTool = getToolHandler()->getActiveTool(); + if (oldTool && win && isSelectToolType(type) && oldTool->getToolType() == ToolType::TOOL_TEXT && + this->win->getXournal()->getTextEditor() && !(this->win->getXournal()->getTextEditor()->bufferEmpty())) { + auto xournal = this->win->getXournal(); + Text* textobj = xournal->getTextEditor()->getTextElement(); + clearSelectionEndText(); + + auto pageNr = getCurrentPageNo(); + XojPageView* view = xournal->getViewFor(pageNr); + xoj_assert(view != nullptr); + PageRef page = view->getPage(); + auto sel = SelectionFactory::createFromElementOnActiveLayer(this, page, view, textobj); + + xournal->setSelection(sel.release()); + } + + toolHandler->selectTool(type); + toolHandler->fireToolChanged(); +} + +void Control::selectDefaultTool() { + ButtonConfig* cfg = settings->getButtonConfig(Button::BUTTON_DEFAULT); + cfg->applyConfigToToolbarTool(toolHandler); + + if (toolHandler->getToolType() != TOOL_NONE) { + selectTool(toolHandler->getToolType()); + } +} + +void Control::setFontSelected(const XojFont& font) { + this->actionDB->setActionState(Action::FONT, font.asString().c_str()); +} + +void Control::toolChanged() { + ToolType type = toolHandler->getToolType(); + + this->actionDB->setActionState(Action::SELECT_TOOL, type); + + this->actionDB->enableAction(Action::TOOL_DRAW_LINE, toolHandler->hasCapability(TOOL_CAP_RULER)); + this->actionDB->enableAction(Action::TOOL_DRAW_RECTANGLE, toolHandler->hasCapability(TOOL_CAP_RECTANGLE)); + this->actionDB->enableAction(Action::TOOL_DRAW_ELLIPSE, toolHandler->hasCapability(TOOL_CAP_ELLIPSE)); + this->actionDB->enableAction(Action::TOOL_DRAW_ARROW, toolHandler->hasCapability(TOOL_CAP_ARROW)); + this->actionDB->enableAction(Action::TOOL_DRAW_DOUBLE_ARROW, toolHandler->hasCapability(TOOL_CAP_DOUBLE_ARROW)); + this->actionDB->enableAction(Action::TOOL_DRAW_COORDINATE_SYSTEM, toolHandler->hasCapability(TOOL_CAP_ARROW)); + this->actionDB->enableAction(Action::TOOL_DRAW_SPLINE, toolHandler->hasCapability(TOOL_CAP_SPLINE)); + this->actionDB->enableAction(Action::TOOL_DRAW_SHAPE_RECOGNIZER, toolHandler->hasCapability(TOOL_CAP_RECOGNIZER)); + + DrawingType dt = toolHandler->getDrawingType(); + this->actionDB->setActionState(Action::TOOL_DRAW_LINE, dt == DRAWING_TYPE_LINE); + this->actionDB->setActionState(Action::TOOL_DRAW_RECTANGLE, dt == DRAWING_TYPE_RECTANGLE); + this->actionDB->setActionState(Action::TOOL_DRAW_ELLIPSE, dt == DRAWING_TYPE_ELLIPSE); + this->actionDB->setActionState(Action::TOOL_DRAW_ARROW, dt == DRAWING_TYPE_ARROW); + this->actionDB->setActionState(Action::TOOL_DRAW_DOUBLE_ARROW, dt == DRAWING_TYPE_DOUBLE_ARROW); + this->actionDB->setActionState(Action::TOOL_DRAW_COORDINATE_SYSTEM, dt == DRAWING_TYPE_COORDINATE_SYSTEM); + this->actionDB->setActionState(Action::TOOL_DRAW_SPLINE, dt == DRAWING_TYPE_SPLINE); + this->actionDB->setActionState(Action::TOOL_DRAW_SHAPE_RECOGNIZER, dt == DRAWING_TYPE_SHAPE_RECOGNIZER); + + bool enableSize = toolHandler->hasCapability(TOOL_CAP_SIZE); + this->actionDB->enableAction(Action::TOOL_SIZE, enableSize); + if (enableSize) { + toolSizeChanged(); + } + + // Set or reset the lineStyle + toolLineStyleChanged(); + + bool enableFill = toolHandler->hasCapability(TOOL_CAP_FILL); + this->actionDB->enableAction(Action::TOOL_FILL, enableFill); + this->actionDB->enableAction(Action::TOOL_FILL_OPACITY, enableFill); + if (enableFill) { + toolFillChanged(); + } + + bool enableColor = toolHandler->hasCapability(TOOL_CAP_COLOR); + this->actionDB->enableAction(Action::TOOL_COLOR, enableColor); + this->actionDB->enableAction(Action::SELECT_COLOR, enableColor); + if (enableColor) { + toolColorChanged(); + } + + getCursor()->updateCursor(); + + if (type != TOOL_TEXT) { + if (win) { + win->getXournal()->endTextAllPages(); + } + } + if (toolHandler->getDrawingType() != DRAWING_TYPE_SPLINE) { + if (win) { + win->getXournal()->endSplineAllPages(); + } + } +} + +void Control::eraserSizeChanged() { + this->actionDB->setActionState(Action::TOOL_ERASER_SIZE, toolHandler->getEraserSize()); +} + +void Control::penSizeChanged() { this->actionDB->setActionState(Action::TOOL_PEN_SIZE, toolHandler->getPenSize()); } + +void Control::highlighterSizeChanged() { + this->actionDB->setActionState(Action::TOOL_HIGHLIGHTER_SIZE, toolHandler->getHighlighterSize()); +} + +void Control::toolSizeChanged() { + if (toolHandler->getToolType() == TOOL_PEN) { + penSizeChanged(); + } else if (toolHandler->getToolType() == TOOL_ERASER) { + eraserSizeChanged(); + } else if (toolHandler->getToolType() == TOOL_HIGHLIGHTER) { + highlighterSizeChanged(); + } + + this->actionDB->setActionState(Action::TOOL_SIZE, toolHandler->getSize()); + + getCursor()->updateCursor(); +} + +void Control::toolFillChanged() { + this->actionDB->setActionState(Action::TOOL_FILL, toolHandler->getFill() != -1); + this->actionDB->setActionState(Action::TOOL_PEN_FILL, toolHandler->getPenFillEnabled()); + this->actionDB->setActionState(Action::TOOL_HIGHLIGHTER_FILL, toolHandler->getHighlighterFillEnabled()); +} + +void Control::toolLineStyleChanged() { + std::optional style = getLineStyleToSelect(); + this->actionDB->setActionState(Action::TOOL_PEN_LINE_STYLE, style ? style->c_str() : "none"); +} + +auto Control::getLineStyleToSelect() -> std::optional const { + std::optional style; + if (auto* tool = toolHandler->getActiveTool(); tool->getToolType() == TOOL_PEN) { + style = StrokeStyle::formatStyle(tool->getLineStyle()); + } + + if (!win) { + return style; + } + + const EditSelection* sel = win->getXournal()->getSelection(); + if (!sel) { + return style; + } + + bool isFirstPenStrokeElement = true; + std::optional previous_style; + + // Todo(cpp20) Replace with std::ranges::filter_view and for_first_then_for_each + for (const Element* e: sel->getElementsView()) { + if (e->getType() == ELEMENT_STROKE) { + const auto* s = dynamic_cast(e); + + if (s->getToolType().hasLineStyle()) { + style = StrokeStyle::formatStyle(s->getLineStyle()); + + if (isFirstPenStrokeElement) { + previous_style = style; + isFirstPenStrokeElement = false; + } else { + if (style != previous_style) { + return std::nullopt; + } + } + } + } + } + + return style; +} + +void Control::toolColorChanged() { + this->actionDB->setActionState(Action::TOOL_COLOR, getToolHandler()->getColorMaskAlpha()); + getCursor()->updateCursor(); +} + +void Control::changeColorOfSelection() { + if (this->win && toolHandler->hasCapability(TOOL_CAP_COLOR)) { + EditSelection* sel = this->win->getXournal()->getSelection(); + if (sel) { + undoRedo->addUndoAction(sel->setColor(toolHandler->getColor())); + } + + TextEditor* edit = getTextEditor(); + + + if (this->toolHandler->getToolType() == TOOL_TEXT && edit != nullptr) { + // Todo move into selection + edit->setColor(toolHandler->getColor()); + } + } +} + +void Control::showSettings() { + // take note of some settings before to compare with after + struct { + Color selectionColor; + bool verticalSpace; + int verticalSpaceAmountAbove; + int verticalSpaceAmountBelow; + bool horizontalSpace; + int horizontalSpaceAmountRight; + int horizontalSpaceAmountLeft; + bool unlimitedScrolling; + StylusCursorType stylusCursorType; + bool highlightPosition; + SidebarNumberingStyle sidebarStyle; + std::optional colorPaletteSetting; + RecolorParameters recolorParameters; + } settingsBeforeDialog = { + settings->getBorderColor(), + settings->getAddVerticalSpace(), + settings->getAddVerticalSpaceAmountAbove(), + settings->getAddVerticalSpaceAmountBelow(), + settings->getAddHorizontalSpace(), + settings->getAddHorizontalSpaceAmountRight(), + settings->getAddHorizontalSpaceAmountLeft(), + settings->getUnlimitedScrolling(), + settings->getStylusCursorType(), + settings->isHighlightPosition(), + settings->getSidebarNumberingStyle(), + settings->getColorPaletteSetting(), + settings->getRecolorParameters(), + }; + + auto dlg = xoj::popup::PopupWindowWrapper( + this->gladeSearchPath, settings, this, + std::vector{Util::getBuiltInPaletteDirectoryPath(), Util::getCustomPaletteDirectoryPath()}, + [ctrl = this, settingsBeforeDialog]() { + Settings* settings = ctrl->getSettings(); + MainWindow* win = ctrl->win; + XournalView* xournal = win->getXournal(); + // note which settings have changed and act accordingly + if (settingsBeforeDialog.selectionColor != settings->getBorderColor()) { + xournal->forceUpdatePagenumbers(); + } + + if (!settings->getUnlimitedScrolling() && + (settingsBeforeDialog.verticalSpace != settings->getAddVerticalSpace() || + settingsBeforeDialog.horizontalSpace != settings->getAddHorizontalSpace() || + settingsBeforeDialog.verticalSpaceAmountAbove != settings->getAddVerticalSpaceAmountAbove() || + settingsBeforeDialog.horizontalSpaceAmountRight != settings->getAddHorizontalSpaceAmountRight() || + settingsBeforeDialog.verticalSpaceAmountBelow != settings->getAddVerticalSpaceAmountBelow() || + settingsBeforeDialog.horizontalSpaceAmountLeft != settings->getAddHorizontalSpaceAmountLeft())) { + xournal->layoutPages(); + double const xChange = + (settings->getAddHorizontalSpace() ? settings->getAddHorizontalSpaceAmountLeft() : 0) - + (settingsBeforeDialog.horizontalSpace ? settingsBeforeDialog.horizontalSpaceAmountLeft : 0); + const double yChange = + (settings->getAddVerticalSpace() ? settings->getAddVerticalSpaceAmountAbove() : 0) - + (settingsBeforeDialog.verticalSpace ? settingsBeforeDialog.verticalSpaceAmountAbove : 0); + + win->getLayout()->scrollRelative(xChange, yChange); + } + + if (settingsBeforeDialog.unlimitedScrolling != settings->getUnlimitedScrolling()) { + const int xUnlimited = static_cast(win->getLayout()->getVisibleRect().width); + const int yUnlimited = static_cast(win->getLayout()->getVisibleRect().height); + const double xChange = + settingsBeforeDialog.unlimitedScrolling ? + -xUnlimited + (settingsBeforeDialog.horizontalSpace ? + settingsBeforeDialog.horizontalSpaceAmountLeft : + 0) : + xUnlimited - (settingsBeforeDialog.horizontalSpace ? + settingsBeforeDialog.horizontalSpaceAmountLeft : + 0); + const double yChange = + settingsBeforeDialog.unlimitedScrolling ? + -yUnlimited + (settingsBeforeDialog.verticalSpace ? + settingsBeforeDialog.verticalSpaceAmountAbove : + 0) : + yUnlimited - (settingsBeforeDialog.verticalSpace ? + settingsBeforeDialog.verticalSpaceAmountAbove : + 0); + + xournal->layoutPages(); + win->getLayout()->scrollRelative(xChange, yChange); + } + + if (settingsBeforeDialog.stylusCursorType != settings->getStylusCursorType() || + settingsBeforeDialog.highlightPosition != settings->isHighlightPosition()) { + ctrl->getCursor()->updateCursor(); + } + + + bool reloadToolbars = false; + if (settingsBeforeDialog.colorPaletteSetting.has_value() && + settingsBeforeDialog.colorPaletteSetting.value() != settings->getColorPaletteSetting()) { + ctrl->loadPaletteFromSettings(); + ctrl->getWindow()->getToolMenuHandler()->updateColorToolItems(ctrl->getPalette()); + reloadToolbars = true; + } + + if (settingsBeforeDialog.recolorParameters != settings->getRecolorParameters()) { + ctrl->getWindow()->getToolMenuHandler()->updateColorToolItemsRecoloring( + settings->getRecolorParameters().recolorizeMainView ? + std::make_optional(settings->getRecolorParameters().recolor) : + std::nullopt); + reloadToolbars = true; + } + + if (reloadToolbars) { + ctrl->getWindow()->reloadToolbars(); + } + + ctrl->getSidebar()->saveSize(); + ctrl->win->updateScrollbarSidebarPosition(); + ctrl->updateWindowTitle(); + + ctrl->enableAutosave(settings->isAutosaveEnabled()); + + ctrl->zoom->setZoomStep(settings->getZoomStep() / 100.0); + ctrl->zoom->setZoomStepScroll(settings->getZoomStepScroll() / 100.0); + ctrl->win->setDPI(); + + if (settingsBeforeDialog.sidebarStyle != settings->getSidebarNumberingStyle()) { + ctrl->getSidebar()->layout(); + } + + xournal->getHandRecognition()->reload(); + ctrl->win->updateColorscheme(); + + ctrl->getActionDatabase()->setActionState(Action::TOGGLE_TOUCH_DRAWING, + settings->getTouchDrawingEnabled()); + }); + dlg.show(GTK_WINDOW(this->win->getWindow())); +} + +static std::unique_ptr createNewDocument(Control* ctrl, fs::path filepath, + const std::optional& pageTemplate) { + auto newDoc = std::make_unique(ctrl); + if (!filepath.empty()) { + newDoc->setFilepath(std::move(filepath)); + } + ctrl->addDefaultPage(pageTemplate, newDoc.get()); + return newDoc; +} + +void Control::newFile(fs::path filepath) { + this->close( + [ctrl = this, filepath = std::move(filepath)](bool closed) { + if (closed) { + ctrl->replaceDocument(createNewDocument(ctrl, std::move(filepath), std::nullopt), -1); + } + }, + true); +} + +/** + * Check if this is an autosave file, return false in this case and display a user instruction + */ +static auto shouldFileOpen(fs::path const& filepath, GtkWindow* win) -> bool { + auto basePath = Util::getCacheSubfolder(""); + auto isChild = Util::isChildOrEquivalent(filepath, basePath); + if (isChild) { + string msg = FS(_F("Do not open Autosave files. They may will be overwritten!\n" + "Copy the files to another folder.\n" + "Files from Folder {1} cannot be opened.") % + basePath.u8string()); + XojMsgBox::showErrorToUser(win, msg); + } + return !isChild; +} + +void Control::askToOpenFile() { + /** + * Question: in case the current file has not been saved yet, do we want: + * 1. First ask to save it, save it or discard it and then show the FileChooserDialog to open a new file + * 2. First show the FileChooserDialog, and if a valid file has been selected and successfully opened, ask to + * save or discard the current file + * For now, this implements option 1. + */ + this->close([ctrl = this](bool closed) { + if (closed) { + xoj::OpenDlg::showOpenFileDialog(ctrl->getGtkWindow(), ctrl->settings, [ctrl](fs::path path) { + g_message("%s", (_F("file: {1}") % path.string()).c_str()); + ctrl->openFileWithoutSavingTheCurrentDocument(std::move(path), false, -1, [](bool) {}); + }); + } + }); +} + +void Control::replaceDocument(std::unique_ptr doc, int scrollToPage) { + this->closeDocument(); + + fs::path filepath = doc->getFilepath(); + + this->doc->lock(); + *this->doc = *doc; // This calls fireDocumentChanged(DOCUMENT_CHANGE_COMPLETE). No need to fire it again + this->doc->unlock(); + + setEmergencyDocument(this->doc); + + // Set folder as last save path, so the next save will be at the current document location + // This is important because of the new .xopp format, where Xournal .xoj handled as import, + // not as file to load + if (!filepath.empty()) { + settings->setLastSavePath(filepath.parent_path()); + } + + fileLoaded(scrollToPage); +} + +void Control::openXoppFile(fs::path filepath, int scrollToPage, std::function callback) { + LoadHandler loadHandler; + std::unique_ptr doc(loadHandler.loadDocument(filepath)); + + if (!doc) { + string msg = FS(_F("Error opening file \"{1}\"") % filepath.u8string()) + "\n" + loadHandler.getLastError(); + XojMsgBox::showErrorToUser(this->getGtkWindow(), msg); + callback(false); + return; + } + + std::optional missingPdf; + if (!loadHandler.getMissingPdfFilename().empty() || loadHandler.isAttachedPdfMissing()) { + missingPdf = {loadHandler.isAttachedPdfMissing(), loadHandler.getMissingPdfFilename()}; + } + + auto afterOpen = [ctrl = this, missingPdf = std::move(missingPdf), doc = std::move(doc), filepath, + scrollToPage]() mutable { + ctrl->replaceDocument(std::move(doc), scrollToPage); + + if (missingPdf && (missingPdf->wasPdfAttached || !missingPdf->missingFileName.empty())) { + // give the user a second chance to select a new PDF filepath, or to discard the PDF + ctrl->promptMissingPdf(missingPdf.value(), filepath); + } + }; + + if (loadHandler.getFileVersion() > FILE_FORMAT_VERSION) { + enum { YES = 1, NO }; + std::vector buttons = {{_("Yes"), YES}, {_("No"), NO}}; + XojMsgBox::askQuestion( + this->getGtkWindow(), _("File version mismatch"), + _("The file being loaded has a file format version newer than the one currently supported by this " + "version of Xournal++, so it may not load properly. Open anyways?"), + buttons, [afterOpen = std::move(afterOpen), callback = std::move(callback)](int response) mutable { + if (response == YES) { + afterOpen(); + callback(true); + } else { + callback(false); + } + }); + } else { + afterOpen(); + callback(true); + } +} + +bool Control::openPdfFile(fs::path filepath, bool attachToDocument, int scrollToPage) { + this->getCursor()->setCursorBusy(true); + auto doc = std::make_unique(this); + bool success = doc->readPdf(filepath, /*initPages=*/true, attachToDocument); + if (success) { + this->replaceDocument(std::move(doc), scrollToPage); + } else { + std::string msg = FS(_F("Error reading PDF file \"{1}\"\n{2}") % filepath.u8string() % doc->getLastErrorMsg()); + XojMsgBox::showErrorToUser(this->getGtkWindow(), msg); + } + this->getCursor()->setCursorBusy(false); + return success; +} + +bool Control::openPngFile(fs::path filepath, bool attachToDocument, int scrollToPage) { + fs::path imagePath(filepath); + this->getCursor()->setCursorBusy(true); + this->replaceDocument(createNewDocument(this, std::move(filepath), std::nullopt), -1); + + // Put a png file directly in the page + this->doc->lock(); + const size_t pageNr = this->getCurrentPageNo(); + PageRef page = this->doc->getPage(pageNr); + BackgroundImage img; + GError* error = nullptr; + img.loadFile(imagePath, &error); + + if (error) { + std::string msg = FS(_F("Error reading PNG file \"{1}\"") % imagePath.u8string()); + XojMsgBox::showErrorToUser(this->getGtkWindow(), msg); + g_error_free(error); + this->doc->unlock(); + this->getCursor()->setCursorBusy(false); + return false; + } + + page->setBackgroundImage(std::move(img)); + page->setBackgroundType(PageType(PageTypeFormat::Image)); + + // Apply correct page size + GdkPixbuf* pixbuf = page->getBackgroundImage().getPixbuf(); + if (pixbuf) { + page->setSize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); + } else { + g_warning("PageBackgroundChangeController::setPageImageBackground(): Page with image background but nullptr " + "pixbuf"); + } + + this->doc->unlock(); + this->getCursor()->setCursorBusy(false); + return true; +} + +bool Control::openXoptFile(fs::path filepath) { + auto pageTemplate = Util::readString(filepath); + if (!pageTemplate) { + // Unable to read the template from the file + return false; + } + this->replaceDocument(createNewDocument(this, std::move(filepath), pageTemplate), -1); + return true; +} + +void Control::openFileWithoutSavingTheCurrentDocument(fs::path filepath, bool attachToDocument, int scrollToPage, + std::function callback) { + if (filepath.empty()) { + this->replaceDocument(createNewDocument(this, fs::path(), std::nullopt), -1); + callback(true); + return; + } + + if (std::error_code err; !fs::exists(filepath, err)) { + std::string message = + err ? FS(_F("Failed to determine if path exists \"{1}\": {2}") % filepath.u8string() % err.message()) : + FS(_F("That file does not exist:\n\"{1}\"") % filepath.u8string()); + XojMsgBox::showErrorToUser(getGtkWindow(), message); + // We create an empty document to avoid ever being in a "no document" state. + this->replaceDocument(createNewDocument(this, fs::path(), std::nullopt), -1); + callback(false); + return; + } + + if (filepath.extension() == ".xopt") { + this->openXoptFile(std::move(filepath)); + callback(true); + return; + } + + if (Util::hasPngFileExt(filepath)) { + callback(this->openPngFile(std::move(filepath), attachToDocument, scrollToPage)); + return; + } + + if (Util::hasPdfFileExt(filepath)) { + if (!attachToDocument && this->settings->isAutoloadPdfXoj()) { + const std::vector exts = {".xopp", ".xoj", ".pdf.xopp", ".pdf.xoj"}; + fs::path root = filepath; + Util::clearExtensions(root, ".pdf"); + for (const std::string& ext: exts) { + fs::path f = root; + f += ext; + if (fs::exists(f)) { + this->openXoppFile(std::move(f), scrollToPage, std::move(callback)); + return; + } + } + } + callback(this->openPdfFile(std::move(filepath), attachToDocument, scrollToPage)); + return; + } + + this->openXoppFile(std::move(filepath), scrollToPage, std::move(callback)); +} + +void Control::openFile(fs::path filepath, std::function callback, int scrollToPage, bool forceOpen) { + if (filepath.empty() || (!forceOpen && !shouldFileOpen(filepath, getGtkWindow()))) { + return; + } + + this->close( + [ctrl = this, filepath = std::move(filepath), cb = std::move(callback), scrollToPage](bool closed) mutable { + if (closed) { + ctrl->openFileWithoutSavingTheCurrentDocument(std::move(filepath), false, scrollToPage, + std::move(cb)); + } + }, + false, true, forceOpen); +} + +void Control::fileLoaded(int scrollToPage) { + this->doc->lock_shared(); + auto filepath = this->doc->getEvMetadataFilename(); + this->doc->unlock_shared(); + + if (!filepath.empty()) { + auto md = MetadataManager::getForFile(filepath); + if (md) { + if (scrollToPage >= 0) { + md->page = scrollToPage; + } + loadMetadata(*md); + } + RecentManager::addRecentFileFilename(filepath); + } else { + zoom->updateZoomFitValue(); + zoom->setZoomFitMode(true); + } + + updateWindowTitle(); + win->getXournal()->forceUpdatePagenumbers(); + getCursor()->updateCursor(); + updatePageActions(); +} + +enum class MissingPdfDialogOptions : gint { USE_PROPOSED, SELECT_OTHER, REMOVE, CANCEL }; + +void Control::promptMissingPdf(Control::MissingPdfData& missingPdf, const fs::path& filepath) { + const fs::path missingFilePath = fs::path(missingPdf.missingFileName); + + // create error message + std::string parentFolderPath; + std::string filename; +#if defined(WIN32) + parentFolderPath = missingFilePath.parent_path().string(); + filename = missingFilePath.filename().string(); +#else + // since POSIX systems detect the whole Windows path as a filename, this checks whether missingFilePath + // contains a Windows path + std::regex regex(R"([A-Z]:\\(?:.*\\)*(.*))"); + std::cmatch matchInfo; + + if (std::regex_match(missingFilePath.filename().string().c_str(), matchInfo, regex) && matchInfo[1].matched) { + parentFolderPath = missingFilePath.filename().string(); + filename = matchInfo[1].str(); + } else { + parentFolderPath = missingFilePath.parent_path().string(); + filename = missingFilePath.filename().string(); + } +#endif + std::string msg; + if (missingPdf.wasPdfAttached) { + msg = FS(_F("The attached background file could not be found. It might have been moved, " + "renamed or deleted.")); + } else { + msg = FS(_F("The background file \"{1}\" could not be found. It might have been moved, renamed or " + "deleted.\nIt was last seen at: \"{2}\"") % + filename % parentFolderPath); + } + + // try to find file in current directory + auto proposedPdfFilepath = filepath.parent_path() / filename; + bool proposePdfFile = !missingPdf.wasPdfAttached && !filename.empty() && fs::exists(proposedPdfFilepath) && + !fs::is_directory(proposedPdfFilepath); + if (proposePdfFile) { + msg += FS(_F("\nProposed replacement file: \"{1}\"") % proposedPdfFilepath.u8string()); + } + + // show the dialog + std::vector buttons = { + {_("Select another PDF"), static_cast(MissingPdfDialogOptions::SELECT_OTHER)}, + {_("Remove PDF Background"), static_cast(MissingPdfDialogOptions::REMOVE)}, + {_("Cancel"), static_cast(MissingPdfDialogOptions::CANCEL)}}; + if (proposePdfFile) { + buttons.insert(buttons.begin(), + {_("Use proposed PDF"), static_cast(MissingPdfDialogOptions::USE_PROPOSED)}); + } + XojMsgBox::askQuestion( + this->getGtkWindow(), _("Missing PDF background file"), msg, buttons, + std::bind(&Control::missingPdfDialogResponseHandler, this, proposedPdfFilepath, std::placeholders::_1)); +} + +void Control::missingPdfDialogResponseHandler(fs::path proposedPdfFilepath, int responseId) { + switch (static_cast(responseId)) { + case MissingPdfDialogOptions::USE_PROPOSED: + if (!proposedPdfFilepath.empty()) { + this->pageBackgroundChangeController->changePdfPagesBackground(proposedPdfFilepath, false); + } + break; + case MissingPdfDialogOptions::SELECT_OTHER: + Util::execInUiThread([this]() { + xoj::OpenDlg::showAnnotatePdfDialog(getGtkWindow(), settings, [this](fs::path path, bool attachPdf) { + if (!path.empty()) { + this->pageBackgroundChangeController->changePdfPagesBackground(path, attachPdf); + } + }); + }); + break; + case MissingPdfDialogOptions::REMOVE: + this->pageBackgroundChangeController->applyBackgroundToAllPages(PageType(PageTypeFormat::Plain)); + break; + default: + break; + } +} + +class MetadataCallbackData { +public: + Control* ctrl{}; + MetadataEntry md; +}; + +/** + * Load the data after processing the document... + */ +auto Control::loadMetadataCallback(MetadataCallbackData* data) -> bool { + ZoomControl* zoom = data->ctrl->zoom; + if (zoom->isZoomPresentationMode()) { + data->ctrl->setViewPresentationMode(true); + } else if (zoom->isZoomFitMode()) { + zoom->updateZoomFitValue(); + zoom->setZoomFitMode(true); + } else { + zoom->setZoomFitMode(false); + zoom->setZoom(data->md.zoom * zoom->getZoom100Value()); + } + data->ctrl->scrollHandler->scrollToPage(as_unsigned(data->md.page)); + // Do not call again! + return false; +} + +void Control::loadMetadata(MetadataEntry md) { + auto* data = new MetadataCallbackData(); + data->md = std::move(md); + data->ctrl = this; + + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, xoj::util::wrap_v, data, + &xoj::util::destroy_cb); +} + +void Control::askToAnnotatePdf() { + this->close([ctrl = this](bool closed) { + if (closed) { + xoj::OpenDlg::showAnnotatePdfDialog(ctrl->getGtkWindow(), ctrl->settings, + [ctrl](fs::path path, bool attachPdf) { + if (!path.empty()) { + ctrl->openPdfFile(std::move(path), attachPdf, -1); + } + }); + } + }); +} + +void Control::print() { + this->doc->lock_shared(); + PrintHandler::print(this->doc, getCurrentPageNo(), this->getGtkWindow()); + this->doc->unlock_shared(); +} + +void Control::block(const string& name) { + if (this->isBlocking) { + return; + } + + // Disable all gui Control, to get full control over the application + win->setDynamicallyGeneratedSubmenuDisabled(true); + actionDB->disableAll(); + getCursor()->setCursorBusy(true); + disableSidebarTmp(true); + + this->statusbar = this->win->get("statusbar"); + this->lbState = GTK_LABEL(this->win->get("lbState")); + this->pgState = GTK_PROGRESS_BAR(this->win->get("pgState")); + + gtk_label_set_text(this->lbState, name.c_str()); + gtk_widget_show(this->statusbar); + + this->maxState = 100; + this->isBlocking = true; +} + +void Control::unblock() { + if (!this->isBlocking) { + return; + } + + this->win->setDynamicallyGeneratedSubmenuDisabled(false); + actionDB->resetEnableStatus(); + getCursor()->setCursorBusy(false); + disableSidebarTmp(false); + + gtk_widget_hide(this->statusbar); + + this->isBlocking = false; +} + +void Control::setMaximumState(size_t max) { this->maxState = max; } + +void Control::setCurrentState(size_t state) { + Util::execInUiThread([state, this]() { + gtk_progress_bar_set_fraction(this->pgState, + static_cast(state) / static_cast(this->maxState)); + }); +} + +void Control::showFontDialog() { + this->actionDB->enableAction(Action::SELECT_FONT, false); // Only one dialog + auto* dlg = gtk_font_chooser_dialog_new(_("Select font"), GTK_WINDOW(this->win->getWindow())); + gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dlg), settings->getFont().asString().c_str()); + + auto popup = xoj::popup::PopupWindowWrapper( + GTK_DIALOG(dlg), + [this, dlg](int response) { + if (response == GTK_RESPONSE_OK) { + auto font = + xoj::util::OwnedCString::assumeOwnership(gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dlg))); + this->actionDB->fireChangeActionState(Action::FONT, font.get()); + } + this->actionDB->enableAction(Action::SELECT_FONT, true); + }, + XojMsgBox::IMMEDIATE); // We need IMMEDIATE so accessing GTK_FONT_CHOOSER(dlg) is not UB + popup.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::showColorChooserDialog() { + this->actionDB->enableAction(Action::SELECT_COLOR, false); // Only one dialog + auto* dlg = gtk_color_chooser_dialog_new(_("Select color"), GTK_WINDOW(this->win->getWindow())); + GdkRGBA c = Util::argb_to_GdkRGBA(toolHandler->getColor()); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dlg), &c); + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dlg), false); + + auto popup = xoj::popup::PopupWindowWrapper( + GTK_DIALOG(dlg), + [this, dlg](int response) { + if (response == GTK_RESPONSE_OK) { + GdkRGBA c; + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dlg), &c); + this->actionDB->fireChangeActionState(Action::TOOL_COLOR, Util::GdkRGBA_to_rgb(c)); + } + this->actionDB->enableAction(Action::SELECT_COLOR, true); + }, + XojMsgBox::IMMEDIATE); // We need IMMEDIATE so accessing GTK_COLOR_CHOOSER(dlg) is not UB + popup.show(GTK_WINDOW(this->win->getWindow())); +} + +void Control::updateWindowTitle() { + std::string title{}; ///< Actually a UTF-8 string + + this->doc->lock_shared(); + const fs::path& refPath = doc->getFilepath().empty() ? doc->getPdfFilepath() : doc->getFilepath(); + if (refPath.empty()) { + title = _("Unsaved Document"); + } else { + if (settings->isPageNumberInTitlebarShown()) { + title = "[" + std::to_string(getCurrentPageNo() + 1) + "/" + std::to_string(doc->getPageCount()) + "] "; + } + if (undoRedo->isChanged()) { + title += "*"; + } + + if (settings->isFilepathInTitlebarShown()) { + title += std::string("[") + char_cast(refPath.parent_path().u8string().c_str()) + "] - "; + } + title += char_cast(refPath.filename().u8string()); + } + this->doc->unlock_shared(); + + title += " - Xournal++"; + + gtk_window_set_title(getGtkWindow(), title.c_str()); +} + +void Control::exportAsPdf() { + this->clearSelectionEndText(); + + auto* job = new PdfExportJob(this); + job->showFileChooser( + [ctrl = this, job]() { + ctrl->scheduler->addJob(job, JOB_PRIORITY_NONE); + job->unref(); + }, + [ctrl = this, job]() { + // The job blocked, so we have to unblock, because the job unblocks only after + ctrl->unblock(); + job->unref(); + }); +} + +void Control::exportAs() { + this->clearSelectionEndText(); + auto* job = new CustomExportJob(this); + job->showDialogAndRun(); +} + +void Control::save(std::function callback) { saveImpl(false, std::move(callback)); } + +void Control::saveAs(std::function callback) { saveImpl(true, std::move(callback)); } + +void Control::saveImpl(bool saveAs, std::function callback) { + // clear selection before saving + clearSelectionEndText(); + + this->doc->lock_shared(); + fs::path filepath = this->doc->getFilepath(); + this->doc->unlock_shared(); + + auto doSave = [ctrl = this, cb = std::move(callback)]() { + // clear selection before saving + ctrl->clearSelectionEndText(); + + auto* job = new SaveJob(ctrl, std::move(cb)); + ctrl->scheduler->addJob(job, JOB_PRIORITY_URGENT); + job->unref(); + }; + + if (saveAs || filepath.empty()) { + // No need to backup the old saved file, as there is none + this->doc->lock_shared(); + this->doc->setCreateBackupOnSave(false); + auto suggestedPath = this->doc->createSaveFoldername(this->settings->getLastSavePath()); + suggestedPath /= this->doc->createSaveFilename(Document::XOPP, this->settings->getDefaultSaveName()); + this->doc->unlock_shared(); + xoj::SaveExportDialog::showSaveFileDialog(getGtkWindow(), settings, std::move(suggestedPath), + [doSave = std::move(doSave), ctrl = this](std::optional p) { + if (p && !p->empty()) { + ctrl->settings->setLastSavePath(p->parent_path()); + ctrl->doc->lock(); + ctrl->doc->setFilepath(std::move(p.value())); + ctrl->doc->unlock(); + doSave(); + } + }); + } else { + doSave(); + } +} + +void Control::resetSavedStatus() { + this->doc->lock_shared(); + auto filepath = this->doc->getFilepath(); + this->doc->unlock_shared(); + + this->undoRedo->documentSaved(); + RecentManager::addRecentFileFilename(filepath); + this->updateWindowTitle(); +} + +void Control::quit(bool allowCancel) { + g_message("Quitting"); + auto afterClosed = [this, allowCancel](bool closed) { + if (!closed) { + g_message("Closing document failed or cancelled"); + if (!allowCancel) { + // Cancel is not allowed, and the user close or did not save + // This is probably called from macOS, where the Application + // now will be killed - therefore do an emergency save. + emergencySave(); + } + } else { +#ifdef ENABLE_AUDIO + if (audioController) { + audioController->stopRecording(); + } +#endif + this->scheduler->lock(); + this->scheduler->removeAllJobs(); + this->scheduler->unlock(); + this->scheduler->stop(); // Finish current task. Must be called to finish pending saves. + this->closeDocument(); // Must be done after all jobs has finished (Segfault on save/export) + settings->save(); + g_application_quit(G_APPLICATION(gtkApp)); + } + }; + + this->close(std::move(afterClosed), true, allowCancel); +} + +void Control::close(std::function callback, const bool allowDestroy, const bool allowCancel, + const bool forceClose) { + clearSelectionEndText(); + + doc->lock_shared(); + auto const& file = doc->getEvMetadataFilename(); + doc->unlock_shared(); + metadata->storeMetadata(file, static_cast(getCurrentPageNo()), zoom->getZoomReal()); + metadata->documentChanged(); + + resetGeometryTool(); + + bool safeToClose = forceClose || !undoRedo->isChanged(); + if (!safeToClose) { + fs::path path = doc->getFilepath(); + const bool fileRemoved = !path.empty() && !fs::exists(path); + const auto message = fileRemoved ? _("Document file was removed.") : _("This document is not saved yet."); + const bool saveAs = fileRemoved || path.empty(); + const auto saveLabel = saveAs ? _("Save As...") : _("Save"); + + enum { SAVE = 1, DISCARD, CANCEL }; + std::vector buttons = {{saveLabel, SAVE}, {_("Discard"), DISCARD}}; + if (allowCancel) { + buttons.emplace_back(_("Cancel"), CANCEL); + } + XojMsgBox::askQuestion(getGtkWindow(), message, std::string(), std::move(buttons), + [ctrl = this, saveAs, allowDestroy, callback = std::move(callback)](int response) { + auto execAfter = [allowDestroy, ctrl, cb = std::move(callback)](bool saved) { + if (saved && allowDestroy) { + ctrl->closeDocument(); + } + cb(saved); + }; + if (response == SAVE) { + ctrl->saveImpl(saveAs, std::move(execAfter)); + return; + } + bool safeToClose = response == DISCARD; + execAfter(safeToClose); + }); + } else { + if (allowDestroy) { + this->closeDocument(); + } + callback(true); + } +} + +void Control::closeDocument() { + this->undoRedo->clearContents(); + + this->doc->lock(); + // FIXME: there could potentially be a data race if a job requires the old document but runs after it is closed + this->doc->clearDocument(true); + this->doc->unlock(); + + this->undoRedoChanged(); +} + +void Control::initButtonTool() { + std::vector